TypeScript runtime type system for IO decoding/encoding
72
Functions for composing complex types from simpler ones, enabling validation of objects, arrays, unions, intersections, and more.
Creates a codec for objects with required properties.
/**
* Create an interface codec from properties
* @param props - Object mapping property names to their codecs
* @param name - Optional name for the codec
*/
function type<P extends Props>(props: P, name?: string): TypeC<P>;
interface TypeC<P extends Props> extends InterfaceType<P,
{ [K in keyof P]: TypeOf<P[K]> },
{ [K in keyof P]: OutputOf<P[K]> },
unknown
> {}Usage Example:
import * as t from "io-ts";
const User = t.type({
name: t.string,
age: t.number,
email: t.string
});
type User = t.TypeOf<typeof User>;
// User = { name: string; age: number; email: string }
const result = User.decode({
name: "Alice",
age: 30,
email: "alice@example.com"
});
// result: Right({ name: "Alice", age: 30, email: "alice@example.com" })Creates a codec for objects with optional properties.
/**
* Create a partial interface codec where all properties are optional
* @param props - Object mapping property names to their codecs
* @param name - Optional name for the codec
*/
function partial<P extends Props>(props: P, name?: string): PartialC<P>;
interface PartialC<P extends Props> extends PartialType<P,
{ [K in keyof P]?: TypeOf<P[K]> },
{ [K in keyof P]?: OutputOf<P[K]> },
unknown
> {}Usage Example:
import * as t from "io-ts";
const PartialUser = t.partial({
name: t.string,
age: t.number,
email: t.string
});
type PartialUser = t.TypeOf<typeof PartialUser>;
// PartialUser = { name?: string; age?: number; email?: string }
const result = PartialUser.decode({ name: "Alice" });
// result: Right({ name: "Alice" })Creates a codec for arrays with elements of a specific type.
/**
* Create an array codec with elements of type C
* @param item - Codec for array elements
* @param name - Optional name for the codec
*/
function array<C extends Mixed>(item: C, name?: string): ArrayC<C>;
interface ArrayC<C extends Mixed> extends ArrayType<C,
Array<TypeOf<C>>,
Array<OutputOf<C>>,
unknown
> {}Usage Example:
import * as t from "io-ts";
const NumberArray = t.array(t.number);
const StringArray = t.array(t.string);
const numbers = NumberArray.decode([1, 2, 3]);
// result: Right([1, 2, 3])
const mixed = NumberArray.decode([1, "2", 3]);
// result: Left([validation error for index 1])Creates a codec that validates against one of several possible types.
/**
* Create a union codec that validates against one of the provided codecs
* @param codecs - Array of at least 2 codecs to try
* @param name - Optional name for the codec
*/
function union<CS extends [Mixed, Mixed, ...Array<Mixed>]>(
codecs: CS,
name?: string
): UnionC<CS>;
interface UnionC<CS extends [Mixed, Mixed, ...Array<Mixed>]> extends UnionType<CS,
TypeOf<CS[number]>,
OutputOf<CS[number]>,
unknown
> {}Usage Example:
import * as t from "io-ts";
const StringOrNumber = t.union([t.string, t.number]);
const Status = t.union([
t.literal('pending'),
t.literal('completed'),
t.literal('failed')
]);
const result1 = StringOrNumber.decode("hello");
// result: Right("hello")
const result2 = StringOrNumber.decode(42);
// result: Right(42)
const status = Status.decode('pending');
// result: Right('pending')Creates a codec that validates against all of the provided types.
/**
* Create an intersection codec that validates against all provided codecs
* @param codecs - Array of at least 2 codecs to intersect
* @param name - Optional name for the codec
*/
function intersection<CS extends [Mixed, Mixed, ...Array<Mixed>]>(
codecs: CS,
name?: string
): IntersectionC<CS>;
interface IntersectionC<CS extends [Mixed, Mixed, ...Array<Mixed>]> extends IntersectionType<CS,
// Complex conditional type for intersection result
unknown,
unknown,
unknown
> {}Usage Example:
import * as t from "io-ts";
const Named = t.type({ name: t.string });
const Aged = t.type({ age: t.number });
const Person = t.intersection([Named, Aged]);
type Person = t.TypeOf<typeof Person>;
// Person = { name: string } & { age: number }
const result = Person.decode({ name: "Alice", age: 30 });
// result: Right({ name: "Alice", age: 30 })Creates a codec for fixed-length arrays with specific types at each position.
/**
* Create a tuple codec with specific types at each position
* @param codecs - Array of codecs for each tuple position
* @param name - Optional name for the codec
*/
function tuple<CS extends [Mixed, ...Array<Mixed>]>(
codecs: CS,
name?: string
): TupleC<CS>;
interface TupleC<CS extends [Mixed, ...Array<Mixed>]> extends TupleType<CS,
// Complex conditional type for tuple result
unknown,
unknown,
unknown
> {}Usage Example:
import * as t from "io-ts";
const Coordinate = t.tuple([t.number, t.number]);
const NamedPoint = t.tuple([t.string, t.number, t.number]);
const point = Coordinate.decode([10, 20]);
// result: Right([10, 20])
const namedPoint = NamedPoint.decode(["origin", 0, 0]);
// result: Right(["origin", 0, 0])Creates a codec for objects with dynamic keys and uniform value types.
/**
* Create a record codec with domain keys and codomain values
* @param domain - Codec for the keys
* @param codomain - Codec for the values
* @param name - Optional name for the codec
*/
function record<D extends Mixed, C extends Mixed>(
domain: D,
codomain: C,
name?: string
): RecordC<D, C>;
interface RecordC<D extends Mixed, C extends Mixed> extends DictionaryType<D, C,
{ [K in TypeOf<D>]: TypeOf<C> },
{ [K in OutputOf<D>]: OutputOf<C> },
unknown
> {}Usage Example:
import * as t from "io-ts";
const StringRecord = t.record(t.string, t.number);
const Scores = t.record(t.keyof({ alice: null, bob: null, charlie: null }), t.number);
const scores = StringRecord.decode({ math: 95, science: 87 });
// result: Right({ math: 95, science: 87 })
const playerScores = Scores.decode({ alice: 100, bob: 85 });
// result: Right({ alice: 100, bob: 85 })Creates a codec for exact literal values.
/**
* Create a literal value codec
* @param value - The exact value to match
* @param name - Optional name for the codec
*/
function literal<V extends LiteralValue>(value: V, name?: string): LiteralC<V>;
interface LiteralC<V extends LiteralValue> extends LiteralType<V> {}
type LiteralValue = string | number | boolean;Usage Example:
import * as t from "io-ts";
const StatusPending = t.literal('pending');
const Version = t.literal(1);
const IsActive = t.literal(true);
const result = StatusPending.decode('pending');
// result: Right('pending')
const invalid = StatusPending.decode('completed');
// result: Left([validation error])Creates a codec for keys of an object type.
/**
* Create a keyof codec from object keys
* @param keys - Object whose keys define the valid values
* @param name - Optional name for the codec
*/
function keyof<D extends { [key: string]: unknown }>(
keys: D,
name?: string
): KeyofC<D>;
interface KeyofC<D extends { [key: string]: unknown }> extends KeyofType<D> {}Usage Example:
import * as t from "io-ts";
const Color = t.keyof({ red: null, green: null, blue: null });
type Color = t.TypeOf<typeof Color>;
// Color = "red" | "green" | "blue"
const result = Color.decode('red');
// result: Right('red')
const invalid = Color.decode('yellow');
// result: Left([validation error])Make codecs readonly to prevent mutations.
/**
* Make a codec readonly
* @param codec - The codec to make readonly
* @param name - Optional name for the codec
*/
function readonly<C extends Mixed>(codec: C, name?: string): ReadonlyC<C>;
interface ReadonlyC<C extends Mixed> extends ReadonlyType<C,
Readonly<TypeOf<C>>,
Readonly<OutputOf<C>>,
unknown
> {}
/**
* Create a readonly array codec
* @param item - Codec for array elements
* @param name - Optional name for the codec
*/
function readonlyArray<C extends Mixed>(item: C, name?: string): ReadonlyArrayC<C>;
interface ReadonlyArrayC<C extends Mixed> extends ReadonlyArrayType<C,
ReadonlyArray<TypeOf<C>>,
ReadonlyArray<OutputOf<C>>,
unknown
> {}Usage Example:
import * as t from "io-ts";
const ReadonlyUser = t.readonly(t.type({
name: t.string,
age: t.number
}));
const ReadonlyNumbers = t.readonlyArray(t.number);
type ReadonlyUser = t.TypeOf<typeof ReadonlyUser>;
// ReadonlyUser = Readonly<{ name: string; age: number }>Strip additional properties from objects.
/**
* Strip additional properties from a codec
* @param codec - The codec to make exact
* @param name - Optional name for the codec
*/
function exact<C extends HasProps>(codec: C, name?: string): ExactC<C>;
interface ExactC<C extends HasProps> extends ExactType<C, TypeOf<C>, OutputOf<C>, InputOf<C>> {}
/**
* Create a strict interface codec (strips extra properties)
* @param props - Object mapping property names to their codecs
* @param name - Optional name for the codec
*/
function strict<P extends Props>(props: P, name?: string): ExactC<TypeC<P>>;Usage Example:
import * as t from "io-ts";
const User = t.type({
name: t.string,
age: t.number
});
const StrictUser = t.strict({
name: t.string,
age: t.number
});
const data = { name: "Alice", age: 30, extra: "ignored" };
const lenient = User.decode(data);
// result: Right({ name: "Alice", age: 30, extra: "ignored" })
const strict = StrictUser.decode(data);
// result: Right({ name: "Alice", age: 30 }) - extra property strippedCreate recursive type definitions for self-referential data structures.
/**
* Create a recursive codec for self-referential types
* @param name - Name for the recursive type
* @param definition - Function that defines the recursive structure
*/
function recursion<A, O = A, I = unknown, C extends Type<A, O, I> = Type<A, O, I>>(
name: string,
definition: (self: C) => C
): RecursiveType<C, A, O, I>;Usage Example:
import * as t from "io-ts";
interface Category {
name: string;
subcategories: Category[];
}
const Category: t.Type<Category> = t.recursion('Category', Self =>
t.type({
name: t.string,
subcategories: t.array(Self)
})
);
const data = {
name: "Electronics",
subcategories: [
{
name: "Computers",
subcategories: []
},
{
name: "Phones",
subcategories: []
}
]
};
const result = Category.decode(data);
// result: Right(nested category structure)import * as t from "io-ts";
const User = t.type({
id: t.number,
profile: t.type({
name: t.string,
email: t.string,
preferences: t.partial({
theme: t.union([t.literal('light'), t.literal('dark')]),
notifications: t.boolean
})
}),
roles: t.array(t.string),
metadata: t.record(t.string, t.unknown)
});
type User = t.TypeOf<typeof User>;import * as t from "io-ts";
const Shape = t.union([
t.type({
kind: t.literal('circle'),
radius: t.number
}),
t.type({
kind: t.literal('rectangle'),
width: t.number,
height: t.number
}),
t.type({
kind: t.literal('triangle'),
base: t.number,
height: t.number
})
]);
type Shape = t.TypeOf<typeof Shape>;
// Shape = { kind: 'circle'; radius: number }
// | { kind: 'rectangle'; width: number; height: number }
// | { kind: 'triangle'; base: number; height: number }Install with Tessl CLI
npx tessl i tessl/npm-io-tsdocs
evals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10