Comparing version 2.0.0 to 2.0.1
@@ -17,2 +17,7 @@ # Changelog | ||
# 2.0.1 | ||
- **Bug Fix** | ||
- fix `getTags` algorithm for mutually recursive codecs, closes #354 (@gcanti) | ||
# 2.0.0 | ||
@@ -19,0 +24,0 @@ |
@@ -113,6 +113,18 @@ import { Either } from 'fp-ts/lib/Either'; | ||
encode: Encode<A, O>); | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
pipe<B, IB, A extends IB, OB extends A>(this: Type<A, O, I>, ab: Type<B, OB, IB>, name?: string): Type<B, O, I>; | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
asDecoder(): Decoder<I, A>; | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
asEncoder(): Encoder<A, O>; | ||
/** a version of `validate` with a default context */ | ||
/** | ||
* a version of `validate` with a default context | ||
* @since 1.0.0 | ||
*/ | ||
decode(i: I): Validation<A>; | ||
@@ -161,3 +173,2 @@ } | ||
/** | ||
* @alias `null` | ||
* @since 1.0.0 | ||
@@ -192,3 +203,2 @@ */ | ||
/** | ||
* @alias `void` | ||
* @since 1.2.0 | ||
@@ -471,3 +481,2 @@ */ | ||
/** | ||
* @alias `interface` | ||
* @since 1.0.0 | ||
@@ -739,16 +748,30 @@ */ | ||
export declare const exact: <C extends HasProps>(codec: C, name?: string) => ExactC<C>; | ||
export { nullType as null }; | ||
export { undefinedType as undefined }; | ||
export { | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
nullType as null }; | ||
export { | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
undefinedType as undefined }; | ||
export { | ||
/** | ||
* Use `UnknownArray` instead | ||
* @deprecated | ||
* @since 1.0.0 | ||
*/ | ||
export { UnknownArray as Array }; | ||
UnknownArray as Array }; | ||
export { | ||
/** | ||
* Use `type` instead | ||
* @deprecated | ||
* @since 1.0.0 | ||
*/ | ||
export { type as interface }; | ||
export { voidType as void }; | ||
type as interface }; | ||
export { | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
voidType as void }; | ||
/** | ||
* Use `unknown` instead | ||
@@ -755,0 +778,0 @@ * @since 1.0.0 |
@@ -47,2 +47,5 @@ var __extends = (this && this.__extends) || (function () { | ||
} | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
Type.prototype.pipe = function (ab, name) { | ||
@@ -53,9 +56,18 @@ var _this = this; | ||
}; | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
Type.prototype.asDecoder = function () { | ||
return this; | ||
}; | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
Type.prototype.asEncoder = function () { | ||
return this; | ||
}; | ||
/** a version of `validate` with a default context */ | ||
/** | ||
* a version of `validate` with a default context | ||
* @since 1.0.0 | ||
*/ | ||
Type.prototype.decode = function (i) { | ||
@@ -136,3 +148,2 @@ return this.validate(i, [{ key: '', type: this, actual: i }]); | ||
/** | ||
* @alias `null` | ||
* @since 1.0.0 | ||
@@ -169,3 +180,2 @@ */ | ||
/** | ||
* @alias `void` | ||
* @since 1.2.0 | ||
@@ -483,3 +493,2 @@ */ | ||
/** | ||
* @alias `interface` | ||
* @since 1.0.0 | ||
@@ -1056,17 +1065,31 @@ */ | ||
}; | ||
export { nullType as null }; | ||
export { undefinedType as undefined }; | ||
export { | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
nullType as null }; | ||
export { | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
undefinedType as undefined }; | ||
export { | ||
/** | ||
* Use `UnknownArray` instead | ||
* @deprecated | ||
* @since 1.0.0 | ||
*/ | ||
export { UnknownArray as Array }; | ||
UnknownArray as Array }; | ||
export { | ||
/** | ||
* Use `type` instead | ||
* @deprecated | ||
* @since 1.0.0 | ||
*/ | ||
export { type as interface }; | ||
export { voidType as void }; | ||
type as interface }; | ||
export { | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
voidType as void }; | ||
/** | ||
* @since 1.0.0 | ||
* @deprecated | ||
@@ -1295,3 +1318,3 @@ */ | ||
} | ||
var lazyCodec = null; | ||
var lazyCodecs = []; | ||
/** | ||
@@ -1301,3 +1324,3 @@ * @internal | ||
export function getTags(codec) { | ||
if (codec === lazyCodec) { | ||
if (lazyCodecs.indexOf(codec) !== -1) { | ||
return emptyTags; | ||
@@ -1329,5 +1352,5 @@ } | ||
else if (isRecursiveC(codec)) { | ||
lazyCodec = codec; | ||
lazyCodecs.push(codec); | ||
var tags = getTags(codec.type); | ||
lazyCodec = null; | ||
lazyCodecs.pop(); | ||
return tags; | ||
@@ -1334,0 +1357,0 @@ } |
@@ -113,6 +113,18 @@ import { Either } from 'fp-ts/lib/Either'; | ||
encode: Encode<A, O>); | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
pipe<B, IB, A extends IB, OB extends A>(this: Type<A, O, I>, ab: Type<B, OB, IB>, name?: string): Type<B, O, I>; | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
asDecoder(): Decoder<I, A>; | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
asEncoder(): Encoder<A, O>; | ||
/** a version of `validate` with a default context */ | ||
/** | ||
* a version of `validate` with a default context | ||
* @since 1.0.0 | ||
*/ | ||
decode(i: I): Validation<A>; | ||
@@ -161,3 +173,2 @@ } | ||
/** | ||
* @alias `null` | ||
* @since 1.0.0 | ||
@@ -192,3 +203,2 @@ */ | ||
/** | ||
* @alias `void` | ||
* @since 1.2.0 | ||
@@ -471,3 +481,2 @@ */ | ||
/** | ||
* @alias `interface` | ||
* @since 1.0.0 | ||
@@ -739,16 +748,30 @@ */ | ||
export declare const exact: <C extends HasProps>(codec: C, name?: string) => ExactC<C>; | ||
export { nullType as null }; | ||
export { undefinedType as undefined }; | ||
export { | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
nullType as null }; | ||
export { | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
undefinedType as undefined }; | ||
export { | ||
/** | ||
* Use `UnknownArray` instead | ||
* @deprecated | ||
* @since 1.0.0 | ||
*/ | ||
export { UnknownArray as Array }; | ||
UnknownArray as Array }; | ||
export { | ||
/** | ||
* Use `type` instead | ||
* @deprecated | ||
* @since 1.0.0 | ||
*/ | ||
export { type as interface }; | ||
export { voidType as void }; | ||
type as interface }; | ||
export { | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
voidType as void }; | ||
/** | ||
* Use `unknown` instead | ||
@@ -755,0 +778,0 @@ * @since 1.0.0 |
@@ -49,2 +49,5 @@ "use strict"; | ||
} | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
Type.prototype.pipe = function (ab, name) { | ||
@@ -55,9 +58,18 @@ var _this = this; | ||
}; | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
Type.prototype.asDecoder = function () { | ||
return this; | ||
}; | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
Type.prototype.asEncoder = function () { | ||
return this; | ||
}; | ||
/** a version of `validate` with a default context */ | ||
/** | ||
* a version of `validate` with a default context | ||
* @since 1.0.0 | ||
*/ | ||
Type.prototype.decode = function (i) { | ||
@@ -138,3 +150,2 @@ return this.validate(i, [{ key: '', type: this, actual: i }]); | ||
/** | ||
* @alias `null` | ||
* @since 1.0.0 | ||
@@ -173,3 +184,2 @@ */ | ||
/** | ||
* @alias `void` | ||
* @since 1.2.0 | ||
@@ -489,3 +499,2 @@ */ | ||
/** | ||
* @alias `interface` | ||
* @since 1.0.0 | ||
@@ -1293,3 +1302,3 @@ */ | ||
} | ||
var lazyCodec = null; | ||
var lazyCodecs = []; | ||
/** | ||
@@ -1299,3 +1308,3 @@ * @internal | ||
function getTags(codec) { | ||
if (codec === lazyCodec) { | ||
if (lazyCodecs.indexOf(codec) !== -1) { | ||
return exports.emptyTags; | ||
@@ -1327,5 +1336,5 @@ } | ||
else if (isRecursiveC(codec)) { | ||
lazyCodec = codec; | ||
lazyCodecs.push(codec); | ||
var tags = getTags(codec.type); | ||
lazyCodec = null; | ||
lazyCodecs.pop(); | ||
return tags; | ||
@@ -1332,0 +1341,0 @@ } |
{ | ||
"name": "io-ts", | ||
"version": "2.0.0", | ||
"version": "2.0.1", | ||
"description": "TypeScript compatible runtime type system for IO validation", | ||
@@ -47,3 +47,3 @@ "files": [ | ||
"benchmark": "2.1.4", | ||
"docs-ts": "^0.1.0", | ||
"docs-ts": "^0.2.1", | ||
"doctoc": "^1.4.0", | ||
@@ -50,0 +50,0 @@ "dtslint": "github:gcanti/dtslint", |
179
README.md
@@ -13,7 +13,6 @@ [![build status](https://img.shields.io/travis/gcanti/io-ts/master.svg?style=flat-square)](https://travis-ci.org/gcanti/io-ts) | ||
- [The idea](#the-idea) | ||
- [TypeScript integration](#typescript-integration) | ||
- [TypeScript compatibility](#typescript-compatibility) | ||
- [Error reporters](#error-reporters) | ||
- [Custom error messages](#custom-error-messages) | ||
- [Community](#community) | ||
- [TypeScript integration](#typescript-integration) | ||
- [Implemented types / combinators](#implemented-types--combinators) | ||
@@ -28,4 +27,4 @@ - [Recursive types](#recursive-types) | ||
- [Piping](#piping) | ||
- [Community](#community) | ||
- [Tips and Tricks](#tips-and-tricks) | ||
- [Is there a way to turn the checks off in production code?](#is-there-a-way-to-turn-the-checks-off-in-production-code) | ||
- [Union of string literals](#union-of-string-literals) | ||
@@ -40,32 +39,33 @@ | ||
```sh | ||
npm i io-ts | ||
npm i io-ts fp-ts | ||
``` | ||
Note: [`fp-ts`](https://github.com/gcanti/fp-ts) is a peer dependency for `io-ts` | ||
# The idea | ||
Blog post: ["Typescript and validations at runtime boundaries"](https://lorefnon.tech/2018/03/25/typescript-and-validations-at-runtime-boundaries/) by [@lorefnon](https://github.com/lorefnon) | ||
A value of type `Type<A, O, I>` (called "codec") is the runtime representation of the static type `A`. | ||
Also a codec can | ||
A codec can: | ||
- decode inputs of type `I` (through `decode`) | ||
- encode outputs of type `O` (through `encode`) | ||
- be used as a custom type guard (through `is`) | ||
- be used as a custom [type guard](https://basarat.gitbooks.io/typescript/content/docs/types/typeGuard.html) (through `is`) | ||
```ts | ||
class Type<A, O, I> { | ||
readonly _A: A | ||
readonly _O: O | ||
readonly _I: I | ||
constructor( | ||
/** a unique name for this codec */ | ||
readonly name: string, | ||
/** a custom type guard */ | ||
readonly is: (u: unknown) => u is A, | ||
/** succeeds if a value of type I can be decoded to a value of type A */ | ||
readonly validate: (input: I, context: Context) => Either<Errors, A>, | ||
/** converts a value of type A to a value of type O */ | ||
readonly encode: (a: A) => O | ||
) {} | ||
/** a version of `validate` with a default context */ | ||
@@ -76,8 +76,23 @@ decode(i: I): Either<Errors, A> | ||
Note. The `Either` type is defined in [fp-ts](https://github.com/gcanti/fp-ts), a library containing implementations of | ||
common algebraic types in TypeScript. | ||
The [`Either`](https://gcanti.github.io/fp-ts/modules/Either.ts.html) type returned by `decode` is defined in [fp-ts](https://github.com/gcanti/fp-ts), a library containing implementations of common algebraic types in TypeScript. | ||
The `Either` type represents a value of one of two possible types (a disjoint union). An instance of `Either` is either an instance of `Left` or `Right`: | ||
```ts | ||
type Either<E, A> = | ||
| { | ||
readonly _tag: 'Left' | ||
readonly left: E | ||
} | ||
| { | ||
readonly _tag: 'Right' | ||
readonly right: A | ||
} | ||
``` | ||
Convention dictates that `Left` is used for **failure** and `Right` is used for **success**. | ||
**Example** | ||
A codec representing `string` can be defined as | ||
A codec representing `string` can be defined as: | ||
@@ -87,8 +102,8 @@ ```ts | ||
const isString = (u: unknown): u is string => typeof u === 'string' | ||
const string = new t.Type<string, string, unknown>( | ||
'string', | ||
isString, | ||
(u, c) => (isString(u) ? t.success(u) : t.failure(u, c)), | ||
(input: unknown): input is string => typeof input === 'string', | ||
// `t.success` and `t.failure` are helpers used to build `Either` instances | ||
(input, context) => (typeof input === 'string' ? t.success(input) : t.failure(input, context)), | ||
// `A` and `O` are the same, so `encode` is just the identity function | ||
t.identity | ||
@@ -98,7 +113,42 @@ ) | ||
A codec can be used to validate an object in memory (for example an API payload) | ||
and we can use it as follows: | ||
```ts | ||
import { isRight } from 'fp-ts/lib/Either' | ||
isRight(string.decode('a string')) // true | ||
isRight(string.decode(null)) // false | ||
``` | ||
More generally the result of calling `decode` can be handled using [`fold`](https://gcanti.github.io/fp-ts/modules/Either.ts.html#fold-function) along with `pipe` (which is similar to the pipeline operator) | ||
```ts | ||
import * as t from 'io-ts' | ||
import { pipe } from 'fp-ts/lib/pipeable' | ||
import { fold } from 'fp-ts/lib/Either' | ||
// failure handler | ||
const onLeft = (errors: t.Errors): string => `${errors.length} error(s) found` | ||
// success handler | ||
const onRight = (s: string) => `No errors: ${s}` | ||
pipe( | ||
t.string.decode('a string'), | ||
fold(onLeft, onRight) | ||
) | ||
// => "No errors: a string" | ||
pipe( | ||
t.string.decode(null), | ||
fold(onLeft, onRight) | ||
) | ||
// => "1 error(s) found" | ||
``` | ||
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: | ||
```ts | ||
import * as t from 'io-ts' | ||
const User = t.type({ | ||
@@ -108,10 +158,40 @@ userId: t.number, | ||
}) | ||
``` | ||
// validation succeeded | ||
User.decode(JSON.parse('{"userId":1,"name":"Giulio"}')) // => Right({ userId: 1, name: "Giulio" }) | ||
So this is equivalent to defining something like: | ||
// validation failed | ||
User.decode(JSON.parse('{"name":"Giulio"}')) // => Left([...]) | ||
```ts | ||
type User = { | ||
userId: number | ||
name: string | ||
} | ||
``` | ||
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. | ||
# TypeScript integration | ||
Codecs can be inspected: | ||
![instrospection](images/introspection.png) | ||
This library uses TypeScript extensively. Its API is defined in a way which automatically infers types for produced | ||
values | ||
![inference](images/inference.png) | ||
Note that the type annotation isn't needed, TypeScript infers the type automatically based on a schema (and comments are preserved). | ||
Static types can be extracted from codecs using the `TypeOf` operator: | ||
```ts | ||
type User = t.TypeOf<typeof User> | ||
// same as | ||
type User = { | ||
userId: number | ||
name: string | ||
} | ||
``` | ||
# TypeScript compatibility | ||
@@ -217,39 +297,2 @@ | ||
# Community | ||
- `io-ts@1.x` | ||
- [io-ts-types](https://github.com/gcanti/io-ts-types) - A collection of codecs and combinators for use with | ||
io-ts | ||
- [io-ts-reporters](https://github.com/OliverJAsh/io-ts-reporters) - Error reporters for io-ts | ||
- [geojson-iots](https://github.com/pierremarc/geojson-iots) - codecs for GeoJSON as defined in rfc7946 made with | ||
io-ts | ||
- [graphql-to-io-ts](https://github.com/micimize/graphql-to-io-ts) - Generate typescript and cooresponding io-ts types from a graphql | ||
schema | ||
- [io-ts-promise](https://github.com/aeirola/io-ts-promise) - Convenience library for using io-ts with promise-based APIs | ||
# TypeScript integration | ||
codecs can be inspected | ||
![instrospection](images/introspection.png) | ||
This library uses TypeScript extensively. Its API is defined in a way which automatically infers types for produced | ||
values | ||
![inference](images/inference.png) | ||
Note that the type annotation isn't needed, TypeScript infers the type automatically based on a schema (and comments are preserved). | ||
Static types can be extracted from codecs using the `TypeOf` operator | ||
```ts | ||
type User = t.TypeOf<typeof User> | ||
// same as | ||
type User = { | ||
userId: number | ||
name: string | ||
} | ||
``` | ||
# Implemented types / combinators | ||
@@ -266,4 +309,2 @@ | ||
| unknown | `unknown` | `t.unknown` | | ||
| never | `never` | `t.never` | | ||
| object | `object` | `t.object` | | ||
| array of unknown | `Array<unknown>` | `t.UnknownArray` | | ||
@@ -283,3 +324,3 @@ | array of type | `Array<A>` | `t.array(A)` | | ||
| keyof | `keyof M` | `t.keyof(M)` (**only supports string keys**) | | ||
| recursive types | ✘ | `t.recursion(name, definition)` | | ||
| recursive types | | `t.recursion(name, definition)` | | ||
| branded types / refinements | ✘ | `t.brand(A, predicate, brand)` | | ||
@@ -515,2 +556,14 @@ | integer | ✘ | `t.Int` (built-in branded codec) | | ||
# Community | ||
- `io-ts@1.x` | ||
- [io-ts-types](https://github.com/gcanti/io-ts-types) - A collection of codecs and combinators for use with | ||
io-ts | ||
- [io-ts-reporters](https://github.com/OliverJAsh/io-ts-reporters) - Error reporters for io-ts | ||
- [geojson-iots](https://github.com/pierremarc/geojson-iots) - codecs for GeoJSON as defined in rfc7946 made with | ||
io-ts | ||
- [graphql-to-io-ts](https://github.com/micimize/graphql-to-io-ts) - Generate typescript and cooresponding io-ts types from a graphql | ||
schema | ||
- [io-ts-promise](https://github.com/aeirola/io-ts-promise) - Convenience library for using io-ts with promise-based APIs | ||
# Tips and Tricks | ||
@@ -517,0 +570,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
181651
4911
595