TypeScript runtime type system for IO decoding/encoding
npx @tessl/cli install tessl/npm-io-ts@2.2.0io-ts is a TypeScript runtime type system for IO decoding/encoding that enables runtime validation of data against TypeScript types. It provides a comprehensive API for defining codecs that can validate, decode, and encode data with full type safety and detailed error reporting.
npm install io-ts fp-tsMain stable API:
import * as t from "io-ts";
// Or specific imports
import { Type, string, number, type TypeOf } from "io-ts";Experimental modules (independent APIs):
import * as D from "io-ts/Decoder";
import * as C from "io-ts/Codec";
import * as E from "io-ts/Encoder";CommonJS:
const t = require("io-ts");
const { PathReporter } = require("io-ts/PathReporter");import * as t from "io-ts";
import { PathReporter } from "io-ts/PathReporter";
// Define a codec
const User = t.type({
name: t.string,
age: t.number,
email: t.string,
});
// Extract the TypeScript type
type User = t.TypeOf<typeof User>;
// Validate data at runtime
const data = { name: "Alice", age: 30, email: "alice@example.com" };
const result = User.decode(data);
if (result._tag === "Right") {
// Valid data
const user: User = result.right;
console.log("Valid user:", user);
} else {
// Invalid data with detailed errors
const errors = PathReporter.failure(result.left);
console.error("Validation errors:", errors);
}io-ts is built around several key architectural patterns:
The foundational Type class and primitive codecs for runtime type validation. This is the main stable API providing comprehensive validation, encoding, and decoding capabilities.
class Type<A, O = A, I = unknown> {
constructor(
name: string,
is: (u: unknown) => u is A,
validate: (i: I, context: Context) => Validation<A>,
encode: (a: A) => O
);
decode(i: I): Validation<A>;
is(u: unknown): u is A;
validate(i: I, context: Context): Validation<A>;
encode(a: A): O;
}
type TypeOf<C extends Any> = C["_A"];
type InputOf<C extends Any> = C["_I"];
type OutputOf<C extends Any> = C["_O"];Built-in codecs for basic JavaScript/TypeScript types with runtime validation.
const string: StringC;
const number: NumberC;
const boolean: BooleanC;
const unknown: UnknownC;
const any: AnyC;
const never: NeverC;
const nullType: NullC;
const voidType: VoidC;
const UnknownArray: UnknownArrayC;
const UnknownRecord: UnknownRecordC;Functions for composing complex types from simpler ones, enabling validation of objects, arrays, unions, and more.
function type<P extends Props>(props: P): TypeC<P>;
function partial<P extends Props>(props: P): PartialC<P>;
function array<C extends Mixed>(item: C): ArrayC<C>;
function union<CS extends [Mixed, Mixed, ...Array<Mixed>]>(codecs: CS): UnionC<CS>;
function intersection<CS extends [Mixed, Mixed, ...Array<Mixed>]>(codecs: CS): IntersectionC<CS>;
function tuple<CS extends [Mixed, ...Array<Mixed>]>(codecs: CS): TupleC<CS>;
function record<D extends Mixed, C extends Mixed>(domain: D, codomain: C): RecordC<D, C>;Advanced type construction for adding runtime constraints and creating branded types.
function refinement<C extends Any>(
codec: C,
predicate: Predicate<TypeOf<C>>,
name?: string
): RefinementC<C>;
function brand<C extends Any, N extends string, B extends { readonly [K in N]: symbol }>(
codec: C,
predicate: Refinement<TypeOf<C>, Branded<TypeOf<C>, B>>,
name: N
): BrandC<C, B>;
const Int: BrandC<NumberC, IntBrand>;Comprehensive error reporting system with detailed context information for debugging validation failures.
interface ValidationError {
readonly value: unknown;
readonly context: Context;
readonly message?: string;
}
type Validation<A> = Either<Errors, A>;
type Errors = Array<ValidationError>;
function success<T>(value: T): Validation<T>;
function failure<T>(value: unknown, context: Context, message?: string): Validation<T>;Experimental modern decoder system with enhanced error reporting and functional composition patterns.
interface Decoder<I, A> {
readonly decode: (i: I) => Either<DecodeError, A>;
}
function fromRefinement<I, A>(refinement: Refinement<I, A>, expected: string): Decoder<I, A>;
function struct<A>(properties: { [K in keyof A]: Decoder<unknown, A[K]> }): Decoder<unknown, A>;
function array<A>(item: Decoder<unknown, A>): Decoder<unknown, Array<A>>;Experimental encoding system for transforming data between different representations.
interface Encoder<O, A> {
readonly encode: (a: A) => O;
}
function struct<P>(encoders: { [K in keyof P]: Encoder<P[K], P[K]> }): Encoder<P, P>;
function array<O, A>(item: Encoder<O, A>): Encoder<Array<O>, Array<A>>;Experimental unified codec system combining decoding and encoding operations with enhanced composability.
interface Codec<I, O, A> extends Decoder<I, A>, Encoder<O, A> {}
function make<I, O, A>(decoder: Decoder<I, A>, encoder: Encoder<O, A>): Codec<I, O, A>;
function struct<P>(codecs: { [K in keyof P]: Codec<unknown, P[K], P[K]> }): Codec<unknown, P, P>;Advanced schema-based type construction and functional programming abstractions for maximum composability.
interface Schema<A> {
<S>(S: Schemable<S>): HKT<S, A>;
}
interface Schemable<S> {
readonly URI: S;
readonly literal: <A extends [Literal, ...Array<Literal>]>(...values: A) => HKT<S, A[number]>;
readonly string: HKT<S, string>;
readonly number: HKT<S, number>;
readonly boolean: HKT<S, boolean>;
}Error reporting interfaces and implementations for converting validation failures into human-readable messages.
interface Reporter<A> {
report: (validation: Validation<any>) => A;
}
const PathReporter: Reporter<Array<string>>;
function failure(es: Array<ValidationError>): Array<string>;
function success(): Array<string>;⚠️ EXPERIMENTAL: Asynchronous decoder system based on TaskEither for validating and decoding data with async operations.
interface TaskDecoder<I, A> extends Kleisli<TaskEither.URI, I, DecodeError, A> {
readonly decode: (i: I) => TaskEither<DecodeError, A>;
}
function fromDecoder<I, A>(decoder: Decoder<I, A>): TaskDecoder<I, A>;
function struct<A>(properties: { [K in keyof A]: TaskDecoder<unknown, A[K]> }): TaskDecoder<unknown, A>;
function parse<A, B>(parser: (a: A) => TaskEither<DecodeError, B>): <I>(from: TaskDecoder<I, A>) => TaskDecoder<I, B>;⚠️ EXPERIMENTAL: Core infrastructure modules that provide error handling and functional composition utilities.
type DecodeError<E> = Leaf<E> | Key<E> | Index<E> | Member<E> | Lazy<E> | Wrap<E>;
type FreeSemigroup<A> = Of<A> | Concat<A>;
function leaf<E>(actual: unknown, error: E): DecodeError<E>;
function key<E>(key: string, kind: Kind, errors: FreeSemigroup<DecodeError<E>>): DecodeError<E>;
function of<A>(value: A): FreeSemigroup<A>;
function concat<A>(left: FreeSemigroup<A>, right: FreeSemigroup<A>): FreeSemigroup<A>;interface Any extends Type<any, any, any> {}
interface Mixed extends Type<any, any, unknown> {}
interface Props {
[key: string]: Mixed;
}
interface AnyProps {
[key: string]: Any;
}
interface Context extends ReadonlyArray<ContextEntry> {}
interface ContextEntry {
readonly key: string;
readonly type: Decoder<any, any>;
readonly actual?: unknown;
}interface Brand<B> {
readonly [_brand]: B;
}
type Branded<A, B> = A & Brand<B>;
interface IntBrand {
readonly Int: unique symbol;
}
type Int = Branded<number, IntBrand>;type Is<A> = (u: unknown) => u is A;
type Validate<I, A> = (i: I, context: Context) => Validation<A>;
type Decode<I, A> = (i: I) => Validation<A>;
type Encode<A, O> = (a: A) => O;