API reference › @evolu/common › Type › createTypeWithError

function createTypeWithError<
  Name extends string,
  T extends AnyType,
  Error extends TypeError<Name>,
>(
  name: Name & ValidateConcreteTypeName<Name>,
  type: T & ValidateOutput<T> & IdentityEncodingOf<T> extends true
    ? unknown
    : "⛔ Type error: Source Type must use identity encoding.",
  mapError: (error: InferErrors<T>, value: unknown) => Error,
  formatError: TypeErrorFormatter<NoInfer>,
): Type<Name, T["Output"], T["Output"], Error>;

Defined in: packages/common/src/Type.ts:2545

Creates a root Type with a custom error for an existing validator.

The source must use identity encoding. The new Type accepts its Output as Input and hides its parent boundaries. Validation and error collection are delegated automatically; the mapper receives the failure and original value. The formatter presents the mapped error as one issue.

Use it to give an existing composite Type, such as a union of literal templates, one domain error. To add a rule to a plain value, prefer a child createType: it keeps its parent's Input, such as string, while this Type accepts only its Output.

When formatError is declared separately rather than written inline, annotate the mapper's parameters as well as its return type. Otherwise TypeScript infers the error type from its constraint and rejects the formatter.

Example

import {
  assertEqual,
  assertErr,
  createTypeWithError,
  Number,
  String,
  union,
  type TypeError,
  type UnionError,
} from "@evolu/common";

interface ValueError extends TypeError<"Value"> {
  readonly cause: UnionError;
}

const Value = createTypeWithError(
  "Value",
  union(String, Number),
  (cause): ValueError => ({ type: "Value", cause }),
  () => "Enter text or a number.",
);

const result = Value.fromUnknown(false, { errors: "all" });
assertErr(result);
assertEqual(result.error.cause.errors.length, 2);
assertEqual(Value.formatError(result.error), "Enter text or a number.");