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

codec.mddocs/

0

# Codec System

1

2

Experimental unified codec system combining decoding and encoding operations with enhanced composability.

3

4

## Capabilities

5

6

### Codec Interface

7

8

The core Codec interface that combines Decoder and Encoder functionality.

9

10

```typescript { .api }

11

/**

12

* Codec interface combining decoding and encoding operations

13

* @template I - Input type for decoding

14

* @template O - Output type for encoding

15

* @template A - The runtime type

16

*/

17

interface Codec<I, O, A> extends Decoder<I, A>, Encoder<O, A> {}

18

```

19

20

### Constructor Functions

21

22

Functions for creating codecs from decoders and encoders.

23

24

```typescript { .api }

25

/**

26

* Create a codec from separate decoder and encoder

27

* @param decoder - The decoder for input validation

28

* @param encoder - The encoder for output transformation

29

*/

30

function make<I, O, A>(decoder: Decoder<I, A>, encoder: Encoder<O, A>): Codec<I, O, A>;

31

32

/**

33

* Create a codec from decoder with identity encoder

34

* @param decoder - The decoder to wrap

35

*/

36

function fromDecoder<I, A>(decoder: Decoder<I, A>): Codec<I, A, A>;

37

38

/**

39

* Create a literal value codec

40

* @param value - The literal value to encode/decode

41

*/

42

function literal<A extends Literal>(value: A): Codec<unknown, A, A>;

43

44

type Literal = string | number | boolean | null;

45

```

46

47

**Usage Examples:**

48

49

```typescript

50

import * as C from "io-ts/Codec";

51

import * as D from "io-ts/Decoder";

52

import * as E from "io-ts/Encoder";

53

54

// Create codec from decoder and encoder

55

const TimestampCodec = C.make(

56

D.number, // Decode from number

57

E.id<Date>().contramap((date: Date) => date.getTime()) // Encode Date to number

58

);

59

60

// Create codec from decoder only (identity encoding)

61

const StringCodec = C.fromDecoder(D.string);

62

63

// Literal codec

64

const StatusCodec = C.literal('active');

65

```

66

67

### Primitive Codecs

68

69

Built-in codecs for basic types.

70

71

```typescript { .api }

72

/** String codec */

73

const string: Codec<unknown, string, string>;

74

75

/** Number codec */

76

const number: Codec<unknown, number, number>;

77

78

/** Boolean codec */

79

const boolean: Codec<unknown, boolean, boolean>;

80

81

/** Unknown array codec */

82

const UnknownArray: Codec<unknown, Array<unknown>, Array<unknown>>;

83

84

/** Unknown record codec */

85

const UnknownRecord: Codec<unknown, Record<string, unknown>, Record<string, unknown>>;

86

```

87

88

### Combinator Functions

89

90

Functions for composing complex codecs.

91

92

```typescript { .api }

93

/**

94

* Create struct codec from property codecs

95

* @param properties - Object mapping property names to codecs

96

*/

97

function struct<P>(properties: { [K in keyof P]: Codec<unknown, P[K], P[K]> }): Codec<unknown, P, P>;

98

99

/**

100

* Create partial struct codec where all properties are optional

101

* @param properties - Object mapping property names to codecs

102

*/

103

function partial<P>(properties: { [K in keyof P]: Codec<unknown, P[K], P[K]> }): Codec<unknown, Partial<P>, Partial<P>>;

104

105

/**

106

* Create array codec with element codec

107

* @param item - Codec for array elements

108

*/

109

function array<O, A>(item: Codec<unknown, O, A>): Codec<unknown, Array<O>, Array<A>>;

110

111

/**

112

* Create record codec with value codec

113

* @param codomain - Codec for record values

114

*/

115

function record<O, A>(codomain: Codec<unknown, O, A>): Codec<unknown, Record<string, O>, Record<string, A>>;

116

117

/**

118

* Create tuple codec with component codecs

119

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

120

*/

121

function tuple<C extends ReadonlyArray<Codec<unknown, any, any>>>(

122

...components: C

123

): Codec<unknown, { [K in keyof C]: OutputOf<C[K]> }, { [K in keyof C]: TypeOf<C[K]> }>;

124

125

/**

126

* Create sum codec for discriminated unions

127

* @param tag - Discriminant property name

128

*/

129

function sum<T extends string>(tag: T): <MS extends Record<string, Codec<unknown, any, any>>>(

130

members: MS

131

) => Codec<unknown, OutputOf<MS[keyof MS]>, TypeOf<MS[keyof MS]>>;

132

```

