0
# Encoding System
1
2
Experimental encoding system for transforming data between different representations.
3
4
## Capabilities
5
6
### Encoder Interface
7
8
The core Encoder interface for data transformation.
9
10
```typescript { .api }
11
/**
12
* Encoder interface for transforming data to different representations
13
* @template O - Output type after encoding
14
* @template A - Input type to encode
15
*/
16
interface Encoder<O, A> {
17
readonly encode: (a: A) => O;
18
}
19
```
20
21
### Combinator Functions
22
23
Functions for creating and composing encoders.
24
25
```typescript { .api }
26
/**
27
* Create struct encoder from property encoders
28
* @param encoders - Object mapping property names to encoders
29
*/
30
function struct<P>(encoders: { [K in keyof P]: Encoder<P[K], P[K]> }): Encoder<P, P>;
31
32
/**
33
* Create partial struct encoder where properties may be undefined
34
* @param encoders - Object mapping property names to encoders
35
*/
36
function partial<P>(encoders: { [K in keyof P]: Encoder<P[K], P[K]> }): Encoder<Partial<P>, Partial<P>>;
37
38
/**
39
* Create array encoder with element encoder
40
* @param item - Encoder for array elements
41
*/
42
function array<O, A>(item: Encoder<O, A>): Encoder<Array<O>, Array<A>>;
43
44
/**
45
* Create record encoder with value encoder
46
* @param codomain - Encoder for record values
47
*/
48
function record<O, A>(codomain: Encoder<O, A>): Encoder<Record<string, O>, Record<string, A>>;
49
50
/**
51
* Create tuple encoder with component encoders
52
* @param components - Array of encoders for each tuple position
53
*/
54
function tuple<C extends ReadonlyArray<Encoder<any, any>>>(
55
...components: C
56
): Encoder<{ [K in keyof C]: OutputOf<C[K]> }, { [K in keyof C]: TypeOf<C[K]> }>;
57
58
/**
59
* Create sum encoder for discriminated unions
60
* @param tag - Discriminant property name
61
*/
62
function sum<T extends string>(tag: T): <MS extends Record<string, Encoder<any, any>>>(
63
members: MS
64
) => Encoder<OutputOf<MS[keyof MS]>, TypeOf<MS[keyof MS]>>;
65
66
/**
67
* Make encoder nullable
68
* @param encoder - Base encoder to make nullable
69
*/
70
function nullable<O, A>(encoder: Encoder<O, A>): Encoder<O | null, A | null>;
71
72
/**
73
* Create lazy encoder for recursive types
74
* @param f - Function that returns the encoder
75
*/
76
function lazy<O, A>(f: () => Encoder<O, A>): Encoder<O, A>;
77
78
/**
79
* Make encoder readonly
80
* @param encoder - Base encoder to make readonly
81
*/
82
function readonly<O, A>(encoder: Encoder<O, A>): Encoder<Readonly<O>, Readonly<A>>;
83
84
/**
85
* Intersect two encoders
86
* @param right - Second encoder to intersect
87
*/
88
function intersect<P, B>(
89
right: Encoder<P, B>
90
): <O, A>(left: Encoder<O, A>) => Encoder<O & P, A & B>;
91
```
92
93
**Usage Examples:**
94
95
```typescript
96
import * as E from "io-ts/Encoder";
97
98
// Struct encoder
99
const UserEncoder = E.struct({
100
name: E.id<string>(),
101
age: E.id<number>(),
102
email: E.id<string>()
103
});
104
105
// Array encoder
106
const NumbersEncoder = E.array(E.id<number>());
107
108
// Custom encoder that transforms dates to ISO strings
109
const DateToStringEncoder: E.Encoder<string, Date> = {
110
encode: (date: Date) => date.toISOString()
111
};
112
113
// Compose encoders
114
const TimestampedDataEncoder = E.struct({
115
data: E.id<string>(),
116
timestamp: DateToStringEncoder
117
});
118
119
const result = TimestampedDataEncoder.encode({
120
data: "hello",
121
timestamp: new Date("2023-01-01")
122
});
123
// result: { data: "hello", timestamp: "2023-01-01T00:00:00.000Z" }
124
```
125
126
### Functional Composition
127
128
Functional programming utilities for encoder composition.
129
130
```typescript { .api }
131
/**
132
* Contramap over encoder (transform input before encoding)
133
* @param f - Function to transform input
134
*/
135
function contramap<A, B>(f: (b: B) => A): <O>(encoder: Encoder<O, A>) => Encoder<O, B>;
136
137
/**
138
* Compose two encoders
139
* @param to - Target encoder
140
*/
141
function compose<E, A>(to: Encoder<E, A>): <O>(from: Encoder<A, O>) => Encoder<E, O>;
142
143
/**
144
* Identity encoder
145
*/
146
function id<A>(): Encoder<A, A>;
147
```
148
149
**Usage Examples:**
150
151
```typescript
152
import * as E from "io-ts/Encoder";
153
import { pipe } from "fp-ts/function";
154
155
// Contramap example: encode Person as string representation
156
interface Person {
157
firstName: string;
158
lastName: string;
159
age: number;
160
}
161
162
const PersonToStringEncoder = pipe(
163
E.id<string>(),
164
E.contramap((person: Person) =>
165
`${person.firstName} ${person.lastName} (${person.age})`
166
)
167
);
168
169
const person: Person = {
170
firstName: "Alice",
171
lastName: "Smith",
172
age: 30
173
};
174
175
const encoded = PersonToStringEncoder.encode(person);
176
// result: "Alice Smith (30)"
177
178
// Compose encoders
179
const NumberToStringEncoder = pipe(
180
E.id<string>(),
181
E.contramap(String)
182
);
183
184
const result = NumberToStringEncoder.encode(42);
185
// result: "42"
186
```
187
188
### Type Utilities
189
190
Type extraction utilities for encoders.
191
192
```typescript { .api }
193
/** Extract output type from encoder */
194
type OutputOf<E> = E extends Encoder<infer O, any> ? O : never;
195
196
/** Extract input type from encoder */
197
type TypeOf<E> = E extends Encoder<any, infer A> ? A : never;
198
```
199
200
### Functional Programming Support
201
202
Instances for functional programming patterns.
203
204
```typescript { .api }
205
/** Module URI for HKT support */
206
const URI = 'io-ts/Encoder';
207
208
/** Contravariant functor instance */
209
const Contravariant: Contravariant1<typeof URI>;
210
211
/** Category instance */
212
const Category: Category1<typeof URI>;
213
```
214
215
## Advanced Usage Patterns
216
217
### Data Transformation Pipeline
218
219
```typescript
220
import * as E from "io-ts/Encoder";
221
import { pipe } from "fp-ts/function";
222
223
// Transform complex objects for API serialization
224
interface User {
225
id: number;
226
profile: {
227
firstName: string;
228
lastName: string;
229
birthDate: Date;
230
};
231
preferences: {
232
theme: 'light' | 'dark';
233
notifications: boolean;
234
};
235
}
236
237
interface UserApiFormat {
238
id: number;
239
fullName: string;
240
birthYear: number;
241
theme: string;
242
notificationsEnabled: boolean;
243
}
244
245
const UserToApiEncoder: E.Encoder<UserApiFormat, User> = {
246
encode: (user: User) => ({
247
id: user.id,
248
fullName: `${user.profile.firstName} ${user.profile.lastName}`,
249
birthYear: user.profile.birthDate.getFullYear(),
250
theme: user.preferences.theme,
251
notificationsEnabled: user.preferences.notifications
252
})
253
};
254
255
const user: User = {
256
id: 123,
257
profile: {
258
firstName: "Alice",
259
lastName: "Johnson",
260
birthDate: new Date("1990-05-15")
261
},
262
preferences: {
263
theme: 'dark',
264
notifications: true
265
}
266
};
267
268
const apiFormat = UserToApiEncoder.encode(user);
269
// result: {
270
// id: 123,
271
// fullName: "Alice Johnson",
272
// birthYear: 1990,
273
// theme: "dark",
274
// notificationsEnabled: true
275
// }
276
```
277
278
### Database Entity Encoding
279
280
```typescript
281
import * as E from "io-ts/Encoder";
282
283
// Encode domain objects for database storage
284
interface Product {
285
id: string;
286
name: string;
287
price: number;
288
categories: string[];
289
metadata: Record<string, unknown>;
290
createdAt: Date;
291
updatedAt: Date;
292
}
293
294
interface ProductRow {
295
id: string;
296
name: string;
297
price_cents: number;
298
categories_json: string;
299
metadata_json: string;
300
created_at: string;
301
updated_at: string;
302
}
303
304
const ProductToRowEncoder: E.Encoder<ProductRow, Product> = {
305
encode: (product: Product) => ({
306
id: product.id,
307
name: product.name,
308
price_cents: Math.round(product.price * 100),
309
categories_json: JSON.stringify(product.categories),
310
metadata_json: JSON.stringify(product.metadata),
311
created_at: product.createdAt.toISOString(),
312
updated_at: product.updatedAt.toISOString()
313
})
314
};
315
316
const product: Product = {
317
id: "prod-123",
318
name: "Laptop",
319
price: 999.99,
320
categories: ["electronics", "computers"],
321
metadata: { brand: "TechCorp", model: "X1" },
322
createdAt: new Date("2023-01-01"),
323
updatedAt: new Date("2023-01-15")
324
};
325
326
const dbRow = ProductToRowEncoder.encode(product);
327
```
328
329
### Format Conversion
330
331
```typescript
332
import * as E from "io-ts/Encoder";
333
import { pipe } from "fp-ts/function";
334
335
// Convert between different date/time formats
336
const IsoDateEncoder = pipe(
337
E.id<string>(),
338
E.contramap((date: Date) => date.toISOString())
339
);
340
341
const UnixTimestampEncoder = pipe(
342
E.id<number>(),
343
E.contramap((date: Date) => Math.floor(date.getTime() / 1000))
344
);
345
346
const LocaleDateEncoder = pipe(
347
E.id<string>(),
348
E.contramap((date: Date) => date.toLocaleDateString())
349
);
350
351
// Create different representations of the same data
352
const now = new Date();
353
354
const formats = {
355
iso: IsoDateEncoder.encode(now),
356
unix: UnixTimestampEncoder.encode(now),
357
locale: LocaleDateEncoder.encode(now)
358
};
359
360
console.log(formats);
361
// Output: {
362
// iso: "2023-01-01T12:00:00.000Z",
363
// unix: 1672574400,
364
// locale: "1/1/2023"
365
// }
366
```
367
368
### Configuration Serialization
369
370
```typescript
371
import * as E from "io-ts/Encoder";
372
373
// Encode configuration objects for storage or transmission
374
interface AppConfig {
375
database: {
376
host: string;
377
port: number;
378
ssl: boolean;
379
};
380
features: {
381
enableAnalytics?: boolean;
382
maxConnections?: number;
383
};
384
environment: 'development' | 'staging' | 'production';
385
}
386
387
interface ConfigFile {
388
database_url: string;
389
analytics_enabled: boolean;
390
max_connections: number;
391
env: string;
392
}
393
394
const ConfigToFileEncoder: E.Encoder<ConfigFile, AppConfig> = {
395
encode: (config: AppConfig) => ({
396
database_url: `${config.database.ssl ? 'postgresql' : 'postgres'}://${config.database.host}:${config.database.port}`,
397
analytics_enabled: config.features.enableAnalytics ?? false,
398
max_connections: config.features.maxConnections ?? 10,
399
env: config.environment
400
})
401
};
402
403
const appConfig: AppConfig = {
404
database: {
405
host: "localhost",
406
port: 5432,
407
ssl: true
408
},
409
features: {
410
enableAnalytics: true,
411
maxConnections: 20
412
},
413
environment: 'production'
414
};
415
416
const configFile = ConfigToFileEncoder.encode(appConfig);
417
// result: {
418
// database_url: "postgresql://localhost:5432",
419
// analytics_enabled: true,
420
// max_connections: 20,
421
// env: "production"
422
// }
423
```