or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

codec.mdcombinators.mdcore-types.mddecoder.mdencoder.mdindex.mdinfrastructure.mdprimitives.mdrefinement.mdreporters.mdschema.mdtask-decoder.mdvalidation.md
tile.json

combinators.mddocs/

0

# Combinators

1

2

Functions for composing complex types from simpler ones, enabling validation of objects, arrays, unions, intersections, and more.

3

4

## Capabilities

5

6

### Object Type Combinator

7

8

Creates a codec for objects with required properties.

9

10

```typescript { .api }

11

/**

12

* Create an interface codec from properties

13

* @param props - Object mapping property names to their codecs

14

* @param name - Optional name for the codec

15

*/

16

function type<P extends Props>(props: P, name?: string): TypeC<P>;

17

18

interface TypeC<P extends Props> extends InterfaceType<P,

19

{ [K in keyof P]: TypeOf<P[K]> },

20

{ [K in keyof P]: OutputOf<P[K]> },

21

unknown

22

> {}

23

```

24

25

**Usage Example:**

26

27

```typescript

28

import * as t from "io-ts";

29

30

const User = t.type({

31

name: t.string,

32

age: t.number,

33

email: t.string

34

});

35

36

type User = t.TypeOf<typeof User>;

37

// User = { name: string; age: number; email: string }

38

39

const result = User.decode({

40

name: "Alice",

41

age: 30,

42

email: "alice@example.com"

43

});

44

// result: Right({ name: "Alice", age: 30, email: "alice@example.com" })

45

```

46

47

### Partial Type Combinator

48

49

Creates a codec for objects with optional properties.

50

51

```typescript { .api }

52

/**

53

* Create a partial interface codec where all properties are optional

54

* @param props - Object mapping property names to their codecs

55

* @param name - Optional name for the codec

56

*/

57

function partial<P extends Props>(props: P, name?: string): PartialC<P>;

58

59

interface PartialC<P extends Props> extends PartialType<P,

60

{ [K in keyof P]?: TypeOf<P[K]> },

61

{ [K in keyof P]?: OutputOf<P[K]> },

62

unknown

63

> {}

64

```

65

66

**Usage Example:**

67

68

```typescript

69

import * as t from "io-ts";

70

71

const PartialUser = t.partial({

72

name: t.string,

73

age: t.number,

74

email: t.string

75

});

76

77

type PartialUser = t.TypeOf<typeof PartialUser>;

78

// PartialUser = { name?: string; age?: number; email?: string }

79

80

const result = PartialUser.decode({ name: "Alice" });

81

// result: Right({ name: "Alice" })

82

```

83

84

### Array Combinator

85

86

Creates a codec for arrays with elements of a specific type.

87

88

```typescript { .api }

89

/**

90

* Create an array codec with elements of type C

91

* @param item - Codec for array elements

92

* @param name - Optional name for the codec

93

*/

94

function array<C extends Mixed>(item: C, name?: string): ArrayC<C>;

95

96

interface ArrayC<C extends Mixed> extends ArrayType<C,

97

Array<TypeOf<C>>,

98

Array<OutputOf<C>>,

99

unknown

100

> {}

101

```

102

103

**Usage Example:**

104

105

```typescript

106

import * as t from "io-ts";

107

108

const NumberArray = t.array(t.number);

109

const StringArray = t.array(t.string);

110

111

const numbers = NumberArray.decode([1, 2, 3]);

112

// result: Right([1, 2, 3])

113

114

const mixed = NumberArray.decode([1, "2", 3]);

115

// result: Left([validation error for index 1])

116

```

117

118

### Union Combinator

119

120

Creates a codec that validates against one of several possible types.

121

122