133

134

**Usage Examples:**

135

136

```typescript

137

import * as C from "io-ts/Codec";

138

139

// Struct codec

140

const User = C.struct({

141

name: C.string,

142

age: C.number,

143

email: C.string

144

});

145

146

// Array codec

147

const Numbers = C.array(C.number);

148

149

// Record codec

150

const Scores = C.record(C.number);

151

152

// Tuple codec

153

const Point = C.tuple(C.number, C.number);

154

155

// Sum codec for discriminated unions

156

const Shape = C.sum('type')({

157

circle: C.struct({

158

type: C.literal('circle'),

159

radius: C.number

160

}),

161

rectangle: C.struct({

162

type: C.literal('rectangle'),

163

width: C.number,

164

height: C.number

165

})

166

});

167

```

168

169

### Advanced Combinators

170

171

More sophisticated codec combinators.

172

173

```typescript { .api }

174

/**

175

* Refine a codec with additional predicate

176

* @param predicate - Predicate function for refinement

177

* @param expected - Expected type description

178

*/

179

function refine<A, B extends A>(

180

predicate: Refinement<A, B>,

181

expected: string

182

): <I, O>(codec: Codec<I, O, A>) => Codec<I, O, B>;

183

184

/**

185

* Make codec nullable (accepts null values)

186

* @param codec - Base codec to make nullable

187

*/

188

function nullable<I, O, A>(codec: Codec<I, O, A>): Codec<I, O | null, A | null>;

189

190

/**

191

* Create lazy codec for recursive types

192

* @param f - Function that returns the codec

193

*/

194

function lazy<I, O, A>(f: () => Codec<I, O, A>): Codec<I, O, A>;

195

196

/**

197

* Make codec readonly

198

* @param codec - Base codec to make readonly

199

*/

200

function readonly<I, O, A>(codec: Codec<I, O, A>): Codec<I, Readonly<O>, Readonly<A>>;

201

202

/**

203

* Compose two codecs

204

* @param to - Target codec

205

*/

206

function compose<L, A, P, B>(

207

from: Codec<L, A, A>,

208

to: Codec<A, P, B>

209

): Codec<L, P, B>;

210

211

/**

212

* Intersect two codecs

213

* @param right - Second codec to intersect

214

*/

215

function intersect<IB, OB, B>(

216

right: Codec<IB, OB, B>

217

): <IA, OA, A>(left: Codec<IA, OA, A>) => Codec<IA & IB, OA & OB, A & B>;

218

219

/**

220

* Map left side with input for better error handling

221

* @param f - Function to transform errors with input

222

*/

223

function mapLeftWithInput<I>(

224

f: (input: I, error: DecodeError) => DecodeError

225

): <O, A>(codec: Codec<I, O, A>) => Codec<I, O, A>;

226

```

227

228

**Usage Examples:**

229

230

```typescript

231

import * as C from "io-ts/Codec";

232

import { pipe } from "fp-ts/function";

233

234

// Refined codec

235

const PositiveNumber = pipe(

236

C.number,

237

C.refine((n): n is number => n > 0, 'positive number')

238

);

239

240

// Nullable codec

241

const OptionalString = C.nullable(C.string);

242

243

// Recursive codec

244

interface Category {

245

name: string;

246

subcategories: Category[];

247

}

248

249

const Category: C.Codec<unknown, Category, Category> = C.lazy(() =>

250

C.struct({

251

name: C.string,

252

subcategories: C.array(Category)

253

})

254

);

255

256

// Composition

257

const StringToNumber = C.compose(

258

C.string,

259

C.make(

260

pipe(D.string, D.parse(s => {

261

const n = parseFloat(s);

262

return isNaN(n) ? left(`Invalid number: ${s}`) : right(n);

263

})),

264

E.id<number>().contramap(String)

265

)

266

);

267

```

268

269

### Type Utilities

270

271

Type extraction utilities for codecs.

272

273

```typescript { .api }

274

/** Extract input type from codec */

275

type InputOf<C> = C extends Codec<infer I, any, any> ? I : never;

276

277

/** Extract output type from codec */

278

type OutputOf<C> = C extends Codec<any, infer O, any> ? O : never;

279

280

/** Extract runtime type from codec */

281

type TypeOf<C> = C extends Codec<any, any, infer A> ? A : never;

282

```

283

284

### Functional Programming Support

285

286

Instances and utilities for functional programming.

287

288

