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 ParameterDefault type
Name extends string-
ParentType extends AnyType-
ParentInferType<ParentType>
RefineError extends TypeError<Capitalize<string>>never

Parameters

ParameterType
nameName
parentParentType
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

ParameterType
nameName
parentParentType

Returns

BrandType<ParentType, Name, BrandWithoutRefineError<Name, InferErrors<ParentType>>>

Was this page helpful?