API reference › @evolu/common › Type › brand
Call Signature
function brand<Name, ParentType>(
name: ValidateConcreteTypeName<Name>,
parent: ValidateParent<ParentType>,
validate?: (value: ParentType["Output"]) => Result<void, never>,
): BrandType<ParentType, Name, never>;
Defined in: packages/common/src/Type.ts:4914
Branded Type.
Branding is the recommended way to define domain-specific primitive Types in Evolu. A Brand distinguishes values that share the same runtime representation, preventing values with different meanings from being used interchangeably.
brand takes the name of the new Brand, the parent Type to brand, and an
optional validation callback for an additional constraint. Its Output retains
the parent Output and its brands, and adds the new Brand.
Without a validation callback, the brand adds no errors and inherits its
parent's formatter. A validation callback returns ok() when the parent
value satisfies the constraint or an Err describing the failure. brand
always preserves the parent value; representation-changing work belongs in
transform. A fallible validation callback must format only the error
it introduces; inherited errors are formatted by the parent Type
automatically. A fallible brand's error type must equal the Brand name.
Example
A signed 64-bit integer:
import {
assertEqual,
assertErr,
assertOk,
assertType,
BigInt,
brand,
Data,
err,
ok,
type Brand,
type TypeError,
} from "@evolu/common";
const Int64 = brand(
"Int64",
BigInt,
(value) =>
globalThis.BigInt.asIntN(64, value) === value
? ok()
: err<Int64Error>({ type: "Int64", value }),
() => "Expected a signed 64-bit integer.",
);
type Int64 = typeof Int64.Output;
// Note the Brand.
assertType<bigint & Brand<"Int64">, Int64>();
interface Int64Error extends TypeError<"Int64"> {
readonly value: bigint;
}
assertOk(Int64.fromUnknown(42n), 42n);
const invalid = Int64.fromUnknown(2n ** 63n);
assertErr(invalid);
assertType(Data, invalid.error);
assertEqual(invalid.error, {
type: "Int64",
value: 2n ** 63n,
});
To reuse and compose a Brand constraint with different parent Types, define a BrandFactory.
Call Signature
function brand<Name, ParentType, Error>(
name: Name,
parent: ValidateBrandParent<Name, ParentType>,
validate: (value: ParentType["Output"]) => Result<void, Error>,
formatError: TypeErrorFormatter<NoInfer>,
): BrandType<ParentType, Name, Error>;
Defined in: packages/common/src/Type.ts:4925
Creates a validated Brand Type with its own error formatter.