```typescript { .api }

289

/** Module URI for HKT support */

290

const URI = 'io-ts/Codec';

291

292

/** Invariant functor instance */

293

const Invariant: Invariant1<typeof URI>;

294

295

/**

296

* Invariant map over codec

297

* @param f - Function from A to B

298

* @param g - Function from B to A

299

*/

300

function imap<I, O, A, B>(

301

f: (a: A) => B,

302

g: (b: B) => A

303

): (codec: Codec<I, O, A>) => Codec<I, O, B>;

304

```

305

306

## Advanced Usage Patterns

307

308

### Round-trip Encoding/Decoding

309

310

```typescript

311

import * as C from "io-ts/Codec";

312

import { pipe } from "fp-ts/function";

313

import { fold } from "fp-ts/Either";

314

315

// Create a codec that can round-trip between JSON and typed data

316

const UserCodec = C.struct({

317

id: C.number,

318

name: C.string,

319

createdAt: C.make(

320

pipe(

321

D.string,

322

D.parse(s => {

323

const date = new Date(s);

324

return isNaN(date.getTime()) ? left('Invalid date') : right(date);

325

})

326

),

327

E.id<Date>().contramap(d => d.toISOString())

328

)

329

});

330

331

// Decode from JSON

332

const jsonData = {

333

id: 123,

334

name: "Alice",

335

createdAt: "2023-01-01T00:00:00.000Z"

336

};

337

338

const decodeResult = UserCodec.decode(jsonData);

339

if (decodeResult._tag === 'Right') {

340

const user = decodeResult.right;

341

console.log('Decoded user:', user);

342

343

// Encode back to JSON-serializable format

344

const encoded = UserCodec.encode(user);

345

console.log('Encoded back:', encoded);

346

// Output: { id: 123, name: "Alice", createdAt: "2023-01-01T00:00:00.000Z" }

347

}

348

```

349

350

### API Request/Response Codecs

351

352

```typescript

353

import * as C from "io-ts/Codec";

354

355

// Request codec

356

const CreateUserRequest = C.struct({

357

name: C.string,

358

email: C.string,

359

age: C.number

360

});

361

362

// Response codec with additional server fields

363

const UserResponse = C.struct({

364

id: C.number,

365

name: C.string,

366

email: C.string,

367

age: C.number,

368

createdAt: C.string,

369

updatedAt: C.string

370

});

371

372

// API client function

373

async function createUser(request: C.TypeOf<typeof CreateUserRequest>) {

374

const encodedRequest = CreateUserRequest.encode(request);

375

376

const response = await fetch('/api/users', {

377

method: 'POST',

378

headers: { 'Content-Type': 'application/json' },

379

body: JSON.stringify(encodedRequest)

380

});

381

382

const responseData = await response.json();

383

const decodedResponse = UserResponse.decode(responseData);

384

385

if (decodedResponse._tag === 'Left') {

386

throw new Error('Invalid response format');

387

}

388

389

return decodedResponse.right;

390

}

391

```

392

393

### Configuration Management

394

395

```typescript

396

import * as C from "io-ts/Codec";

397

398

// Environment-specific codec

399

const DatabaseConfig = C.struct({

400

host: C.string,

401

port: C.number,

402

database: C.string,

403

ssl: C.boolean

404

});

405

406

const AppConfig = C.struct({

407

environment: C.union(

408

C.literal('development'),

409

C.literal('staging'),

410

C.literal('production')

411

),

412

database: DatabaseConfig,

413

features: C.partial({

414

enableAnalytics: C.boolean,

415

enableCache: C.boolean,

416

maxConnections: C.number

417

})

418

});

419

420

// Load and validate configuration

421

function loadConfig(): C.TypeOf<typeof AppConfig> {

422

const rawConfig = {

423

environment: process.env.NODE_ENV || 'development',

424

database: {

425

host: process.env.DB_HOST || 'localhost',

426

port: parseInt(process.env.DB_PORT || '5432'),

427

database: process.env.DB_NAME || 'myapp',

428

ssl: process.env.DB_SSL === 'true'

429

},

430

features: {

431

enableAnalytics: process.env.ENABLE_ANALYTICS === 'true',

432

enableCache: process.env.ENABLE_CACHE !== 'false'

433

}

434

};

435

436

const result = AppConfig.decode(rawConfig);

437

if (result._tag === 'Left') {

438

throw new Error(`Invalid configuration: ${D.draw(result.left)}`);

439

}

440

441

return result.right;

442

}

443

```