io-ts-validator
Advanced tools
Comparing version 0.0.2 to 0.0.3
@@ -55,16 +55,20 @@ import * as t from 'io-ts'; | ||
} | ||
export declare type Defaults<O, I> = Settings<Array<string>, O, O, I, I>; | ||
export declare const defaults: <A, O, I>(codec: Codec<A, O, I>) => Settings<string[], O, O, I, I>; | ||
export declare const jsonDefaults: <A, O, I>(codec: Codec<A, O, I>) => { | ||
parser: { | ||
serialize: (o: O) => string; | ||
deserialize: (s: string) => I; | ||
}; | ||
mapError: ErrorMap<string[]>; | ||
Promise: PromiseConstructor; | ||
export declare type Errors = Array<string>; | ||
export declare type Jsontext = string; | ||
export declare type Preset = 'raw' | 'json' | 'strict'; | ||
export declare const raw: <A, O, I>(codec: Codec<A, O, I>) => Settings<Errors, O, O, I, I>; | ||
export declare const json: <A, O, I>(codec: Codec<A, O, I>) => Settings<Errors, O, string, I, string>; | ||
export declare const strict: <A, O, I>(codec: Codec<A, O, I>) => Settings<Errors, O, O, O, O>; | ||
declare type Presets<O, I> = { | ||
raw: Settings<Errors, O, O, I, I>; | ||
json: Settings<Errors, O, Jsontext, I, Jsontext>; | ||
strict: Settings<Errors, O, O, O, O>; | ||
}; | ||
export declare type FromDefaults<E, O, SO, I, SI> = (s: Defaults<O, I>) => Settings<E, O, SO, I, SI>; | ||
export declare type CustomOrDefault<E, O, SO, I, SI> = Defaults<O, I> | Settings<E, O, SO, I, SI>; | ||
export declare function validator<E, A, O, SO, I, SI>(codec: Codec<A, O, I>): Validator<Array<string>, A, O, O, I, I>; | ||
export declare function validator<E, A, O, SO, I, SI>(codec: Codec<A, O, I>, options: 'json'): Validator<Array<string>, A, O, string, I, string>; | ||
export declare function validator<E, A, O, SO, I, SI>(codec: Codec<A, O, I>, options: FromDefaults<E, O, SO, I, SI>): Validator<E, A, O, SO, I, SI>; | ||
declare type Customizer<E, O, SO, I, SI> = (p: Presets<O, I>) => Settings<E, O, SO, I, SI>; | ||
export declare function validator<E, A, O, SO, I, SI>(codec: Codec<A, O, I>): Validator<Errors, A, O, O, I, I>; | ||
export declare function validator<E, A, O, SO, I, SI>(codec: Codec<A, O, I>, settings: 'raw'): Validator<Errors, A, O, SO, I, SI>; | ||
export declare function validator<E, A, O, SO, I, SI>(codec: Codec<A, O, I>, settings: 'json'): Validator<Errors, A, O, Jsontext, I, Jsontext>; | ||
export declare function validator<E, A, O, SO, I, SI>(codec: Codec<A, O, I>, settings: 'strict'): Validator<Errors, A, O, O, O, O>; | ||
export declare function validator<E, A, O, SO, I, SI>(codec: Codec<A, O, I>, settings: Settings<E, O, SO, I, SI>): Validator<E, A, O, SO, I, SI>; | ||
export declare function validator<E, A, O, SO, I, SI>(codec: Codec<A, O, I>, settings: Customizer<E, O, SO, I, SI>): Validator<E, A, O, SO, I, SI>; | ||
export {}; |
"use strict"; | ||
var __assign = (this && this.__assign) || function () { | ||
__assign = Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
}; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
}) : function(o, v) { | ||
o["default"] = v; | ||
}); | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; | ||
result["default"] = mod; | ||
if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); | ||
__setModuleDefault(result, mod); | ||
return result; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.validator = exports.strict = exports.json = exports.raw = exports.Validator = void 0; | ||
var PathReporter_ = __importStar(require("io-ts/lib/PathReporter")); | ||
@@ -66,7 +68,4 @@ var Either_ = __importStar(require("fp-ts/lib/Either")); | ||
exports.Validator = Validator; | ||
var _validator = function (codec, settings) { | ||
return new Validator(codec, settings); | ||
}; | ||
var identity = function (i) { return i; }; | ||
exports.defaults = function (codec) { return ({ | ||
exports.raw = function (codec) { return ({ | ||
Promise: Promise, | ||
@@ -79,15 +78,48 @@ mapError: PathReporter_.failure, | ||
}); }; | ||
exports.jsonDefaults = function (codec) { return (__assign(__assign({}, exports.defaults(codec)), { parser: { | ||
exports.json = function (codec) { return ({ | ||
Promise: Promise, | ||
mapError: PathReporter_.failure, | ||
parser: { | ||
serialize: function (o) { return JSON.stringify(o); }, | ||
deserialize: function (s) { return JSON.parse(s); }, | ||
} })); }; | ||
function validator(codec, options) { | ||
if (typeof options === 'undefined') { | ||
return _validator(codec, exports.defaults(codec)); | ||
} | ||
if (options === 'json') { | ||
return _validator(codec, exports.jsonDefaults(codec)); | ||
}); }; | ||
exports.strict = function (codec) { return ({ | ||
Promise: Promise, | ||
mapError: PathReporter_.failure, | ||
parser: { | ||
serialize: identity, | ||
deserialize: identity, | ||
}, | ||
}); }; | ||
var presets = function (codec) { return ({ | ||
raw: exports.raw(codec), | ||
json: exports.json(codec), | ||
strict: exports.strict(codec), | ||
}); }; | ||
var select = function (p) { return function (codec) { return presets(codec)[p]; }; }; | ||
function fromSettings(settings) { | ||
return function (codec) { return new Validator(codec, settings); }; | ||
} | ||
function fromCustomizer(customizer) { | ||
return function (codec) { return pipeable_1.pipe(codec, fromSettings(customizer(presets(codec)))); }; | ||
} | ||
function fromPresetRaw(_preset) { | ||
return function (codec) { return pipeable_1.pipe(codec, fromSettings(select('raw')(codec))); }; | ||
} | ||
function fromPresetJson(_preset) { | ||
return function (codec) { return pipeable_1.pipe(codec, fromSettings(select('json')(codec))); }; | ||
} | ||
function validator(codec, settings) { | ||
if (typeof settings === 'object') { | ||
return pipeable_1.pipe(codec, fromSettings(settings)); | ||
} | ||
return options(exports.defaults(codec)); | ||
if (typeof settings === 'function') { | ||
return pipeable_1.pipe(codec, fromCustomizer(settings)); | ||
} | ||
if (settings === 'json') { | ||
return pipeable_1.pipe(codec, fromPresetJson('json')); | ||
} | ||
return pipeable_1.pipe(codec, fromPresetRaw('raw')); | ||
} | ||
exports.validator = validator; |
{ | ||
"name": "io-ts-validator", | ||
"version": "0.0.2", | ||
"version": "0.0.3", | ||
"description": "", | ||
@@ -40,3 +40,3 @@ "main": "lib/validator.js", | ||
"ts-jest": "^25.2.1", | ||
"typescript": "^3.5.1" | ||
"typescript": "3.9" | ||
}, | ||
@@ -43,0 +43,0 @@ "repository": { |
121
README.md
# io-ts-validator | ||
Io-ts-validator is a convenience wrapper that is supposed to make [io-ts](https://github.com/gcanti/io-ts) codecs easier to use. | ||
Io-ts-validator is a wrapper that provides convenience features for [io-ts](https://github.com/gcanti/io-ts) codecs. | ||
The codecs themself are described in greater detail in the [io-ts guide](https://github.com/gcanti/io-ts/blob/master/index.md). | ||
If you have existing JSON Schema definitions for your types you can use | ||
[io-ts-from-json-schema](https://www.npmjs.com/package/io-ts-from-json-schema) to convert your schema into io-ts codecs. | ||
If you have a custom schema format you can use [io-ts-codegen](https://github.com/gcanti/io-ts-codegen) to implement | ||
a converter for your custom schema format. | ||
## Defining Codecs | ||
Below we define a simple example codec `Person` that we can use to define a person `{ name: string, age: number }`. | ||
We can extract the static type from the codec and use the static type to validate our person literal. | ||
```typescript | ||
import * as t from 'io-ts'; | ||
export const Person = t.type({ | ||
name: t.string, | ||
age: t.number, | ||
}) | ||
export type Person = t.Typeof<typeof Person> | ||
const joe: Person = { | ||
name: 'Joe', | ||
age: 45, | ||
} | ||
``` | ||
## Input Decoding | ||
Sooner or later we will run into a situation where we encounter a person with an unknown type. | ||
Perhaps we received that person over the network or read the information from a loosely typed database. | ||
Below we simulate this by turning Joe into a person candidate with unknown type. | ||
We can use the various decode methods from the codec. | ||
```typescript | ||
import { validator } from 'io-ts-validator'; | ||
const candidate: unknown = joe | ||
validator(Person).decodeSync(candidate); // returns Person, throws on errors | ||
validator(Person).decodePromise(candidate); // returns Promise<Person>, rejects on errors | ||
validator(Person).decodeEither(candidate); // returns Either<Array<string>, Person> | ||
validator(Person).decodeAsync(candidate, (errors, person) => { ... }); // returns void | ||
``` | ||
## Output Encoding | ||
Some codecs define a separate input type. For example codec `NumberFromString` is useful | ||
for numeric URL parameters that need to be encoded as string when they part of the URL. | ||
The validator also provides encode methods for such cases. In general it is a good practice | ||
to encode all outputs to make sure they match the codec that would be used to validate them. | ||
```typescript | ||
import { NumberFromString } from 'io-ts-types/lib/NumberFromString'; | ||
const arg: string = validator(NumberFromString).encodeSync(123); | ||
const x: number = validator(NumberFromString).decodeSync(arg); | ||
``` | ||
## Json Integration | ||
The validator has a preset configuration that you can use to | ||
perform json serialization and deserialization on the fly | ||
while performing validation. | ||
```typescript | ||
const wireJoe = validator(Person, 'json').encodeSync(joe); | ||
const ramJoe = validator(Person, 'json').decodeSync(wireJoe); | ||
``` | ||
## Runtime Validation | ||
TypeScript static type system has some limitations which makes | ||
it difficult to validate specific properties of strings and | ||
numbers. The io-ts codecs provide runtime validation with branding. | ||
Brands are inspection stamps that can be used by the validator to indicate that the | ||
item has passed validation. In the example below we define a codec that change | ||
the age of the person and brands them as adults if they pass validation. | ||
```typescript | ||
export interface AdultBrand { | ||
readonly Adult: unique symbol | ||
} | ||
export const Adult = t.brand( | ||
Person, | ||
(p: Person): p is Adult => p.age >= 18, | ||
'Adult', | ||
); | ||
export type Adult = t.Typeof<typeof Adult> | ||
// @ts-expect-error bob *might* not yet be 18 | ||
const noBob: Adult = { | ||
name: 'Bob', | ||
age: 22, | ||
} | ||
// this is ok | ||
const adultBob: Adult = validator(Adult).decodeSync({ | ||
name: 'Bob', | ||
age: 22, | ||
}); | ||
``` | ||
## Strict Validator Inputs | ||
The validator has a strict mode that requires validator inputs to match validator outputs. | ||
Using it makes sense in some cases. For example in some cases the adult validator might | ||
benefit from restricting acceptable inputs to type `Person`. | ||
```typescript | ||
// @ts-expect-error validator input needs to be a person | ||
const neverBob: Adult = validator(Adult, 'strict').decodeSync('bar'); | ||
// this is ok but throws at runtime because `age` < 18 | ||
const underBob: Adult = validator(Adult, 'strict').decodeSync({ | ||
name: 'Bob', | ||
age: 17, | ||
}); | ||
``` |
16223
196
123