Skip to content

Commit dedb64e

Browse files
phireskygcanti
authored andcommitted
reorder documentation to make it more beginner-friendly
1 parent 9103e88 commit dedb64e

File tree

2 files changed

+88
-53
lines changed

2 files changed

+88
-53
lines changed

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ npm i io-ts fp-ts
2323

2424
# Usage
2525

26+
2627
## Stable features
2728

28-
- [`index.ts` module](index.md)
29+
- [Documentation of the main stable features (`index.ts` module)](index.md)
2930

3031
## Experimental modules (version `2.2+`)
3132

index.md

+86-52
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,91 @@
2121

2222
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
2323

24+
# Basic usage
25+
26+
```ts
27+
import * as t from 'io-ts'
28+
29+
const User = t.type({
30+
userId: t.number,
31+
name: t.string
32+
})
33+
```
34+
35+
This is equivalent to defining something like:
36+
37+
```ts
38+
type User = {
39+
userId: number
40+
name: string
41+
}
42+
```
43+
44+
The advantage of using `io-ts` to define the runtime type is that we can validate the type at runtime, and we can also extract the corresponding static type, so we don’t have to define it twice.
45+
46+
You can use this runtime type to validate or decode untrusted data:
47+
48+
```ts
49+
import * as t from "io-ts";
50+
import { PathReporter } from "io-ts/PathReporter";
51+
import { isLeft } from "fp-ts/Either";
52+
53+
const User = t.type({
54+
userId: t.number,
55+
name: t.string,
56+
});
57+
58+
const data: unknown = { userId: 123, name: "foo" }; // data that looks like User but from an unknown source
59+
60+
const decoded = User.decode(data); // Either<Errors, User>
61+
if (isLeft(decoded)) {
62+
throw Error(
63+
`Could not validate data: ${PathReporter.report(decoded).join("\n")}`
64+
);
65+
// e.g.: Could not validate data: Invalid value "foo" supplied to : { userId: number, name: string }/userId: number
66+
}
67+
68+
type UserT = t.TypeOf<typeof User>; // compile-time type
69+
const decodedUser: UserT = decoded.right; // now safely the correct type
70+
71+
console.log("decoded user id:", decodedUser.userId);
72+
```
73+
74+
io-ts also supports more complex encoding/decoding (serialization/deserialization) scenarios where the output type of `decode()` is not necessarily the same as the input type. This allows you to, for example, encode a `Date` object as a string and decode it back into a `Date` object (see [Custom types](#custom-types)).
75+
76+
# Implemented types / combinators
77+
78+
| Type | TypeScript | codec / combinator |
79+
| --------------------------- | --------------------------- | -------------------------------------------------------------------- |
80+
| null | `null` | `t.null` or `t.nullType` |
81+
| undefined | `undefined` | `t.undefined` |
82+
| void | `void` | `t.void` or `t.voidType` |
83+
| string | `string` | `t.string` |
84+
| number | `number` | `t.number` |
85+
| boolean | `boolean` | `t.boolean` |
86+
| unknown | `unknown` | `t.unknown` |
87+
| array of unknown | `Array<unknown>` | `t.UnknownArray` |
88+
| array of type | `Array<A>` | `t.array(A)` |
89+
| record of unknown | `Record<string, unknown>` | `t.UnknownRecord` |
90+
| record of type | `Record<K, A>` | `t.record(K, A)` |
91+
| function | `Function` | `t.Function` |
92+
| literal | `'s'` | `t.literal('s')` |
93+
| partial | `Partial<{ name: string }>` | `t.partial({ name: t.string })` |
94+
| readonly | `Readonly<A>` | `t.readonly(A)` |
95+
| readonly array | `ReadonlyArray<A>` | `t.readonlyArray(A)` |
96+
| type alias | `type T = { name: A }` | `t.type({ name: A })` |
97+
| tuple | `[ A, B ]` | `t.tuple([ A, B ])` |
98+
| union | `A \| B` | `t.union([ A, B ])` |
99+
| intersection | `A & B` | `t.intersection([ A, B ])` |
100+
| keyof | `keyof M` | `t.keyof(M)` (**only supports string keys**) |
101+
| recursive types | | `t.recursion(name, definition)` |
102+
| branded types / refinements || `t.brand(A, predicate, brand)` |
103+
| integer || `t.Int` (built-in branded codec) |
104+
| exact types || `t.exact(type)` (no unknown extra properties) |
105+
| strict || `t.strict({ name: A })` (an alias of `t.exact(t.type({ name: A })))` |
106+
107+
108+
24109
# The idea
25110

26111
A value of type `Type<A, O, I>` (called "codec") is the runtime representation of the static type `A`.
@@ -116,27 +201,7 @@ pipe(t.string.decode(null), fold(onLeft, onRight))
116201
// => "1 error(s) found"
117202
```
118203

119-
We can combine these codecs through [combinators](#implemented-types--combinators) to build composite types which represent entities like domain models, request payloads etc. in our applications:
120-
121-
```ts
122-
import * as t from 'io-ts'
123-
124-
const User = t.type({
125-
userId: t.number,
126-
name: t.string
127-
})
128-
```
129-
130-
So this is equivalent to defining something like:
131-
132-
```ts
133-
type User = {
134-
userId: number
135-
name: string
136-
}
137-
```
138-
139-
The advantage of using `io-ts` to define the runtime type is that we can validate the type at runtime, and we can also extract the corresponding static type, so we don’t have to define it twice.
204+
We can combine these codecs through [combinators](#implemented-types--combinators) to build composite types which represent entities like domain models, request payloads etc. in our applications.
140205

141206
# TypeScript integration
142207

@@ -266,37 +331,6 @@ console.log(PathReporter.report(NumberFromString.decode('a')))
266331
267332
You can also use the [`withMessage`](https://gcanti.github.io/io-ts-types/modules/withMessage.ts.html) helper from [io-ts-types](https://github.com/gcanti/io-ts-types)
268333
269-
# Implemented types / combinators
270-
271-
| Type | TypeScript | codec / combinator |
272-
| --------------------------- | --------------------------- | -------------------------------------------------------------------- |
273-
| null | `null` | `t.null` or `t.nullType` |
274-
| undefined | `undefined` | `t.undefined` |
275-
| void | `void` | `t.void` or `t.voidType` |
276-
| string | `string` | `t.string` |
277-
| number | `number` | `t.number` |
278-
| boolean | `boolean` | `t.boolean` |
279-
| unknown | `unknown` | `t.unknown` |
280-
| array of unknown | `Array<unknown>` | `t.UnknownArray` |
281-
| array of type | `Array<A>` | `t.array(A)` |
282-
| record of unknown | `Record<string, unknown>` | `t.UnknownRecord` |
283-
| record of type | `Record<K, A>` | `t.record(K, A)` |
284-
| function | `Function` | `t.Function` |
285-
| literal | `'s'` | `t.literal('s')` |
286-
| partial | `Partial<{ name: string }>` | `t.partial({ name: t.string })` |
287-
| readonly | `Readonly<A>` | `t.readonly(A)` |
288-
| readonly array | `ReadonlyArray<A>` | `t.readonlyArray(A)` |
289-
| type alias | `type T = { name: A }` | `t.type({ name: A })` |
290-
| tuple | `[ A, B ]` | `t.tuple([ A, B ])` |
291-
| union | `A \| B` | `t.union([ A, B ])` |
292-
| intersection | `A & B` | `t.intersection([ A, B ])` |
293-
| keyof | `keyof M` | `t.keyof(M)` (**only supports string keys**) |
294-
| recursive types | | `t.recursion(name, definition)` |
295-
| branded types / refinements | ✘ | `t.brand(A, predicate, brand)` |
296-
| integer | ✘ | `t.Int` (built-in branded codec) |
297-
| exact types | ✘ | `t.exact(type)` |
298-
| strict | ✘ | `t.strict({ name: A })` (an alias of `t.exact(t.type({ name: A })))` |
299-
300334
# Recursive types
301335
302336
Recursive types can't be inferred by TypeScript so you must provide the static type as a hint

0 commit comments

Comments
 (0)