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.");