```typescript { .api }

123

/**

124

* Create a union codec that validates against one of the provided codecs

125

* @param codecs - Array of at least 2 codecs to try

126

* @param name - Optional name for the codec

127

*/

128

function union<CS extends [Mixed, Mixed, ...Array<Mixed>]>(

129

codecs: CS,

130

name?: string

131

): UnionC<CS>;

132

133

interface UnionC<CS extends [Mixed, Mixed, ...Array<Mixed>]> extends UnionType<CS,

134

TypeOf<CS[number]>,

135

OutputOf<CS[number]>,

136

unknown

137

> {}

138

```

139

140

**Usage Example:**

141

142

```typescript

143

import * as t from "io-ts";

144

145

const StringOrNumber = t.union([t.string, t.number]);

146

const Status = t.union([

147

t.literal('pending'),

148

t.literal('completed'),

149

t.literal('failed')

150

]);

151

152

const result1 = StringOrNumber.decode("hello");

153

// result: Right("hello")

154

155

const result2 = StringOrNumber.decode(42);

156

// result: Right(42)

157

158

const status = Status.decode('pending');

159

// result: Right('pending')

160

```

161

162

### Intersection Combinator

163

164

Creates a codec that validates against all of the provided types.

165

166

```typescript { .api }

167

/**

168

* Create an intersection codec that validates against all provided codecs

169

* @param codecs - Array of at least 2 codecs to intersect

170

* @param name - Optional name for the codec

171

*/

172

function intersection<CS extends [Mixed, Mixed, ...Array<Mixed>]>(

173

codecs: CS,

174

name?: string

175

): IntersectionC<CS>;

176

177

interface IntersectionC<CS extends [Mixed, Mixed, ...Array<Mixed>]> extends IntersectionType<CS,

178

// Complex conditional type for intersection result

179

unknown,

180

unknown,

181

unknown

182

> {}

183

```

184

185

**Usage Example:**

186

187

```typescript

188

import * as t from "io-ts";

189

190

const Named = t.type({ name: t.string });

191

const Aged = t.type({ age: t.number });

192

const Person = t.intersection([Named, Aged]);

193

194

type Person = t.TypeOf<typeof Person>;

195

// Person = { name: string } & { age: number }

196

197

const result = Person.decode({ name: "Alice", age: 30 });

198

// result: Right({ name: "Alice", age: 30 })

199

```

200

201

### Tuple Combinator

202

203

Creates a codec for fixed-length arrays with specific types at each position.

204

205

```typescript { .api }

206

/**

207

* Create a tuple codec with specific types at each position

208

* @param codecs - Array of codecs for each tuple position

209

* @param name - Optional name for the codec

210

*/

211

function tuple<CS extends [Mixed, ...Array<Mixed>]>(

212

codecs: CS,

213

name?: string

214

): TupleC<CS>;

215

216

interface TupleC<CS extends [Mixed, ...Array<Mixed>]> extends TupleType<CS,

217

// Complex conditional type for tuple result

218

unknown,

219

unknown,

220

unknown

221

> {}

222

```

223

224

**Usage Example:**

225

226

```typescript

227

import * as t from "io-ts";

228

229

const Coordinate = t.tuple([t.number, t.number]);

230

const NamedPoint = t.tuple([t.string, t.number, t.number]);

231

232

const point = Coordinate.decode([10, 20]);

233

// result: Right([10, 20])

234

235

const namedPoint = NamedPoint.decode(["origin", 0, 0]);

236

// result: Right(["origin", 0, 0])

237

```

238

239

### Record Combinator

240

241

Creates a codec for objects with dynamic keys and uniform value types.

242

243

```typescript { .api }

244

/**

245

* Create a record codec with domain keys and codomain values

246

* @param domain - Codec for the keys

247

* @param codomain - Codec for the values

248

* @param name - Optional name for the codec

249

*/

250

function record<D extends Mixed, C extends Mixed>(

251

domain: D,

252

codomain: C,

253

name?: string

254

): RecordC<D, C>;

255

256

interface RecordC<D extends Mixed, C extends Mixed> extends DictionaryType<D, C,

257

{ [K in TypeOf<D>]: TypeOf<C> },

258

{ [K in OutputOf<D>]: OutputOf<C> },

259

unknown

260

> {}

261

```

