API reference / @evolu/common / Type
Type
π§© Type-safe runtime types
Evolu Type is like a type guard that returns typed errors (via Result) instead of throwing. We either get a safely typed value or a precise, composable error value telling us exactly why validation failed.
Evolu Type supports Standard Schema for interoperability with 40+ validation-compatible tools and frameworks.
Why another validation library?
- Result-based error handling β no exceptions for normal control flow.
- Typed errors with decoupled formatters β validation logic β user messages.
- Consistent constraints via Brand β every constraint becomes part of the type.
- No user-land chaining DSL β designed with the upcoming ES pipe operator in mind.
- Selective validation β parent validations are skipped when already proved by typing.
- Simple, top-down implementation β readable source code from top to bottom with no hidden magic; just plain functions and composition.
Base Types Quick Start
// Validate unknown values
const value: unknown = "hello";
const stringResult = String.fromUnknown(value);
if (!stringResult.ok) {
// console.error(formatStringError(stringResult.error));
return stringResult; // inside a function returning Result<string, _>
}
// Safe branch: value is now string
const upper = stringResult.value.toUpperCase();
// Type guard style
if (String.is(value)) {
// narrowed to string
}
// Composing: arrays & objects
const Numbers = array(Number); // ReadonlyArray<number>
const Point = object({ x: Number, y: Number });
Numbers.from([1, 2, 3]); // ok
Point.from({ x: 1, y: 2 }); // ok
Point.from({ x: 1, y: "2" }); // err -> nested Number error
Branding Basics
Branding adds semantic meaning & constraints while preserving the runtime shape:
const CurrencyCode = brand("CurrencyCode", String, (value) =>
/^[A-Z]{3}$/.test(value)
? ok(value)
: err<CurrencyCodeError>({ type: "CurrencyCode", value }),
);
type CurrencyCode = typeof CurrencyCode.Type; // string & Brand<"CurrencyCode">
interface CurrencyCodeError extends TypeError<"CurrencyCode"> {}
const formatCurrencyCodeError = createTypeErrorFormatter<CurrencyCodeError>(
(error) => `Invalid currency code: ${error.value}`,
);
const r = CurrencyCode.from("USD"); // ok("USD")
const e = CurrencyCode.from("usd"); // err(...)
See also reusable brand factories like minLength, maxLength, trimmed,
positive, between, etc.
Objects & Optional Fields
const User = object({
name: NonEmptyTrimmedString100,
age: optional(PositiveInt),
});
type User = typeof User.Type;
User.from({ name: "Alice" }); // ok
User.from({ name: "Alice", age: -1 }); // err(PositiveInt)
Deriving JSON String Types
const Person = object({
name: NonEmptyString50,
// Did you know that JSON.stringify converts NaN (a number) into null?
// To prevent this, use FiniteNumber.
age: FiniteNumber,
});
type Person = typeof Person.Type;
const [PersonJson, personToPersonJson, personJsonToPerson] = json(
Person,
"PersonJson",
);
// string & Brand<"PersonJson">
type PersonJson = typeof PersonJson.Type;
const person = Person.orThrow({
name: "Alice",
age: 30,
});
const personJson = personToPersonJson(person);
expect(personJsonToPerson(personJson)).toEqual(person);
Error Formatting
Evolu separates validation logic from human-readable messages. There are two layers:
- Per-type formatters (e.g.
formatStringError) β simple, focused, already used earlier in the quick start example. - A unified formatter via
createFormatTypeErrorβ composes all built-in and custom errors (including nested composite types) and lets us override selected messages.
1. Per-Type Formatter (recap)
const r = String.fromUnknown(42);
if (!r.ok) console.error(formatStringError(r.error));
2. Unified Formatter with Overrides
// Override only what we care about; fall back to built-ins for the rest.
const formatTypeError = createFormatTypeError((error) => {
if (error.type === "MinLength") return `Min length is ${error.min}`;
});
const User = object({ name: NonEmptyTrimmedString100 });
const resultUser = User.from({ name: "" });
if (!resultUser.ok) console.error(formatTypeError(resultUser.error));
const badPoint = object({ x: Number, y: Number }).from({
x: 1,
y: "foo",
});
if (!badPoint.ok) console.error(formatTypeError(badPoint.error));
The unified formatter walks nested structures (object / array / record / tuple / union) and applies overrides only where specified, greatly reducing boilerplate when formatting complex validation errors.
Tip
If necessary, write globalThis.String instead of String to avoid naming
clashes with native types.
Design Decision: No Bidirectional Transformations
Evolu Type intentionally does not support bidirectional transformations. It previously did, but supporting that while keeping typed error fidelity added complexity that hurt readability & reliability. Most persistence pipelines (e.g. SQLite) already require explicit mapping of query results, so implicit reverse transforms would not buy much. We may revisit this if we can design a minimal, 100% safe API that preserves simplicity.
Base Factories
| Function | Description |
|---|---|
| array | Array of a specific Type. |
| base | Base Type. |
| brand | Branded Type. |
| instanceOf | instanceof Type. |
| literal | Literal Type. |
| nullishOr | union(undefined, null, T) Type. |
| nullOr | union(null, T) Type. |
| object | Object Type. |
| record | Record of a key Type and value Type. |
| recursive | Recursive Type. |
| tuple | Tuple Type. |
| undefinedOr | union(undefined, T) Type. |
| union | Union Type. |
Base Types
| Variable | Description |
|---|---|
| BigInt | - |
| Boolean | - |
| Date | JavaScript Date. |
| Function | - |
| JsonValue | JSON-compatible value: string, FiniteNumber, boolean, null, JsonArray, or JsonObject. |
| Null | - |
| Number | - |
| String | - |
| Uint8Array | - |
| Undefined | - |
| Unknown | - |
String
| Name | Description |
|---|---|
| Base64Url | Base64Url without padding. |
| CurrencyCode | A three-letter ISO 4217 currency code (e.g., USD, EUR). |
| DateIso | ISO 8601 date-time string. |
| Id | Globally unique identifier. |
| Int64String | Stringified Int64. |
| Json | JSON-string Type. |
| length | Exact length. |
| maxLength | Maximum length. |
| minLength | Minimum length. |
| Mnemonic | The mnemonic, also known as a "seed phrase," is a set of 12 words in a specific order chosen from a predefined list (BIP39). It provides a human-readable way to store a private key securely. The mnemonic is generated safely on the user's device using cryptographically secure random number generation, ensuring it remains private and unique. |
| NonEmptyString | - |
| NonEmptyString100 | - |
| NonEmptyString1000 | - |
| NonEmptyTrimmedString | - |
| NonEmptyTrimmedString100 | - |
| NonEmptyTrimmedString1000 | - |
| regex | String matching a regular expression. |
| SimpleName | Simple alphanumeric string for naming in file systems, URLs, and identifiers. |
| SimplePassword | Trimmed string between 8 and 64 characters, branded as SimplePassword. |
| String | - |
| String100 | - |
| String1000 | - |
| trimmed | Trimmed string. |
| TrimmedString | Trimmed string |
| TrimmedString100 | - |
| TrimmedString1000 | - |
| UrlSafeString | URL-safe string. |
| createIdFromString | Creates an Id from a string using SHA-256. |
| id | Creates a branded Id Type for a table's primary key. |
Number
| Variable | Description |
|---|---|
| between | Number within a range, inclusive. |
| finite | Finite number. |
| FiniteNumber | Finite number. |
| greaterThan | Number greater than a specified value. |
| greaterThanOrEqualTo | Number β₯ a specified value. |
| int | Integer within the safe range of JavaScript numbers. |
| Int | Integer within the safe range of JavaScript numbers. |
| Int64 | 64-bit signed integer. |
| lessThan | Number less than a specified value. |
| lessThanOrEqualTo | Number β€ a specified value. |
| multipleOf | Number that is a multiple of a divisor. |
| negative | Negative number. |
| NegativeInt | - |
| NegativeNumber | - |
| nonNaN | Number that is not NaN. |
| NonNaNNumber | - |
| nonNegative | Non-negative number. |
| NonNegativeInt | - |
| NonNegativeNumber | - |
| nonPositive | Non-positive number. |
| NonPositiveInt | - |
| NonPositiveNumber | - |
| positive | Positive number. |
| PositiveInt | - |
| PositiveNumber | - |
Array
| Name | Description |
|---|---|
| JsonArray | JSON-compatible array of JsonValue elements. |
| length | Exact length. |
| maxLength | Maximum length. |
| minLength | Minimum length. |
| array | Array of a specific Type. |
Object
| Name | Description |
|---|---|
| JsonObject | JSON-compatible object with string keys and JsonValue values. |
| nullableToOptional | Converts each βnullableβ property (a union that includes Null) into an optional property. This means consumers can omit the property entirely, or set it to null, or set it to the non-null member of the union. |
| object | Object Type. |
| omit | Create a new object Type by omitting some keys. |
| partial | Creates a partial object type where all properties are optional. |
| record | Record of a key Type and value Type. |
Utilities
| Name | Description |
|---|---|
| BrandFactory | Helper type for Type Factory that creates a branded Type. |
| InferError | Extracts the specific error type from a Type. |
| InferErrors | Extracts all error types (Error / ParentError) from a Type. |
| InferInput | Extracts the input type from a Type. |
| InferName | Extracts the name from a Type. |
| InferParent | Extracts the parent type from a Type. |
| InferParentError | Extracts the parent error type from a Type. |
| InferType | Extracts the type from a Type. |
| MergeObjectTypeErrors | Merge Error and ParentError into one ObjectError so tooltips and error messages are easier to read. |
| TypeErrors | Union of all TypeErrors defined in the Type.ts file, including base type errors (e.g., StringError, NumberError), composite type errors (ArrayError, ObjectError), and optionally, user-defined extra errors. |
| TypeName | Unique identifier for a Type. |
| createBaseTypeErrorFormatter | Creates a formatter function for a base TypeError. |
| createFormatTypeError | Formats Evolu Type errors into user-friendly messages. |
| createTypeErrorFormatter | Creates a formatter function for TypeError. |
| isType | Checks if the given value is an Type. |
| typeErrorToStandardSchemaIssues | Converts an Evolu TypeError to Standard Schema V1 issues format. |