|
2 | 2 |
|
3 | 3 | Functional assertion combinators.
|
4 | 4 |
|
| 5 | +Name | Type | Runtime |
| 6 | +--------------------------|-------------------------------------------------|------------------------------ |
| 7 | +Primitive | `string` | `$.string` |
| 8 | +Primitive | `number` | `$.number` |
| 9 | +Primitive | `boolean` | `$.boolean` |
| 10 | +Primitive | `undefined` | `$.undefined` |
| 11 | +Primitive | `null` | `$.null` |
| 12 | +Undefined or null | `undefined \| null` | `$.nil` |
| 13 | +Undefined or A | `undefined \| A` | `$.undefinedOr(a)` |
| 14 | +Nullable A | `null \| A` | `$.nullOr(a)` |
| 15 | +Nillable A | `undefined \| null \| A` | `$.nilOr(a)` |
| 16 | +Unknown | `unknown` | `$.unknown` |
| 17 | +Array | `A[]` | `$.array(a)` |
| 18 | +Object | `{ a: A, b: B }` | `$.object({ a, b })` |
| 19 | +Exact object | `{ a: A, b: B }` | `$.exact({ a, b })` |
| 20 | +Record | `Record<K, V>` | `$.indexer(k, v)` |
| 21 | +Keyed object | `Record<string, undefined \| V>` | `$.keyed(v)` |
| 22 | +Intersection | `A & B` | `$.and(a, b)` |
| 23 | +Union | `A \| B` | `$.or(a, b)` |
| 24 | +Date string `YYYY-MM-DD` | `string`<sup>[weaker](#not-precise-types)</sup> | `$.dateString` |
| 25 | +Defined | `Exclude<A, undefined>` | `$.defined(a)` |
| 26 | +Literal | `"foo"`, `42` | `$.eq('foo')`, `$.eq(42)` |
| 27 | +Tuple | `[number, string]` | `$.tuple($.number, $.string)` |
| 28 | +Finite number | `number`<sup>[weaker](#not-precise-types)</sup> | `$.finite` |
| 29 | +Positive number | `number`<sup>[weaker](#not-precise-types)</sup> | `$.positive` |
| 30 | +Safe integer | `number`<sup>[weaker](#not-precise-types)</sup> | `$.safeInteger` |
| 31 | +Greater than | `number`<sup>[weaker](#not-precise-types)</sup> | `$.gt(42)` |
| 32 | +Greater than | `number`<sup>[weaker](#not-precise-types)</sup> | `$.gt(42)` |
| 33 | +Greater or equal than | `number`<sup>[weaker](#not-precise-types)</sup> | `$.gte(42)` |
| 34 | +Non blank string | `string`<sup>[weaker](#not-precise-types)</sup> | `$.nonBlankString` |
| 35 | +Regexp | `string`<sup>[weaker](#not-precise-types)</sup> | `$.regexp(/^[a-z]+$/i)` |
| 36 | +Strftime formatted string | `string`<sup>[weaker](#not-precise-types)</sup> | `$.strftime('%Y-%m-%d')` |
| 37 | + |
| 38 | +## Utility functions |
| 39 | + |
| 40 | +* identity |
| 41 | +* if |
| 42 | +* implies |
| 43 | +* in |
| 44 | +* indexer |
| 45 | +* predicate |
| 46 | +* rethrow |
| 47 | + |
| 48 | +## Examples |
| 49 | + |
| 50 | +```ts |
| 51 | +ws.on('message', _ => { |
| 52 | + |
| 53 | + const { method, agree } = $.object({ |
| 54 | + method: $.string, |
| 55 | + agree: $.boolean |
| 56 | + })(JSON.parse(_)) |
| 57 | + |
| 58 | + // Types are correct: |
| 59 | + // method: string |
| 60 | + // agree: boolean |
| 61 | + |
| 62 | +}) |
| 63 | +``` |
| 64 | + |
| 65 | +## Not precise types |
| 66 | + |
| 67 | +In some cases runtime type assertions provide stronger guarantees than static types. |
| 68 | + |
| 69 | +For example `$.finite` asserts that the value is not only a `number` but also that is not `NaN`, `Infinity` or `-Infinity`. |
| 70 | + |
| 71 | +[Opaque types](#opaque-types) section provides solution for some cases. |
| 72 | + |
| 73 | +## Opaque types |
| 74 | + |
| 75 | +Unlike Flow, TypeScript doesn't directly support opaque types. |
| 76 | + |
| 77 | +However, they can be emulated by intersecting with object containing unique property type. |
| 78 | + |
| 79 | +```ts |
| 80 | +type Finite = number & { readonly _tag: 'Finite' } |
| 81 | +``` |
| 82 | +
|
| 83 | +Opaque types allow to design code in such a way that value of the type can be created in one place – as result of runtime type assertion – only. The only possible way of creating values of this type is to create valid values. Those assertions have to happen at construction and I/O boundaries only. Once value is validated, it enters static type system. It doesn't have to be re-validated anywhere else in the code. Usage of the value is safe, guaranteed to conform to this assertion. |
| 84 | +
|
| 85 | +Good examples of opaque type candidates are `NonEmptyArray<T>`, `Positive`, `Email`. |
| 86 | +`ValidatedEmail` – ie. an email that passed some async validation can be used to annotate function parameter for functions that should be used only for validated emails – without the need for re-validating email in each function's body. |
| 87 | +
|
5 | 88 | ## License
|
6 | 89 |
|
7 | 90 | MIT License
|
|
0 commit comments