@@ -16,17 +16,15 @@ Also a codec can
16
16
- be used as a custom type guard (through ` is ` )
17
17
18
18
``` ts
19
- export type mixed = unknown
20
-
21
- class Type <A , O = A , I = mixed > {
19
+ class Type <A , O , I > {
22
20
readonly _A: A
23
21
readonly _O: O
24
22
readonly _I: I
25
23
constructor (
26
24
/** a unique name for this codec */
27
25
readonly name : string ,
28
26
/** a custom type guard */
29
- readonly is : (v : mixed ) => v is A ,
27
+ readonly is : (u : unknown ) => u is A ,
30
28
/** succeeds if a value of type I can be decoded to a value of type A */
31
29
readonly validate : (input : I , context : Context ) => Either <Errors , A >,
32
30
/** converts a value of type A to a value of type O */
@@ -47,22 +45,14 @@ A codec representing `string` can be defined as
47
45
``` ts
48
46
import * as t from ' io-ts'
49
47
50
- // codec definition
51
- export class StringType extends t .Type <string > {
52
- // equivalent to Type<string, string, mixed> as per type parameter defaults
53
- readonly _tag: ' StringType' = ' StringType'
54
- constructor () {
55
- super (
56
- ' string' ,
57
- (m ): m is string => typeof m === ' string' ,
58
- (m , c ) => (this .is (m ) ? t .success (m ) : t .failure (m , c )),
59
- t .identity
60
- )
61
- }
62
- }
48
+ const isString = (u : unknown ): u is string => typeof u === ' string'
63
49
64
- // codec instance: use this when building other codecs instances
65
- export const string = new StringType ()
50
+ const string = new t .Type <string , string , unknown >(
51
+ ' string' ,
52
+ isString ,
53
+ (u , c ) => (isString (u ) ? t .success (u ) : t .failure (u , c )),
54
+ t .identity
55
+ )
66
56
```
67
57
68
58
A codec can be used to validate an object in memory (for example an API payload)
@@ -124,11 +114,14 @@ interface ContextEntry {
124
114
readonly key: string
125
115
readonly type: Decoder <any , any >
126
116
}
117
+
127
118
interface Context extends ReadonlyArray <ContextEntry > {}
119
+
128
120
interface ValidationError {
129
- readonly value: mixed
121
+ readonly value: unknown
130
122
readonly context: Context
131
123
}
124
+
132
125
interface Errors extends Array <ValidationError > {}
133
126
```
134
127
@@ -212,49 +205,47 @@ type Person = {
212
205
import * as t from ' io-ts'
213
206
```
214
207
215
- | Type | TypeScript | codec / combinator |
216
- | ------------------------- | --------------------------------------- | ----------------------------------------------------- |
217
- | null | ` null ` | ` t.null ` or ` t.nullType ` |
218
- | undefined | ` undefined ` | ` t.undefined ` |
219
- | void | ` void ` | ` t.void ` or ` t.voidType ` |
220
- | string | ` string ` | ` t.string ` |
221
- | number | ` number ` | ` t.number ` |
222
- | boolean | ` boolean ` | ` t.boolean ` |
223
- | any | ` any ` | ` t.any ` |
224
- | never | ` never ` | ` t.never ` |
225
- | object | ` object ` | ` t.object ` |
226
- | integer | ✘ | ` t.Integer ` |
227
- | array of any | ` Array<mixed> ` | ` t.Array ` |
228
- | array of type | ` Array<A> ` | ` t.array(A) ` |
229
- | dictionary of any | ` { [key: string]: mixed } ` | ` t.Dictionary ` |
230
- | dictionary of type | ` { [K in A]: B } ` | ` t.dictionary(A, B) ` |
231
- | function | ` Function ` | ` t.Function ` |
232
- | literal | ` 's' ` | ` t.literal('s') ` |
233
- | partial | ` Partial<{ name: string }> ` | ` t.partial({ name: t.string }) ` |
234
- | readonly | ` Readonly<T> ` | ` t.readonly(T) ` |
235
- | readonly array | ` ReadonlyArray<number> ` | ` t.readonlyArray(t.number) ` |
236
- | type alias | ` type A = { name: string } ` | ` t.type({ name: t.string }) ` |
237
- | tuple | ` [ A, B ] ` | ` t.tuple([ A, B ]) ` |
238
- | union | ` A \| B ` | ` t.union([ A, B ]) ` or ` t.taggedUnion(tag, [ A, B ]) ` |
239
- | intersection | ` A & B ` | ` t.intersection([ A, B ]) ` |
240
- | keyof | ` keyof M ` | ` t.keyof(M) ` |
241
- | recursive types | see [ Recursive types] ( #recursive-types ) | ` t.recursion(name, definition) ` |
242
- | refinement | ✘ | ` t.refinement(A, predicate) ` |
243
- | exact types | ✘ | ` t.exact(type) ` |
244
- | strict types (deprecated) | ✘ | ` t.strict({ name: t.string }) ` |
208
+ | Type | TypeScript | codec / combinator |
209
+ | ----------------- | --------------------------------------- | ----------------------------------------------------- |
210
+ | null | ` null ` | ` t.null ` or ` t.nullType ` |
211
+ | undefined | ` undefined ` | ` t.undefined ` |
212
+ | void | ` void ` | ` t.void ` or ` t.voidType ` |
213
+ | string | ` string ` | ` t.string ` |
214
+ | number | ` number ` | ` t.number ` |
215
+ | boolean | ` boolean ` | ` t.boolean ` |
216
+ | unknown | ` unknown ` | t.unknown |
217
+ | never | ` never ` | ` t.never ` |
218
+ | object | ` object ` | ` t.object ` |
219
+ | integer | ✘ | ` t.Integer ` |
220
+ | array of unknown | ` Array<unknown> ` | ` t.UnknownArray ` |
221
+ | array of type | ` Array<A> ` | ` t.array(A) ` |
222
+ | record of unknown | ` Record<string, unknown> ` | ` t.UnknownRecord ` |
223
+ | record of type | ` Record<K, A> ` | ` t.record(K, A) ` |
224
+ | function | ` Function ` | ` t.Function ` |
225
+ | literal | ` 's' ` | ` t.literal('s') ` |
226
+ | partial | ` Partial<{ name: string }> ` | ` t.partial({ name: t.string }) ` |
227
+ | readonly | ` Readonly<T> ` | ` t.readonly(T) ` |
228
+ | readonly array | ` ReadonlyArray<number> ` | ` t.readonlyArray(t.number) ` |
229
+ | type alias | ` type A = { name: string } ` | ` t.type({ name: t.string }) ` |
230
+ | tuple | ` [ A, B ] ` | ` t.tuple([ A, B ]) ` |
231
+ | union | ` A \| B ` | ` t.union([ A, B ]) ` or ` t.taggedUnion(tag, [ A, B ]) ` |
232
+ | intersection | ` A & B ` | ` t.intersection([ A, B ]) ` |
233
+ | keyof | ` keyof M ` | ` t.keyof(M) ` |
234
+ | recursive types | see [ Recursive types] ( #recursive-types ) | ` t.recursion(name, definition) ` |
235
+ | refinement | ✘ | ` t.refinement(A, predicate) ` |
236
+ | exact types | ✘ | ` t.exact(type) ` |
245
237
246
238
# Recursive types
247
239
248
240
Recursive types can't be inferred by TypeScript so you must provide the static type as a hint
249
241
250
242
``` ts
251
- // helper type
252
- interface ICategory {
243
+ interface Category {
253
244
name: string
254
- categories: Array <ICategory >
245
+ categories: Array <Category >
255
246
}
256
247
257
- const Category = t .recursion < ICategory > (' Category' , Category =>
248
+ const Category: t . RecursiveType < t . Type < Category >> = t .recursion (' Category' , () =>
258
249
t .type ({
259
250
name: t .string ,
260
251
categories: t .array (Category )
@@ -265,24 +256,24 @@ const Category = t.recursion<ICategory>('Category', Category =>
265
256
## Mutually recursive types
266
257
267
258
``` ts
268
- interface IFoo {
259
+ interface Foo {
269
260
type: ' Foo'
270
- b: IBar | undefined
261
+ b: Bar | undefined
271
262
}
272
263
273
- interface IBar {
264
+ interface Bar {
274
265
type: ' Bar'
275
- a: IFoo | undefined
266
+ a: Foo | undefined
276
267
}
277
268
278
- const Foo: t .RecursiveType <t .Type <IFoo >, IFoo > = t .recursion < IFoo > (' Foo' , _ =>
269
+ const Foo: t .RecursiveType <t .Type <Foo > > = t .recursion (' Foo' , () =>
279
270
t .interface ({
280
271
type: t .literal (' Foo' ),
281
272
b: t .union ([Bar , t .undefined ])
282
273
})
283
274
)
284
275
285
- const Bar: t .RecursiveType <t .Type <IBar >, IBar > = t .recursion < IBar > (' Bar' , _ =>
276
+ const Bar: t .RecursiveType <t .Type <Bar > > = t .recursion (' Bar' , () =>
286
277
t .interface ({
287
278
type: t .literal (' Bar' ),
288
279
a: t .union ([Foo , t .undefined ])
@@ -450,6 +441,8 @@ Would be:
450
441
451
442
``` ts
452
443
import * as t from ' io-ts'
444
+
445
+ // t.Mixed = t.Type<any, any, unknown>
453
446
const ResponseBody = <RT extends t .Mixed >(type : RT ) =>
454
447
t .interface ({
455
448
result: type ,
0 commit comments