262

263

**Usage Example:**

264

265

```typescript

266

import * as t from "io-ts";

267

268

const StringRecord = t.record(t.string, t.number);

269

const Scores = t.record(t.keyof({ alice: null, bob: null, charlie: null }), t.number);

270

271

const scores = StringRecord.decode({ math: 95, science: 87 });

272

// result: Right({ math: 95, science: 87 })

273

274

const playerScores = Scores.decode({ alice: 100, bob: 85 });

275

// result: Right({ alice: 100, bob: 85 })

276

```

277

278

### Literal Combinator

279

280

Creates a codec for exact literal values.

281

282

```typescript { .api }

283

/**

284

* Create a literal value codec

285

* @param value - The exact value to match

286

* @param name - Optional name for the codec

287

*/

288

function literal<V extends LiteralValue>(value: V, name?: string): LiteralC<V>;

289

290

interface LiteralC<V extends LiteralValue> extends LiteralType<V> {}

291

292

type LiteralValue = string | number | boolean;

293

```

294

295

**Usage Example:**

296

297

```typescript

298

import * as t from "io-ts";

299

300

const StatusPending = t.literal('pending');

301

const Version = t.literal(1);

302

const IsActive = t.literal(true);

303

304

const result = StatusPending.decode('pending');

305

// result: Right('pending')

306

307

const invalid = StatusPending.decode('completed');

308

// result: Left([validation error])

309

```

310

311

### KeyOf Combinator

312

313

Creates a codec for keys of an object type.

314

315

```typescript { .api }

316

/**

317

* Create a keyof codec from object keys

318

* @param keys - Object whose keys define the valid values

319

* @param name - Optional name for the codec

320

*/

321

function keyof<D extends { [key: string]: unknown }>(

322

keys: D,

323

name?: string

324

): KeyofC<D>;

325

326

interface KeyofC<D extends { [key: string]: unknown }> extends KeyofType<D> {}

327

```

328

329

**Usage Example:**

330

331

```typescript

332

import * as t from "io-ts";

333

334

const Color = t.keyof({ red: null, green: null, blue: null });

335

336

type Color = t.TypeOf<typeof Color>;

337

// Color = "red" | "green" | "blue"

338

339

const result = Color.decode('red');

340

// result: Right('red')

341

342

const invalid = Color.decode('yellow');

343

// result: Left([validation error])

344

```

345

346

### Readonly Combinators

347

348

Make codecs readonly to prevent mutations.

349

350

```typescript { .api }

351

/**

352

* Make a codec readonly

353

* @param codec - The codec to make readonly

354

* @param name - Optional name for the codec

355

*/

356

function readonly<C extends Mixed>(codec: C, name?: string): ReadonlyC<C>;

357

358

interface ReadonlyC<C extends Mixed> extends ReadonlyType<C,

359

Readonly<TypeOf<C>>,

360

Readonly<OutputOf<C>>,

361

unknown

362

> {}

363

364

/**

365

* Create a readonly array codec

366

* @param item - Codec for array elements

367

* @param name - Optional name for the codec

368

*/

369

function readonlyArray<C extends Mixed>(item: C, name?: string): ReadonlyArrayC<C>;

370

371

interface ReadonlyArrayC<C extends Mixed> extends ReadonlyArrayType<C,

372

ReadonlyArray<TypeOf<C>>,

373

ReadonlyArray<OutputOf<C>>,

374

unknown

375

> {}

376

```

377

378

**Usage Example:**

379

380

```typescript

381

import * as t from "io-ts";

382

383

const ReadonlyUser = t.readonly(t.type({

384

name: t.string,

385

age: t.number

386

}));

387

388

const ReadonlyNumbers = t.readonlyArray(t.number);

389

390

type ReadonlyUser = t.TypeOf<typeof ReadonlyUser>;

391

// ReadonlyUser = Readonly<{ name: string; age: number }>

392

```

