API Reference / @evolu/common / Type / brand
Function: brand()
Defined in: packages/common/src/Type.ts:738
Branded Type.
The brand
Type Factory takes the name of a new Brand, a parent Type
to be branded, and the optional refine
function for additional constraint.
If the refine
function is omited, TODO:
Examples
A simple CurrencyCode
Type:
const CurrencyCode = brand("CurrencyCode", String, (value) =>
/^[A-Z]{3}$/.test(value)
? ok(value)
: err<CurrencyCodeError>({ type: "CurrencyCode", value }),
);
// string & Brand<"CurrencyCode">
type CurrencyCode = typeof CurrencyCode.Type;
interface CurrencyCodeError extends TypeError<"CurrencyCode"> {}
const formatCurrencyCodeError =
createTypeErrorFormatter<CurrencyCodeError>(
(error) => `Invalid currency code: ${error.value}`,
);
// Usage
const result = CurrencyCode.from("USD");
if (result.ok) {
console.log("Valid currency code:", result.value);
} else {
console.error(formatCurrencyCodeError(result.error));
}
Often, we want to make a branded Type reusable. For example, instead of
TrimmedString
, we want the trimmed
Type Factory:
const trimmed: BrandFactory<"Trimmed", string, TrimmedError> = (
parent,
) =>
brand("Trimmed", parent, (value) =>
value.trim().length === value.length
? ok(value)
: err<TrimmedError>({ type: "Trimmed", value }),
);
interface TrimmedError extends TypeError<"Trimmed"> {}
const formatTrimmedError = createTypeErrorFormatter<TrimmedError>(
(error) => `A value ${error.value} is not trimmed`,
);
const TrimmedString = trimmed(String);
// string & Brand<"Trimmed">
type TrimmedString = typeof TrimmedString.Type;
const TrimmedNote = trimmed(Note);
As noted earlier, the refine
function is optional. That's useful to add
semantic meaning to the existing Type without altering its functionality:
const SimplePassword = brand(
"SimplePassword",
minLength(8)(maxLength(64)(TrimmedString)),
);
// string & Brand<"Trimmed"> & Brand<"MinLength8"> & Brand<"MaxLength64"> & Brand<"SimplePassword">
type SimplePassword = typeof SimplePassword.Type;
We can use brand
to enforce valid object as well:
const Form = object({
password: SimplePassword,
confirmPassword: SimplePassword,
});
const ValidForm = brand("Valid", Form, (value) => {
if (value.password !== value.confirmPassword)
return err<ValidFormError>({
type: "ValidForm",
value,
reason: { kind: "PasswordMismatch" },
});
return ok(value);
});
type ValidForm = typeof ValidForm.Type;
interface ValidFormError extends TypeError<"ValidForm"> {
readonly reason: { kind: "PasswordMismatch" };
}
const result = ValidForm.from({
password: "abcde123",
confirmPassword: "bbcde123",
});
const safeForm = (_form: ValidForm) => {
//
};
if (result.ok) {
safeForm(result.value);
}
expect(result).toEqual(
err({
type: "ValidForm",
value: {
confirmPassword: "bbcde123",
password: "abcde123",
},
reason: {
kind: "PasswordMismatch",
},
}),
);
Type Parameters
Type Parameter | Default type |
---|---|
Name extends string | - |
ParentType extends AnyType | - |
Parent | InferType <ParentType > |
RefineError extends TypeError <Capitalize <string >> | never |
Parameters
Parameter | Type |
---|---|
name | Name |
parent | ParentType |
refine | (value ) => Result <Parent , RefineError > |
Returns
BrandType
<ParentType
, Name
, RefineError
, InferErrors
<ParentType
>>
Defined in: packages/common/src/Type.ts:749
Branded Type.
The brand
Type Factory takes the name of a new Brand, a parent Type
to be branded, and the optional refine
function for additional constraint.
If the refine
function is omited, TODO:
Examples
A simple CurrencyCode
Type:
const CurrencyCode = brand("CurrencyCode", String, (value) =>
/^[A-Z]{3}$/.test(value)
? ok(value)
: err<CurrencyCodeError>({ type: "CurrencyCode", value }),
);
// string & Brand<"CurrencyCode">
type CurrencyCode = typeof CurrencyCode.Type;
interface CurrencyCodeError extends TypeError<"CurrencyCode"> {}
const formatCurrencyCodeError =
createTypeErrorFormatter<CurrencyCodeError>(
(error) => `Invalid currency code: ${error.value}`,
);
// Usage
const result = CurrencyCode.from("USD");
if (result.ok) {
console.log("Valid currency code:", result.value);
} else {
console.error(formatCurrencyCodeError(result.error));
}
Often, we want to make a branded Type reusable. For example, instead of
TrimmedString
, we want the trimmed
Type Factory:
const trimmed: BrandFactory<"Trimmed", string, TrimmedError> = (
parent,
) =>
brand("Trimmed", parent, (value) =>
value.trim().length === value.length
? ok(value)
: err<TrimmedError>({ type: "Trimmed", value }),
);
interface TrimmedError extends TypeError<"Trimmed"> {}
const formatTrimmedError = createTypeErrorFormatter<TrimmedError>(
(error) => `A value ${error.value} is not trimmed`,
);
const TrimmedString = trimmed(String);
// string & Brand<"Trimmed">
type TrimmedString = typeof TrimmedString.Type;
const TrimmedNote = trimmed(Note);
As noted earlier, the refine
function is optional. That's useful to add
semantic meaning to the existing Type without altering its functionality:
const SimplePassword = brand(
"SimplePassword",
minLength(8)(maxLength(64)(TrimmedString)),
);
// string & Brand<"Trimmed"> & Brand<"MinLength8"> & Brand<"MaxLength64"> & Brand<"SimplePassword">
type SimplePassword = typeof SimplePassword.Type;
We can use brand
to enforce valid object as well:
const Form = object({
password: SimplePassword,
confirmPassword: SimplePassword,
});
const ValidForm = brand("Valid", Form, (value) => {
if (value.password !== value.confirmPassword)
return err<ValidFormError>({
type: "ValidForm",
value,
reason: { kind: "PasswordMismatch" },
});
return ok(value);
});
type ValidForm = typeof ValidForm.Type;
interface ValidFormError extends TypeError<"ValidForm"> {
readonly reason: { kind: "PasswordMismatch" };
}
const result = ValidForm.from({
password: "abcde123",
confirmPassword: "bbcde123",
});
const safeForm = (_form: ValidForm) => {
//
};
if (result.ok) {
safeForm(result.value);
}
expect(result).toEqual(
err({
type: "ValidForm",
value: {
confirmPassword: "bbcde123",
password: "abcde123",
},
reason: {
kind: "PasswordMismatch",
},
}),
);
Type Parameters
Type Parameter |
---|
Name extends string |
ParentType extends AnyType |
Parameters
Parameter | Type |
---|---|
name | Name |
parent | ParentType |
Returns
BrandType
<ParentType
, Name
, BrandWithoutRefineError
<Name
, InferErrors
<ParentType
>>>