TypeScript runtime type system for IO decoding/encoding
72
Experimental modern decoder system with enhanced error reporting and functional composition patterns.
The core Decoder interface with functional programming patterns.
/**
* Kleisli decoder interface for functional composition
* @template I - Input type
* @template A - Output type after successful decoding
*/
interface Decoder<I, A> {
readonly decode: (i: I) => Either<DecodeError, A>;
}
/** Decoder error type using FreeSemigroup for error accumulation */
type DecodeError = FreeSemigroup<DE.DecodeError<string>>;Enhanced error system with detailed decode error information.
/**
* Create a decode error
* @param actual - The actual value that failed
* @param message - Error message
*/
function error(actual: unknown, message: string): DecodeError;
/**
* Create a successful decoder result
* @param value - The successful value
*/
function success<A>(value: A): Either<DecodeError, A>;
/**
* Create a failed decoder result
* @param error - The decode error
*/
function failure<A>(error: DecodeError): Either<DecodeError, A>;Functions for creating decoders from various sources.
/**
* Create decoder from a refinement function
* @param refinement - Type refinement function
* @param expected - Expected type description
*/
function fromRefinement<I, A>(
refinement: Refinement<I, A>,
expected: string
): Decoder<I, A>;
/**
* Create decoder from a Guard
* @param guard - Guard instance
* @param expected - Expected type description
*/
function fromGuard<I, A>(guard: G.Guard<I, A>, expected: string): Decoder<I, A>;
/**
* Create a literal value decoder
* @param value - The literal value to match
*/
function literal<A extends Literal>(value: A): Decoder<unknown, A>;
type Literal = string | number | boolean | null;Built-in decoders for basic types.
/** String decoder */
const string: Decoder<unknown, string>;
/** Number decoder */
const number: Decoder<unknown, number>;
/** Boolean decoder */
const boolean: Decoder<unknown, boolean>;
/** Unknown array decoder */
const UnknownArray: Decoder<unknown, Array<unknown>>;
/** Unknown record decoder */
const UnknownRecord: Decoder<unknown, Record<string, unknown>>;Usage Examples:
import * as D from "io-ts/Decoder";
import { pipe } from "fp-ts/function";
import { fold } from "fp-ts/Either";
// Basic decoding
const result = D.string.decode("hello");
pipe(
result,
fold(
(error) => console.error("Decode failed:", D.draw(error)),
(value) => console.log("Success:", value)
)
);
// Literal decoding
const Status = D.literal('active');
const statusResult = Status.decode('active');
// result: Right('active')Functions for composing complex decoders.
/**
* Create struct decoder from property decoders with typed inputs
* @param properties - Object mapping property names to decoders
*/
function fromStruct<P extends Record<string, Decoder<any, any>>>(
properties: P
): Decoder<{ [K in keyof P]: InputOf<P[K]> }, { [K in keyof P]: TypeOf<P[K]> }>;
/**
* Create struct decoder from property decoders (from unknown input)
* @param properties - Object mapping property names to decoders
*/
function struct<A>(properties: { [K in keyof A]: Decoder<unknown, A[K]> }): Decoder<unknown, A>;
/**
* Create partial struct decoder with typed inputs where all properties are optional
* @param properties - Object mapping property names to decoders
*/
function fromPartial<P extends Record<string, Decoder<any, any>>>(
properties: P
): Decoder<Partial<{ [K in keyof P]: InputOf<P[K]> }>, Partial<{ [K in keyof P]: TypeOf<P[K]> }>>;
/**
* Create partial struct decoder where all properties are optional
* @param properties - Object mapping property names to decoders
*/
function partial<A>(properties: { [K in keyof A]: Decoder<unknown, A[K]> }): Decoder<unknown, Partial<A>>;
/**
* Create array decoder with typed inputs
* @param item - Decoder for array elements
*/
function fromArray<I, A>(item: Decoder<I, A>): Decoder<Array<I>, Array<A>>;
/**
* Create array decoder with element decoder
* @param item - Decoder for array elements
*/
function array<A>(item: Decoder<unknown, A>): Decoder<unknown, Array<A>>;
/**
* Create record decoder with typed inputs
* @param codomain - Decoder for record values
*/
function fromRecord<I, A>(codomain: Decoder<I, A>): Decoder<Record<string, I>, Record<string, A>>;
/**
* Create record decoder with value decoder
* @param codomain - Decoder for record values
*/
function record<A>(codomain: Decoder<unknown, A>): Decoder<unknown, Record<string, A>>;
/**
* Create tuple decoder with typed inputs
* @param components - Array of decoders for each tuple position
*/
function fromTuple<C extends ReadonlyArray<Decoder<any, any>>>(
...components: C
): Decoder<{ [K in keyof C]: InputOf<C[K]> }, { [K in keyof C]: TypeOf<C[K]> }>;
/**
* Create tuple decoder with component decoders
* @param components - Array of decoders for each tuple position
*/
function tuple<A extends ReadonlyArray<unknown>>(
...components: { [K in keyof A]: Decoder<unknown, A[K]> }
): Decoder<unknown, A>;
/**
* Create union decoder that tries multiple decoders
* @param members - Array of decoder alternatives
*/
function union<MS extends [Decoder<unknown, any>, ...Array<Decoder<unknown, any>>]>(
...members: MS
): Decoder<unknown, TypeOf<MS[number]>>;
/**
* Create intersection decoder that applies all decoders
* @param right - Second decoder to intersect
*/
function intersect<B>(right: Decoder<unknown, B>): <A>(left: Decoder<unknown, A>) => Decoder<unknown, A & B>;
/**
* Create tagged union decoder from member decoders with typed inputs
* @param tag - The discriminator property key
*/
function fromSum<T extends string>(
tag: T
): <MS extends Record<string, Decoder<any, any>>>(
members: MS
) => Decoder<InputOf<MS[keyof MS]>, TypeOf<MS[keyof MS]>>;
/**
* Create tagged union decoder from member decoders
* @param tag - The discriminator property key
*/
function sum<T extends string>(
tag: T
): <A>(members: { [K in keyof A]: Decoder<unknown, A[K] & Record<T, K>> }) => Decoder<unknown, A[keyof A]>;The following functions are deprecated and should be avoided in new code:
/**
* @deprecated Use fromStruct instead
*/
const fromType: typeof fromStruct;
/**
* @deprecated Use struct instead
*/
const type: typeof struct;Usage Examples:
import * as D from "io-ts/Decoder";
// Struct decoder
const User = D.struct({
name: D.string,
age: D.number,
email: D.string
});
const userData = User.decode({
name: "Alice",
age: 30,
email: "alice@example.com"
});
// Array decoder
const Numbers = D.array(D.number);
const numbersResult = Numbers.decode([1, 2, 3]);
// Union decoder
const StringOrNumber = D.union(D.string, D.number);
const unionResult1 = StringOrNumber.decode("hello");
const unionResult2 = StringOrNumber.decode(42);
// Tuple decoder
const Coordinate = D.tuple(D.number, D.number);
const coordResult = Coordinate.decode([10, 20]);
// Tagged union decoder
const Shape = D.sum('type')({
circle: D.struct({
type: D.literal('circle'),
radius: D.number
}),
rectangle: D.struct({
type: D.literal('rectangle'),
width: D.number,
height: D.number
})
});
const shapeResult = Shape.decode({ type: 'circle', radius: 5 });More sophisticated decoder combinators for complex scenarios.
/**
* Add custom error message to decoder failures
* @param message - Custom error message
*/
function withMessage<I>(message: string): <A>(decoder: Decoder<I, A>) => Decoder<I, A>;
/**
* Refine a decoder with additional predicate
* @param predicate - Predicate function for refinement
* @param expected - Expected type description
*/
function refine<A, B extends A>(
predicate: Refinement<A, B>,
expected: string
): (decoder: Decoder<unknown, A>) => Decoder<unknown, B>;
/**
* Parse decoder result with custom function
* @param parser - Function to transform decoded value (returns Either<DecodeError, B>)
*/
function parse<A, B>(
parser: (a: A) => Either<DecodeError, B>
): <I>(from: Decoder<I, A>) => Decoder<I, B>;
/**
* Make decoder nullable (accepts null values)
* @param decoder - Base decoder to make nullable
*/
function nullable<A>(decoder: Decoder<unknown, A>): Decoder<unknown, A | null>;
/**
* Create lazy decoder for recursive types
* @param f - Function that returns the decoder
*/
function lazy<A>(f: () => Decoder<unknown, A>): Decoder<unknown, A>;
/**
* Map left side with input for better error messages
* @param f - Function to transform errors with input context
*/
function mapLeftWithInput<I>(
f: (input: I, error: DecodeError) => DecodeError
): <A>(decoder: Decoder<I, A>) => Decoder<I, A>;Usage Examples:
import * as D from "io-ts/Decoder";
import { pipe } from "fp-ts/function";
// Custom error messages
const EmailWithMessage = pipe(
D.string,
D.refine((s): s is string => /\S+@\S+\.\S+/.test(s), 'valid email'),
D.withMessage('Please provide a valid email address')
);
// Parse transformation
const StringToNumber = pipe(
D.string,
D.parse((s) => {
const n = parseFloat(s);
return isNaN(n) ? left(`Cannot parse "${s}" as number`) : right(n);
})
);
// Nullable decoder
const OptionalName = D.nullable(D.string);
const result1 = OptionalName.decode("Alice"); // Right("Alice")
const result2 = OptionalName.decode(null); // Right(null)
// Recursive decoder
interface Category {
name: string;
subcategories: Category[];
}
const Category: D.Decoder<unknown, Category> = D.lazy(() =>
D.struct({
name: D.string,
subcategories: D.array(Category)
})
);Functional programming utilities for decoder composition.
/**
* Map over successful decoder results
* @param f - Transformation function
*/
function map<A, B>(f: (a: A) => B): (decoder: Decoder<unknown, A>) => Decoder<unknown, B>;
/**
* Alternative decoder (try second if first fails)
* @param that - Alternative decoder
*/
function alt<A>(that: () => Decoder<unknown, A>): (decoder: Decoder<unknown, A>) => Decoder<unknown, A>;
/**
* Compose two decoders
* @param to - Target decoder
*/
function compose<A, B>(to: Decoder<A, B>): (from: Decoder<unknown, A>) => Decoder<unknown, B>;
/**
* Identity decoder
*/
function id<A>(): Decoder<A, A>;Type extraction and functional programming instances.
/** Extract input type from decoder */
type InputOf<D> = D extends Decoder<infer I, any> ? I : never;
/** Extract output type from decoder */
type TypeOf<D> = D extends Decoder<any, infer A> ? A : never;
/** Module URI for HKT support */
const URI = 'io-ts/Decoder';
/** Functor instance */
const Functor: Functor1<typeof URI>;
/** Alt instance */
const Alt: Alt1<typeof URI>;
/** Category instance */
const Category: Category1<typeof URI>;Utilities for formatting and displaying decode errors.
/**
* Draw decode error as readable string
* @param error - The decode error to format
*/
function draw(error: DecodeError): string;
/**
* Format Either result as string
* @param result - Either result to stringify
*/
function stringify<A>(result: Either<DecodeError, A>): string;Usage Example:
import * as D from "io-ts/Decoder";
const User = D.struct({
name: D.string,
age: D.number
});
const result = User.decode({ name: 123, age: "thirty" });
if (result._tag === "Left") {
console.log("Formatted error:");
console.log(D.draw(result.left));
console.log("Stringified result:");
console.log(D.stringify(result));
}import * as D from "io-ts/Decoder";
import { pipe } from "fp-ts/function";
import { fold } from "fp-ts/Either";
const UserData = pipe(
D.struct({
name: D.string,
email: D.string,
age: D.number
}),
D.refine(
(user): user is typeof user => user.age >= 18,
'adult user'
),
D.withMessage('User must be an adult with valid contact info')
);
const processUser = (input: unknown) =>
pipe(
UserData.decode(input),
fold(
(error) => ({
success: false,
error: D.draw(error)
}),
(user) => ({
success: true,
user: {
...user,
displayName: user.name.toUpperCase()
}
})
)
);import * as D from "io-ts/Decoder";
const Shape = D.union(
D.struct({
type: D.literal('circle'),
radius: D.number
}),
D.struct({
type: D.literal('rectangle'),
width: D.number,
height: D.number
}),
D.struct({
type: D.literal('triangle'),
base: D.number,
height: D.number
})
);
const shapes = [
{ type: 'circle', radius: 5 },
{ type: 'rectangle', width: 10, height: 20 },
{ type: 'triangle', base: 8, height: 12 }
];
shapes.forEach(shape => {
const result = Shape.decode(shape);
if (result._tag === 'Right') {
console.log('Valid shape:', result.right);
}
});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