393

394

### Exact and Strict Combinators

395

396

Strip additional properties from objects.

397

398

```typescript { .api }

399

/**

400

* Strip additional properties from a codec

401

* @param codec - The codec to make exact

402

* @param name - Optional name for the codec

403

*/

404

function exact<C extends HasProps>(codec: C, name?: string): ExactC<C>;

405

406

interface ExactC<C extends HasProps> extends ExactType<C, TypeOf<C>, OutputOf<C>, InputOf<C>> {}

407

408

/**

409

* Create a strict interface codec (strips extra properties)

410

* @param props - Object mapping property names to their codecs

411

* @param name - Optional name for the codec

412

*/

413

function strict<P extends Props>(props: P, name?: string): ExactC<TypeC<P>>;

414

```

415

416

**Usage Example:**

417

418

```typescript

419

import * as t from "io-ts";

420

421

const User = t.type({

422

name: t.string,

423

age: t.number

424

});

425

426

const StrictUser = t.strict({

427

name: t.string,

428

age: t.number

429

});

430

431

const data = { name: "Alice", age: 30, extra: "ignored" };

432

433

const lenient = User.decode(data);

434

// result: Right({ name: "Alice", age: 30, extra: "ignored" })

435

436

const strict = StrictUser.decode(data);

437

// result: Right({ name: "Alice", age: 30 }) - extra property stripped

438

```

439

440

### Recursive Combinator

441

442

Create recursive type definitions for self-referential data structures.

443

444

```typescript { .api }

445

/**

446

* Create a recursive codec for self-referential types

447

* @param name - Name for the recursive type

448

* @param definition - Function that defines the recursive structure

449

*/

450

function recursion<A, O = A, I = unknown, C extends Type<A, O, I> = Type<A, O, I>>(

451

name: string,

452

definition: (self: C) => C

453

): RecursiveType<C, A, O, I>;

454

```

455

456

**Usage Example:**

457

458

```typescript

459

import * as t from "io-ts";

460

461

interface Category {

462

name: string;

463

subcategories: Category[];

464

}

465

466

const Category: t.Type<Category> = t.recursion('Category', Self =>

467

t.type({

468

name: t.string,

469

subcategories: t.array(Self)

470

})

471

);

472

473

const data = {

474

name: "Electronics",

475

subcategories: [

476

{

477

name: "Computers",

478

subcategories: []

479

},

480

{

481

name: "Phones",

482

subcategories: []

483

}

484

]

485

};

486

487

const result = Category.decode(data);

488

// result: Right(nested category structure)

489

```

490

491

## Advanced Usage Patterns

492

493

### Complex Object Validation

494

495

```typescript

496

import * as t from "io-ts";

497

498

const User = t.type({

499

id: t.number,

500

profile: t.type({

501

name: t.string,

502

email: t.string,

503

preferences: t.partial({

504

theme: t.union([t.literal('light'), t.literal('dark')]),

505

notifications: t.boolean

506

})

507

}),

508

roles: t.array(t.string),

509

metadata: t.record(t.string, t.unknown)

510

});

511

512

type User = t.TypeOf<typeof User>;

513

```

514

515

### Discriminated Unions

516

517

```typescript

518

import * as t from "io-ts";

519

520

const Shape = t.union([

521

t.type({

522

kind: t.literal('circle'),

523

radius: t.number

524

}),

525

t.type({

526

kind: t.literal('rectangle'),

527

width: t.number,

528

height: t.number

529

}),

530

t.type({

531

kind: t.literal('triangle'),

532

base: t.number,

533

height: t.number

534

})

535

]);

536

537

type Shape = t.TypeOf<typeof Shape>;

538

// Shape = { kind: 'circle'; radius: number }

539

// | { kind: 'rectangle'; width: number; height: number }

540

// | { kind: 'triangle'; base: number; height: number }

541

```