@cloudflare/util-en-garde
Advanced tools
Comparing version 4.0.2 to 6.0.0
@@ -6,10 +6,36 @@ # Change Log | ||
## [4.0.2](http://stash.cfops.it:7999/fe/stratus/compare/@cloudflare/util-en-garde@4.0.1...@cloudflare/util-en-garde@4.0.2) (2019-11-14) | ||
# [6.0.0](http://stash.cfops.it:7999/fe/stratus/compare/@cloudflare/util-en-garde@4.0.1...@cloudflare/util-en-garde@6.0.0) (2019-11-20) | ||
**Note:** Version bump only for package @cloudflare/util-en-garde | ||
### Features | ||
* **util-en-garde:** TSX-153 Make en-garde wrap io-ts ([95bb19e](http://stash.cfops.it:7999/fe/stratus/commits/95bb19e)) | ||
### BREAKING CHANGES | ||
* **util-en-garde:** Rather than en-garde simply being composable type guards, we are wrapping io-ts to | ||
provide similar functionality with a nicer API. | ||
# [5.0.0](http://stash.cfops.it:7999/fe/stratus/compare/@cloudflare/util-en-garde@4.0.1...@cloudflare/util-en-garde@5.0.0) (2019-11-19) | ||
### Features | ||
* **util-en-garde:** TSX-153 Make en-garde wrap io-ts ([95bb19e](http://stash.cfops.it:7999/fe/stratus/commits/95bb19e)) | ||
### BREAKING CHANGES | ||
* **util-en-garde:** Rather than en-garde simply being composable type guards, we are wrapping io-ts to | ||
provide similar functionality with a nicer API. | ||
## [4.0.1](http://stash.cfops.it:7999/fe/stratus/compare/@cloudflare/util-en-garde@4.0.0...@cloudflare/util-en-garde@4.0.1) (2019-11-13) | ||
@@ -16,0 +42,0 @@ |
@@ -1,112 +0,108 @@ | ||
import { primitiveGuards, higherOrderGuards } from './core'; | ||
import { TypeGuard, TypeFromGuard, TypeGuardWithOptional } from './type-helpers'; | ||
import { addOptionalToGuard, addOptionalToHigherOrderGuard } from './optional'; | ||
declare class TypeGuardError extends Error { | ||
valueThatFailed: unknown; | ||
constructor(message: string, valueThatFailed: unknown); | ||
import * as t from 'io-ts'; | ||
/** | ||
* Why does this wrapper exist? | ||
* | ||
* `io-ts` is awesome and super powerful, but the API leaves some things to be | ||
* desired. It was designed to be used with `fp-ts` which is a library for | ||
* functional programming patterns in TypeScipt. The resulting type on the | ||
* "decode" method is an Either which is great to work with when using `fp-ts`, | ||
* but is otherwise cumbersome. | ||
* | ||
* By introducing an `assert` method, we can guarantee the function will always | ||
* return the decoded type by throwing an error if it fails to decode. With | ||
* promises we can easily chain an assertion and the promise will reject if | ||
* the data fails to decode. | ||
* | ||
* Additionally, this wrapper provides an `optional` property which unions | ||
* the codec with the `undefined` codec. This provides an API that is both | ||
* easier to use and more readable for a very common scenario. | ||
* | ||
* Next is the new `object` type which seeks to replace `t.type` in io-ts and | ||
* which is less immediately obvious. When deriving type information from | ||
* `t.type` with `t.TypeOf`, keys whose values are unioned with undefined | ||
* are still required. The suggested strategy in `io-ts` is to define optional | ||
* keys separately and intersect them with required keys. The `object` type | ||
* addresses this by returning a type that automatically enables optional | ||
* keys where values are unioned with undefined. This combined with the | ||
* `optional` property makes for a much nicer API when defining object shapes. | ||
*/ | ||
declare type KeysWithValueType<O, T> = { | ||
[Key in keyof O]: Key; | ||
}[Exclude<keyof O, KeysWithoutValueType<O, T>>]; | ||
declare type KeysWithoutValueType<O, T> = { | ||
[Key in keyof O]: T extends O[Key] ? never : Key; | ||
}[keyof O]; | ||
declare type RepackKeys<T> = { | ||
[Key in keyof T]: T[Key]; | ||
} & {}; | ||
declare type EnableOptionalKeys<T> = RepackKeys<{ | ||
[MandatoryKey in KeysWithoutValueType<T, undefined>]: T[MandatoryKey]; | ||
} & { | ||
[OptionalKey in KeysWithValueType<T, undefined>]?: T[OptionalKey]; | ||
}>; | ||
export declare class EnGardeAssertionError extends Error { | ||
errors: t.Errors; | ||
constructor(errors: t.Errors); | ||
} | ||
declare const assertShape: <Guard extends TypeGuard<any>>(guard: Guard) => (obj: unknown) => TypeFromGuard<Guard>; | ||
declare const eg: { | ||
string: TypeGuardWithOptional<string>; | ||
number: TypeGuardWithOptional<number>; | ||
boolean: TypeGuardWithOptional<boolean>; | ||
null: TypeGuardWithOptional<null>; | ||
/** | ||
* Accepts a guard and returns a guard that checks that the value is an | ||
* array and that each element in the array matches the given guard. | ||
*/ | ||
arrayOf: <T>(typeGuard: TypeGuard<T>) => TypeGuardWithOptional<T[]>; | ||
/** | ||
* Accepts any number of guards and returns a type guard that will only | ||
* match if the given value is a tuple where each value matches the guard | ||
* in that same position. | ||
* | ||
* @example | ||
* const isStringStringNumber = eg.tuple( | ||
* eg.string, | ||
* eg.string, | ||
* eg.number, | ||
* ) | ||
* | ||
* if (isStringStringNumber(["hi", "there", 4])) { | ||
* // ... | ||
* } | ||
*/ | ||
tuple: <TypeGuards extends TypeGuard<any>[]>(...params: TypeGuards) => TypeGuardWithOptional<{ | ||
return: []; | ||
recurse: { | ||
return: [TypeFromGuard<import("@cloudflare/types/src").Head<TypeGuards>>]; | ||
recurse: { | ||
return: [TypeFromGuard<import("@cloudflare/types/src").Head<import("@cloudflare/types/src").Tail<TypeGuards>>>]; | ||
recurse: { | ||
return: [TypeFromGuard<import("@cloudflare/types/src").Head<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<TypeGuards>>>>]; | ||
recurse: { | ||
return: [TypeFromGuard<import("@cloudflare/types/src").Head<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<TypeGuards>>>>>]; | ||
recurse: { | ||
return: [TypeFromGuard<import("@cloudflare/types/src").Head<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<TypeGuards>>>>>>]; | ||
recurse: { | ||
return: [TypeFromGuard<import("@cloudflare/types/src").Head<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<TypeGuards>>>>>>>]; | ||
recurse: { | ||
return: [TypeFromGuard<import("@cloudflare/types/src").Head<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<TypeGuards>>>>>>>>]; | ||
recurse: { | ||
return: [TypeFromGuard<import("@cloudflare/types/src").Head<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<TypeGuards>>>>>>>>>]; | ||
recurse: { | ||
return: [TypeFromGuard<import("@cloudflare/types/src").Head<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<TypeGuards>>>>>>>>>>]; | ||
recurse: { | ||
return: [TypeFromGuard<import("@cloudflare/types/src").Head<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<TypeGuards>>>>>>>>>>>]; | ||
recurse: any[import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<TypeGuards>>>>>>>>>>> extends [] ? "return" : "recurse"]; | ||
}[import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<TypeGuards>>>>>>>>>> extends [] ? "return" : "recurse"]; | ||
}[import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<TypeGuards>>>>>>>>> extends [] ? "return" : "recurse"]; | ||
}[import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<TypeGuards>>>>>>>> extends [] ? "return" : "recurse"]; | ||
}[import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<TypeGuards>>>>>>> extends [] ? "return" : "recurse"]; | ||
}[import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<TypeGuards>>>>>> extends [] ? "return" : "recurse"]; | ||
}[import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<TypeGuards>>>>> extends [] ? "return" : "recurse"]; | ||
}[import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<TypeGuards>>>> extends [] ? "return" : "recurse"]; | ||
}[import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<TypeGuards>>> extends [] ? "return" : "recurse"]; | ||
}[import("@cloudflare/types/src").Tail<import("@cloudflare/types/src").Tail<TypeGuards>> extends [] ? "return" : "recurse"]; | ||
}[import("@cloudflare/types/src").Tail<TypeGuards> extends [] ? "return" : "recurse"]; | ||
}[TypeGuards extends [] ? "return" : "recurse"]>; | ||
/** | ||
* Accepts an object definition where each key must be a guard. Returns | ||
* a guard that checks that a given value is an object and that each key's | ||
* value passes the definition's guard. Fails if any keys exist on the given | ||
* value that do not exist on the object definition. | ||
*/ | ||
exact: <Definition extends import("./type-helpers").ObjectGuardDefinition>(definition: Definition) => TypeGuardWithOptional<{ [Key_3 in keyof ({ [MandatoryKey in { [Key_1 in keyof { [Key in keyof Definition]: TypeFromGuard<Definition[Key]>; }]: undefined extends { [Key in keyof Definition]: TypeFromGuard<Definition[Key]>; }[Key_1] ? never : Key_1; }[keyof Definition]]: { [Key in keyof Definition]: TypeFromGuard<Definition[Key]>; }[MandatoryKey]; } & { [OptionalKey in { [Key_2 in keyof { [Key in keyof Definition]: TypeFromGuard<Definition[Key]>; }]: Key_2; }[Exclude<keyof Definition, { [Key_1 in keyof { [Key in keyof Definition]: TypeFromGuard<Definition[Key]>; }]: undefined extends { [Key in keyof Definition]: TypeFromGuard<Definition[Key]>; }[Key_1] ? never : Key_1; }[keyof Definition]>]]?: { [Key in keyof Definition]: TypeFromGuard<Definition[Key]>; }[OptionalKey] | undefined; })]: ({ [MandatoryKey in { [Key_1 in keyof { [Key in keyof Definition]: TypeFromGuard<Definition[Key]>; }]: undefined extends { [Key in keyof Definition]: TypeFromGuard<Definition[Key]>; }[Key_1] ? never : Key_1; }[keyof Definition]]: { [Key in keyof Definition]: TypeFromGuard<Definition[Key]>; }[MandatoryKey]; } & { [OptionalKey in { [Key_2 in keyof { [Key in keyof Definition]: TypeFromGuard<Definition[Key]>; }]: Key_2; }[Exclude<keyof Definition, { [Key_1 in keyof { [Key in keyof Definition]: TypeFromGuard<Definition[Key]>; }]: undefined extends { [Key in keyof Definition]: TypeFromGuard<Definition[Key]>; }[Key_1] ? never : Key_1; }[keyof Definition]>]]?: { [Key in keyof Definition]: TypeFromGuard<Definition[Key]>; }[OptionalKey] | undefined; })[Key_3]; }>; | ||
/** | ||
* Accepts a constructor and returns a guard that checks whether a value is | ||
* an instance of that constructor using `instanceof` | ||
*/ | ||
instanceOf: <Constructor extends new (...args: any[]) => any>(ctor: Constructor) => TypeGuardWithOptional<InstanceType<Constructor>>; | ||
/** | ||
* Accepts an object definition where each key must be a guard. Returns | ||
* a guard that checks that a given value is an object and that each key's | ||
* value passes the definition's guard | ||
*/ | ||
object: <Definition_1 extends import("./type-helpers").ObjectGuardDefinition>(definition: Definition_1) => TypeGuardWithOptional<{ [Key_7 in keyof ({ [MandatoryKey_1 in { [Key_5 in keyof { [Key_4 in keyof Definition_1]: TypeFromGuard<Definition_1[Key_4]>; }]: undefined extends { [Key_4 in keyof Definition_1]: TypeFromGuard<Definition_1[Key_4]>; }[Key_5] ? never : Key_5; }[keyof Definition_1]]: { [Key_4 in keyof Definition_1]: TypeFromGuard<Definition_1[Key_4]>; }[MandatoryKey_1]; } & { [OptionalKey_1 in { [Key_6 in keyof { [Key_4 in keyof Definition_1]: TypeFromGuard<Definition_1[Key_4]>; }]: Key_6; }[Exclude<keyof Definition_1, { [Key_5 in keyof { [Key_4 in keyof Definition_1]: TypeFromGuard<Definition_1[Key_4]>; }]: undefined extends { [Key_4 in keyof Definition_1]: TypeFromGuard<Definition_1[Key_4]>; }[Key_5] ? never : Key_5; }[keyof Definition_1]>]]?: { [Key_4 in keyof Definition_1]: TypeFromGuard<Definition_1[Key_4]>; }[OptionalKey_1] | undefined; })]: ({ [MandatoryKey_1 in { [Key_5 in keyof { [Key_4 in keyof Definition_1]: TypeFromGuard<Definition_1[Key_4]>; }]: undefined extends { [Key_4 in keyof Definition_1]: TypeFromGuard<Definition_1[Key_4]>; }[Key_5] ? never : Key_5; }[keyof Definition_1]]: { [Key_4 in keyof Definition_1]: TypeFromGuard<Definition_1[Key_4]>; }[MandatoryKey_1]; } & { [OptionalKey_1 in { [Key_6 in keyof { [Key_4 in keyof Definition_1]: TypeFromGuard<Definition_1[Key_4]>; }]: Key_6; }[Exclude<keyof Definition_1, { [Key_5 in keyof { [Key_4 in keyof Definition_1]: TypeFromGuard<Definition_1[Key_4]>; }]: undefined extends { [Key_4 in keyof Definition_1]: TypeFromGuard<Definition_1[Key_4]>; }[Key_5] ? never : Key_5; }[keyof Definition_1]>]]?: { [Key_4 in keyof Definition_1]: TypeFromGuard<Definition_1[Key_4]>; }[OptionalKey_1] | undefined; })[Key_7]; }>; | ||
/** | ||
* Accepts a type guard and returns an index signature where values must | ||
* pass the type guard. | ||
*/ | ||
objectWithValuesOf: <T_1, Keys extends string | number | symbol>(typeGuard: TypeGuard<T_1>) => TypeGuardWithOptional<Record<Keys, T_1>>; | ||
/** | ||
* Accepts any number of guards as arguments. Returns a guard that checks if | ||
* a given value passes any of the input guards. | ||
*/ | ||
oneOf: <TypeGuards_1 extends TypeGuard<any>[]>(...params: TypeGuards_1) => TypeGuardWithOptional<TypeFromGuard<import("./type-helpers").TypeOfArray<TypeGuards_1>>>; | ||
/** | ||
* Accepts any number of guards as arguments. Returns a guard that checks if | ||
* a given value passes all of the input guards. | ||
*/ | ||
all: <TypeGuards_2 extends TypeGuard<any>[]>(...params: TypeGuards_2) => TypeGuardWithOptional<import("@cloudflare/types/src").UnionToIntersection<TypeFromGuard<import("./type-helpers").TypeOfArray<TypeGuards_2>>>>; | ||
/** | ||
* Accepts primitives and infers their types. Returns a guard that checks | ||
* whether a value matches any of the literal values. | ||
*/ | ||
oneOfLiterals: <Args extends (string | number | boolean | null | undefined)[]>(...params: Args) => TypeGuardWithOptional<import("./type-helpers").TypeOfArray<Args>>; | ||
any: (_: unknown) => _ is any; | ||
unknown: (_: unknown) => _ is unknown; | ||
undefined: (obj: unknown) => obj is undefined; | ||
declare type IoTsCodec = t.Any; | ||
export declare class Codec<Codec extends IoTsCodec> { | ||
ioTsCodec: Codec; | ||
constructor(ioTsCodec: Codec); | ||
name: string; | ||
is: Codec['is']; | ||
encode: Codec['encode']; | ||
decode: Codec['decode']; | ||
validate: Codec['validate']; | ||
pipe: Codec['pipe']; | ||
_A: Codec['_A']; | ||
_O: Codec['_O']; | ||
_I: Codec['_I']; | ||
asDecoder: Codec['asDecoder']; | ||
asEncoder: Codec['asEncoder']; | ||
assertDecode: (value: unknown) => Codec["_A"]; | ||
get optional(): import(".").Codec<t.UnionC<[Codec, t.UndefinedC]>>; | ||
} | ||
declare type InterfaceTypeWithOptionalKeys<P extends t.Props> = t.InterfaceType<P, EnableOptionalKeys<{ | ||
[K in keyof P]: t.TypeOf<P[K]>; | ||
}>, EnableOptionalKeys<{ | ||
[K in keyof P]: t.OutputOf<P[K]>; | ||
}>, unknown>; | ||
export declare class ObjectCodec<P extends t.Props, CodecWithOptionalKeys extends t.InterfaceType<any, any, any, any> = InterfaceTypeWithOptionalKeys<P>> { | ||
ioTsCodec: t.TypeC<P>; | ||
constructor(ioTsCodec: t.TypeC<P>); | ||
name: string; | ||
is: CodecWithOptionalKeys['is']; | ||
encode: CodecWithOptionalKeys['encode']; | ||
decode: CodecWithOptionalKeys['decode']; | ||
validate: CodecWithOptionalKeys['validate']; | ||
pipe: CodecWithOptionalKeys['pipe']; | ||
_A: CodecWithOptionalKeys['_A']; | ||
_O: CodecWithOptionalKeys['_O']; | ||
_I: CodecWithOptionalKeys['_I']; | ||
asDecoder: CodecWithOptionalKeys['asDecoder']; | ||
asEncoder: CodecWithOptionalKeys['asEncoder']; | ||
assertDecode: (value: unknown) => CodecWithOptionalKeys["_A"]; | ||
get optional(): Codec<t.UnionC<[t.TypeC<P>, t.UndefinedC]>>; | ||
readonly _tag: 'InterfaceType'; | ||
props: CodecWithOptionalKeys['props']; | ||
} | ||
export declare type TypeFromCodec<T extends IoTsCodec> = t.TypeOf<T>; | ||
export declare const eg: { | ||
object: <P extends t.Props>(p: P, name?: string | undefined) => ObjectCodec<P, InterfaceTypeWithOptionalKeys<P>>; | ||
tuple: <Codecs extends never[] | [t.Any, ...t.Any[]]>(codecs: Codecs, name?: string | undefined) => Codec<t.TupleC<Exclude<Codecs, never[]>>>; | ||
intersection: <Codecs_1 extends [t.Any, t.Any, ...t.Any[]]>(codecs: Codecs_1, name?: string | undefined) => Codec<t.IntersectionC<Codecs_1>>; | ||
array: <C extends t.Mixed>(codec: C, name?: string | undefined) => Codec<t.ArrayC<C>>; | ||
union: <CS extends [t.Mixed, t.Mixed, ...t.Mixed[]]>(codecs: CS, name?: string | undefined) => Codec<t.UnionC<CS>>; | ||
record: <D extends t.Mixed, C_1 extends t.Mixed>(domain: D, codomain: C_1, name?: string | undefined) => Codec<t.RecordC<D, C_1>>; | ||
partial: <P_1 extends t.Props>(props: P_1, name?: string | undefined) => Codec<t.PartialC<P_1>>; | ||
literal: <V extends string | number | boolean>(value: V, name?: string | undefined) => Codec<t.LiteralC<V>>; | ||
exact: <C_2 extends t.HasProps>(codec: C_2, name?: string | undefined) => Codec<t.ExactC<C_2>>; | ||
string: Codec<t.StringC>; | ||
number: Codec<t.NumberC>; | ||
boolean: Codec<t.BooleanC>; | ||
null: Codec<t.NullC>; | ||
undefined: Codec<t.UndefinedC>; | ||
any: Codec<t.AnyC>; | ||
unknown: Codec<t.UnknownC>; | ||
}; | ||
export default eg; | ||
export { TypeGuard, TypeFromGuard, TypeGuardWithOptional, primitiveGuards, higherOrderGuards, TypeGuardError, assertShape, addOptionalToGuard, addOptionalToHigherOrderGuard }; | ||
export { t }; |
259
es/index.js
function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } | ||
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; } | ||
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } | ||
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } | ||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
@@ -7,6 +15,6 @@ | ||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } | ||
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } | ||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } | ||
function _wrapNativeSuper(Class) { var _cache = typeof Map === "function" ? new Map() : undefined; _wrapNativeSuper = function _wrapNativeSuper(Class) { if (Class === null || !_isNativeFunction(Class)) return Class; if (typeof Class !== "function") { throw new TypeError("Super expression must either be null or a function"); } if (typeof _cache !== "undefined") { if (_cache.has(Class)) return _cache.get(Class); _cache.set(Class, Wrapper); } function Wrapper() { return _construct(Class, arguments, _getPrototypeOf(this).constructor); } Wrapper.prototype = Object.create(Class.prototype, { constructor: { value: Wrapper, enumerable: false, writable: true, configurable: true } }); return _setPrototypeOf(Wrapper, Class); }; return _wrapNativeSuper(Class); } | ||
@@ -24,107 +32,182 @@ | ||
import { primitiveGuards, higherOrderGuards } from './core'; | ||
import { TypeGuard, TypeFromGuard, TypeGuardWithOptional } from './type-helpers'; | ||
import { addOptionalToGuard, addOptionalToHigherOrderGuard } from './optional'; | ||
import * as t from 'io-ts'; | ||
/** | ||
* Why does this wrapper exist? | ||
* | ||
* `io-ts` is awesome and super powerful, but the API leaves some things to be | ||
* desired. It was designed to be used with `fp-ts` which is a library for | ||
* functional programming patterns in TypeScipt. The resulting type on the | ||
* "decode" method is an Either which is great to work with when using `fp-ts`, | ||
* but is otherwise cumbersome. | ||
* | ||
* By introducing an `assert` method, we can guarantee the function will always | ||
* return the decoded type by throwing an error if it fails to decode. With | ||
* promises we can easily chain an assertion and the promise will reject if | ||
* the data fails to decode. | ||
* | ||
* Additionally, this wrapper provides an `optional` property which unions | ||
* the codec with the `undefined` codec. This provides an API that is both | ||
* easier to use and more readable for a very common scenario. | ||
* | ||
* Next is the new `object` type which seeks to replace `t.type` in io-ts and | ||
* which is less immediately obvious. When deriving type information from | ||
* `t.type` with `t.TypeOf`, keys whose values are unioned with undefined | ||
* are still required. The suggested strategy in `io-ts` is to define optional | ||
* keys separately and intersect them with required keys. The `object` type | ||
* addresses this by returning a type that automatically enables optional | ||
* keys where values are unioned with undefined. This combined with the | ||
* `optional` property makes for a much nicer API when defining object shapes. | ||
*/ | ||
var TypeGuardError = | ||
export var EnGardeAssertionError = | ||
/*#__PURE__*/ | ||
function (_Error) { | ||
_inherits(TypeGuardError, _Error); | ||
_inherits(EnGardeAssertionError, _Error); | ||
function TypeGuardError(message, valueThatFailed) { | ||
_classCallCheck(this, TypeGuardError); | ||
function EnGardeAssertionError(errors) { | ||
var _this; | ||
return _possibleConstructorReturn(this, _getPrototypeOf(TypeGuardError).call(this, message)); | ||
_classCallCheck(this, EnGardeAssertionError); | ||
_this = _possibleConstructorReturn(this, _getPrototypeOf(EnGardeAssertionError).call(this)); | ||
Object.setPrototypeOf(_assertThisInitialized(_assertThisInitialized(_this)), EnGardeAssertionError.prototype); | ||
return _this; | ||
} | ||
return TypeGuardError; | ||
return EnGardeAssertionError; | ||
}(_wrapNativeSuper(Error)); | ||
export var Codec = | ||
/*#__PURE__*/ | ||
function () { | ||
function Codec(ioTsCodec) { | ||
var _this2 = this; | ||
var assertShape = function assertShape(guard) { | ||
return function (obj) { | ||
if (guard(obj)) return obj; | ||
throw new TypeGuardError('Failed type guard', obj); | ||
}; | ||
}; | ||
_classCallCheck(this, Codec); | ||
var eg = { | ||
string: addOptionalToGuard(primitiveGuards.string), | ||
number: addOptionalToGuard(primitiveGuards.number), | ||
boolean: addOptionalToGuard(primitiveGuards.boolean), | ||
null: addOptionalToGuard(primitiveGuards.null), | ||
_defineProperty(this, "name", this.ioTsCodec.name); | ||
/** | ||
* Accepts a guard and returns a guard that checks that the value is an | ||
* array and that each element in the array matches the given guard. | ||
*/ | ||
arrayOf: addOptionalToHigherOrderGuard(higherOrderGuards.arrayOf), | ||
_defineProperty(this, "is", this.ioTsCodec.is); | ||
/** | ||
* Accepts any number of guards and returns a type guard that will only | ||
* match if the given value is a tuple where each value matches the guard | ||
* in that same position. | ||
* | ||
* @example | ||
* const isStringStringNumber = eg.tuple( | ||
* eg.string, | ||
* eg.string, | ||
* eg.number, | ||
* ) | ||
* | ||
* if (isStringStringNumber(["hi", "there", 4])) { | ||
* // ... | ||
* } | ||
*/ | ||
tuple: addOptionalToHigherOrderGuard(higherOrderGuards.tuple), | ||
_defineProperty(this, "encode", this.ioTsCodec.encode); | ||
/** | ||
* Accepts an object definition where each key must be a guard. Returns | ||
* a guard that checks that a given value is an object and that each key's | ||
* value passes the definition's guard. Fails if any keys exist on the given | ||
* value that do not exist on the object definition. | ||
*/ | ||
exact: addOptionalToHigherOrderGuard(higherOrderGuards.exact), | ||
_defineProperty(this, "decode", this.ioTsCodec.decode); | ||
/** | ||
* Accepts a constructor and returns a guard that checks whether a value is | ||
* an instance of that constructor using `instanceof` | ||
*/ | ||
instanceOf: addOptionalToHigherOrderGuard(higherOrderGuards.instanceOf), | ||
_defineProperty(this, "validate", this.ioTsCodec.validate); | ||
/** | ||
* Accepts an object definition where each key must be a guard. Returns | ||
* a guard that checks that a given value is an object and that each key's | ||
* value passes the definition's guard | ||
*/ | ||
object: addOptionalToHigherOrderGuard(higherOrderGuards.object), | ||
_defineProperty(this, "pipe", this.ioTsCodec.pipe); | ||
/** | ||
* Accepts a type guard and returns an index signature where values must | ||
* pass the type guard. | ||
*/ | ||
objectWithValuesOf: addOptionalToHigherOrderGuard(higherOrderGuards.objectWithValuesOf), | ||
_defineProperty(this, "_A", this.ioTsCodec._A); | ||
/** | ||
* Accepts any number of guards as arguments. Returns a guard that checks if | ||
* a given value passes any of the input guards. | ||
*/ | ||
oneOf: addOptionalToHigherOrderGuard(higherOrderGuards.oneOf), | ||
_defineProperty(this, "_O", this.ioTsCodec._O); | ||
/** | ||
* Accepts any number of guards as arguments. Returns a guard that checks if | ||
* a given value passes all of the input guards. | ||
*/ | ||
all: addOptionalToHigherOrderGuard(higherOrderGuards.all), | ||
_defineProperty(this, "_I", this.ioTsCodec._I); | ||
/** | ||
* Accepts primitives and infers their types. Returns a guard that checks | ||
* whether a value matches any of the literal values. | ||
*/ | ||
oneOfLiterals: addOptionalToHigherOrderGuard(higherOrderGuards.oneOfLiterals), | ||
// These guards do not require optional decoration | ||
any: primitiveGuards.any, | ||
unknown: primitiveGuards.unknown, | ||
undefined: primitiveGuards.undefined | ||
_defineProperty(this, "asDecoder", this.ioTsCodec.asDecoder); | ||
_defineProperty(this, "asEncoder", this.ioTsCodec.asEncoder); | ||
_defineProperty(this, "assertDecode", function (value) { | ||
var result = _this2.ioTsCodec.decode(value); | ||
if (result._tag === 'Left') throw new EnGardeAssertionError(result.left); | ||
return result.right; | ||
}); | ||
} | ||
_createClass(Codec, [{ | ||
key: "optional", | ||
get: function get() { | ||
return new Codec(t.union([this.ioTsCodec, t.undefined])); | ||
} | ||
}]); | ||
return Codec; | ||
}(); | ||
export var ObjectCodec = | ||
/*#__PURE__*/ | ||
function () { | ||
function ObjectCodec(ioTsCodec) { | ||
var _this3 = this; | ||
_classCallCheck(this, ObjectCodec); | ||
_defineProperty(this, "name", this.ioTsCodec.name); | ||
_defineProperty(this, "is", this.ioTsCodec.is); | ||
_defineProperty(this, "encode", this.ioTsCodec.encode); | ||
_defineProperty(this, "decode", this.ioTsCodec.decode); | ||
_defineProperty(this, "validate", this.ioTsCodec.validate); | ||
_defineProperty(this, "pipe", this.ioTsCodec.pipe); | ||
_defineProperty(this, "_A", this.ioTsCodec._A); | ||
_defineProperty(this, "_O", this.ioTsCodec._O); | ||
_defineProperty(this, "_I", this.ioTsCodec._I); | ||
_defineProperty(this, "asDecoder", this.ioTsCodec.asDecoder); | ||
_defineProperty(this, "asEncoder", this.ioTsCodec.asEncoder); | ||
_defineProperty(this, "assertDecode", function (value) { | ||
var result = _this3.ioTsCodec.decode(value); | ||
if (result._tag === 'Left') throw new EnGardeAssertionError(result.left); | ||
return result.right; | ||
}); | ||
_defineProperty(this, "_tag", 'InterfaceType'); | ||
_defineProperty(this, "props", this.ioTsCodec.props); | ||
} | ||
_createClass(ObjectCodec, [{ | ||
key: "optional", | ||
get: function get() { | ||
return new Codec(t.union([this.ioTsCodec, t.undefined])); | ||
} | ||
}]); | ||
return ObjectCodec; | ||
}(); | ||
var primitiveCodecs = { | ||
string: new Codec(t.string), | ||
number: new Codec(t.number), | ||
boolean: new Codec(t.boolean), | ||
null: new Codec(t.null), | ||
undefined: new Codec(t.undefined), | ||
any: new Codec(t.any), | ||
unknown: new Codec(t.unknown) | ||
}; | ||
export default eg; | ||
export { TypeGuard, TypeFromGuard, TypeGuardWithOptional, primitiveGuards, higherOrderGuards, TypeGuardError, assertShape, addOptionalToGuard, addOptionalToHigherOrderGuard }; | ||
var wrapHigherOrderCodec = function wrapHigherOrderCodec(higherOrderCodec) { | ||
return function () { | ||
return new Codec(higherOrderCodec.apply(void 0, arguments)); | ||
}; | ||
}; | ||
var higherOrderCodecs = { | ||
// Using a special type for object which enables optional keys | ||
object: function object(p, name) { | ||
return new ObjectCodec(t.type(p, name)); | ||
}, | ||
tuple: function tuple(codecs, name) { | ||
return new Codec(t.tuple(codecs, name)); | ||
}, | ||
intersection: function intersection(codecs, name) { | ||
return new Codec(t.intersection(codecs, name)); | ||
}, | ||
// The types for these higher order codecs work with simple hoc wrapper | ||
array: wrapHigherOrderCodec(t.array), | ||
union: wrapHigherOrderCodec(t.union), | ||
record: wrapHigherOrderCodec(t.record), | ||
partial: wrapHigherOrderCodec(t.partial), | ||
literal: wrapHigherOrderCodec(t.literal), | ||
exact: wrapHigherOrderCodec(t.exact) | ||
}; | ||
export var eg = _objectSpread({}, primitiveCodecs, higherOrderCodecs); | ||
export { t }; |
289
lib/index.js
@@ -6,54 +6,20 @@ "use strict"; | ||
}); | ||
Object.defineProperty(exports, "primitiveGuards", { | ||
enumerable: true, | ||
get: function get() { | ||
return _core.primitiveGuards; | ||
} | ||
}); | ||
Object.defineProperty(exports, "higherOrderGuards", { | ||
enumerable: true, | ||
get: function get() { | ||
return _core.higherOrderGuards; | ||
} | ||
}); | ||
Object.defineProperty(exports, "TypeGuard", { | ||
enumerable: true, | ||
get: function get() { | ||
return _typeHelpers.TypeGuard; | ||
} | ||
}); | ||
Object.defineProperty(exports, "TypeFromGuard", { | ||
enumerable: true, | ||
get: function get() { | ||
return _typeHelpers.TypeFromGuard; | ||
} | ||
}); | ||
Object.defineProperty(exports, "TypeGuardWithOptional", { | ||
enumerable: true, | ||
get: function get() { | ||
return _typeHelpers.TypeGuardWithOptional; | ||
} | ||
}); | ||
Object.defineProperty(exports, "addOptionalToGuard", { | ||
enumerable: true, | ||
get: function get() { | ||
return _optional.addOptionalToGuard; | ||
} | ||
}); | ||
Object.defineProperty(exports, "addOptionalToHigherOrderGuard", { | ||
enumerable: true, | ||
get: function get() { | ||
return _optional.addOptionalToHigherOrderGuard; | ||
} | ||
}); | ||
exports.assertShape = exports.TypeGuardError = exports.default = void 0; | ||
exports.t = exports.eg = exports.ObjectCodec = exports.Codec = exports.EnGardeAssertionError = void 0; | ||
var _core = require("./core"); | ||
var t = _interopRequireWildcard(require("io-ts")); | ||
var _typeHelpers = require("./type-helpers"); | ||
exports.t = t; | ||
var _optional = require("./optional"); | ||
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } | ||
function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } | ||
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; } | ||
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } | ||
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } | ||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
@@ -63,6 +29,6 @@ | ||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } | ||
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } | ||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } | ||
function _wrapNativeSuper(Class) { var _cache = typeof Map === "function" ? new Map() : undefined; _wrapNativeSuper = function _wrapNativeSuper(Class) { if (Class === null || !_isNativeFunction(Class)) return Class; if (typeof Class !== "function") { throw new TypeError("Super expression must either be null or a function"); } if (typeof _cache !== "undefined") { if (_cache.has(Class)) return _cache.get(Class); _cache.set(Class, Wrapper); } function Wrapper() { return _construct(Class, arguments, _getPrototypeOf(this).constructor); } Wrapper.prototype = Object.create(Class.prototype, { constructor: { value: Wrapper, enumerable: false, writable: true, configurable: true } }); return _setPrototypeOf(Wrapper, Class); }; return _wrapNativeSuper(Class); } | ||
@@ -80,106 +46,163 @@ | ||
var TypeGuardError = | ||
var EnGardeAssertionError = | ||
/*#__PURE__*/ | ||
function (_Error) { | ||
_inherits(TypeGuardError, _Error); | ||
_inherits(EnGardeAssertionError, _Error); | ||
function TypeGuardError(message, valueThatFailed) { | ||
_classCallCheck(this, TypeGuardError); | ||
function EnGardeAssertionError(errors) { | ||
var _this; | ||
return _possibleConstructorReturn(this, _getPrototypeOf(TypeGuardError).call(this, message)); | ||
_classCallCheck(this, EnGardeAssertionError); | ||
_this = _possibleConstructorReturn(this, _getPrototypeOf(EnGardeAssertionError).call(this)); | ||
Object.setPrototypeOf(_assertThisInitialized(_assertThisInitialized(_this)), EnGardeAssertionError.prototype); | ||
return _this; | ||
} | ||
return TypeGuardError; | ||
return EnGardeAssertionError; | ||
}(_wrapNativeSuper(Error)); | ||
exports.TypeGuardError = TypeGuardError; | ||
exports.EnGardeAssertionError = EnGardeAssertionError; | ||
var assertShape = function assertShape(guard) { | ||
return function (obj) { | ||
if (guard(obj)) return obj; | ||
throw new TypeGuardError('Failed type guard', obj); | ||
}; | ||
}; | ||
var Codec = | ||
/*#__PURE__*/ | ||
function () { | ||
function Codec(ioTsCodec) { | ||
var _this2 = this; | ||
exports.assertShape = assertShape; | ||
var eg = { | ||
string: (0, _optional.addOptionalToGuard)(_core.primitiveGuards.string), | ||
number: (0, _optional.addOptionalToGuard)(_core.primitiveGuards.number), | ||
boolean: (0, _optional.addOptionalToGuard)(_core.primitiveGuards.boolean), | ||
null: (0, _optional.addOptionalToGuard)(_core.primitiveGuards.null), | ||
_classCallCheck(this, Codec); | ||
/** | ||
* Accepts a guard and returns a guard that checks that the value is an | ||
* array and that each element in the array matches the given guard. | ||
*/ | ||
arrayOf: (0, _optional.addOptionalToHigherOrderGuard)(_core.higherOrderGuards.arrayOf), | ||
_defineProperty(this, "name", this.ioTsCodec.name); | ||
/** | ||
* Accepts any number of guards and returns a type guard that will only | ||
* match if the given value is a tuple where each value matches the guard | ||
* in that same position. | ||
* | ||
* @example | ||
* const isStringStringNumber = eg.tuple( | ||
* eg.string, | ||
* eg.string, | ||
* eg.number, | ||
* ) | ||
* | ||
* if (isStringStringNumber(["hi", "there", 4])) { | ||
* // ... | ||
* } | ||
*/ | ||
tuple: (0, _optional.addOptionalToHigherOrderGuard)(_core.higherOrderGuards.tuple), | ||
_defineProperty(this, "is", this.ioTsCodec.is); | ||
/** | ||
* Accepts an object definition where each key must be a guard. Returns | ||
* a guard that checks that a given value is an object and that each key's | ||
* value passes the definition's guard. Fails if any keys exist on the given | ||
* value that do not exist on the object definition. | ||
*/ | ||
exact: (0, _optional.addOptionalToHigherOrderGuard)(_core.higherOrderGuards.exact), | ||
_defineProperty(this, "encode", this.ioTsCodec.encode); | ||
/** | ||
* Accepts a constructor and returns a guard that checks whether a value is | ||
* an instance of that constructor using `instanceof` | ||
*/ | ||
instanceOf: (0, _optional.addOptionalToHigherOrderGuard)(_core.higherOrderGuards.instanceOf), | ||
_defineProperty(this, "decode", this.ioTsCodec.decode); | ||
/** | ||
* Accepts an object definition where each key must be a guard. Returns | ||
* a guard that checks that a given value is an object and that each key's | ||
* value passes the definition's guard | ||
*/ | ||
object: (0, _optional.addOptionalToHigherOrderGuard)(_core.higherOrderGuards.object), | ||
_defineProperty(this, "validate", this.ioTsCodec.validate); | ||
/** | ||
* Accepts a type guard and returns an index signature where values must | ||
* pass the type guard. | ||
*/ | ||
objectWithValuesOf: (0, _optional.addOptionalToHigherOrderGuard)(_core.higherOrderGuards.objectWithValuesOf), | ||
_defineProperty(this, "pipe", this.ioTsCodec.pipe); | ||
/** | ||
* Accepts any number of guards as arguments. Returns a guard that checks if | ||
* a given value passes any of the input guards. | ||
*/ | ||
oneOf: (0, _optional.addOptionalToHigherOrderGuard)(_core.higherOrderGuards.oneOf), | ||
_defineProperty(this, "_A", this.ioTsCodec._A); | ||
/** | ||
* Accepts any number of guards as arguments. Returns a guard that checks if | ||
* a given value passes all of the input guards. | ||
*/ | ||
all: (0, _optional.addOptionalToHigherOrderGuard)(_core.higherOrderGuards.all), | ||
_defineProperty(this, "_O", this.ioTsCodec._O); | ||
/** | ||
* Accepts primitives and infers their types. Returns a guard that checks | ||
* whether a value matches any of the literal values. | ||
*/ | ||
oneOfLiterals: (0, _optional.addOptionalToHigherOrderGuard)(_core.higherOrderGuards.oneOfLiterals), | ||
// These guards do not require optional decoration | ||
any: _core.primitiveGuards.any, | ||
unknown: _core.primitiveGuards.unknown, | ||
undefined: _core.primitiveGuards.undefined | ||
_defineProperty(this, "_I", this.ioTsCodec._I); | ||
_defineProperty(this, "asDecoder", this.ioTsCodec.asDecoder); | ||
_defineProperty(this, "asEncoder", this.ioTsCodec.asEncoder); | ||
_defineProperty(this, "assertDecode", function (value) { | ||
var result = _this2.ioTsCodec.decode(value); | ||
if (result._tag === 'Left') throw new EnGardeAssertionError(result.left); | ||
return result.right; | ||
}); | ||
} | ||
_createClass(Codec, [{ | ||
key: "optional", | ||
get: function get() { | ||
return new Codec(t.union([this.ioTsCodec, t.undefined])); | ||
} | ||
}]); | ||
return Codec; | ||
}(); | ||
exports.Codec = Codec; | ||
var ObjectCodec = | ||
/*#__PURE__*/ | ||
function () { | ||
function ObjectCodec(ioTsCodec) { | ||
var _this3 = this; | ||
_classCallCheck(this, ObjectCodec); | ||
_defineProperty(this, "name", this.ioTsCodec.name); | ||
_defineProperty(this, "is", this.ioTsCodec.is); | ||
_defineProperty(this, "encode", this.ioTsCodec.encode); | ||
_defineProperty(this, "decode", this.ioTsCodec.decode); | ||
_defineProperty(this, "validate", this.ioTsCodec.validate); | ||
_defineProperty(this, "pipe", this.ioTsCodec.pipe); | ||
_defineProperty(this, "_A", this.ioTsCodec._A); | ||
_defineProperty(this, "_O", this.ioTsCodec._O); | ||
_defineProperty(this, "_I", this.ioTsCodec._I); | ||
_defineProperty(this, "asDecoder", this.ioTsCodec.asDecoder); | ||
_defineProperty(this, "asEncoder", this.ioTsCodec.asEncoder); | ||
_defineProperty(this, "assertDecode", function (value) { | ||
var result = _this3.ioTsCodec.decode(value); | ||
if (result._tag === 'Left') throw new EnGardeAssertionError(result.left); | ||
return result.right; | ||
}); | ||
_defineProperty(this, "_tag", 'InterfaceType'); | ||
_defineProperty(this, "props", this.ioTsCodec.props); | ||
} | ||
_createClass(ObjectCodec, [{ | ||
key: "optional", | ||
get: function get() { | ||
return new Codec(t.union([this.ioTsCodec, t.undefined])); | ||
} | ||
}]); | ||
return ObjectCodec; | ||
}(); | ||
exports.ObjectCodec = ObjectCodec; | ||
var primitiveCodecs = { | ||
string: new Codec(t.string), | ||
number: new Codec(t.number), | ||
boolean: new Codec(t.boolean), | ||
null: new Codec(t.null), | ||
undefined: new Codec(t.undefined), | ||
any: new Codec(t.any), | ||
unknown: new Codec(t.unknown) | ||
}; | ||
var _default = eg; | ||
exports.default = _default; | ||
var wrapHigherOrderCodec = function wrapHigherOrderCodec(higherOrderCodec) { | ||
return function () { | ||
return new Codec(higherOrderCodec.apply(void 0, arguments)); | ||
}; | ||
}; | ||
var higherOrderCodecs = { | ||
// Using a special type for object which enables optional keys | ||
object: function object(p, name) { | ||
return new ObjectCodec(t.type(p, name)); | ||
}, | ||
tuple: function tuple(codecs, name) { | ||
return new Codec(t.tuple(codecs, name)); | ||
}, | ||
intersection: function intersection(codecs, name) { | ||
return new Codec(t.intersection(codecs, name)); | ||
}, | ||
// The types for these higher order codecs work with simple hoc wrapper | ||
array: wrapHigherOrderCodec(t.array), | ||
union: wrapHigherOrderCodec(t.union), | ||
record: wrapHigherOrderCodec(t.record), | ||
partial: wrapHigherOrderCodec(t.partial), | ||
literal: wrapHigherOrderCodec(t.literal), | ||
exact: wrapHigherOrderCodec(t.exact) | ||
}; | ||
var eg = _objectSpread({}, primitiveCodecs, higherOrderCodecs); | ||
exports.eg = eg; |
{ | ||
"name": "@cloudflare/util-en-garde", | ||
"description": "", | ||
"version": "4.0.2", | ||
"version": "6.0.0", | ||
"types": "./dist/index.d.ts", | ||
@@ -25,6 +25,7 @@ "main": "lib/index.js", | ||
}, | ||
"devDependencies": { | ||
"@cloudflare/types": "^3.0.2" | ||
"dependencies": { | ||
"fp-ts": "^2.1.1", | ||
"io-ts": "^2.0.1" | ||
}, | ||
"gitHead": "500ff258e12d77a670469794f3d4e9e10c137bd7" | ||
"gitHead": "2c7116804e9d7e6f2e005ee4d7aa37829d2624a3" | ||
} |
263
src/index.ts
@@ -1,107 +0,176 @@ | ||
import { primitiveGuards, higherOrderGuards } from './core'; | ||
import { | ||
TypeGuard, | ||
TypeFromGuard, | ||
TypeGuardWithOptional | ||
} from './type-helpers'; | ||
import { addOptionalToGuard, addOptionalToHigherOrderGuard } from './optional'; | ||
import * as t from 'io-ts'; | ||
class TypeGuardError extends Error { | ||
constructor(message: string, public valueThatFailed: unknown) { | ||
super(message); | ||
/** | ||
* Why does this wrapper exist? | ||
* | ||
* `io-ts` is awesome and super powerful, but the API leaves some things to be | ||
* desired. It was designed to be used with `fp-ts` which is a library for | ||
* functional programming patterns in TypeScipt. The resulting type on the | ||
* "decode" method is an Either which is great to work with when using `fp-ts`, | ||
* but is otherwise cumbersome. | ||
* | ||
* By introducing an `assert` method, we can guarantee the function will always | ||
* return the decoded type by throwing an error if it fails to decode. With | ||
* promises we can easily chain an assertion and the promise will reject if | ||
* the data fails to decode. | ||
* | ||
* Additionally, this wrapper provides an `optional` property which unions | ||
* the codec with the `undefined` codec. This provides an API that is both | ||
* easier to use and more readable for a very common scenario. | ||
* | ||
* Next is the new `object` type which seeks to replace `t.type` in io-ts and | ||
* which is less immediately obvious. When deriving type information from | ||
* `t.type` with `t.TypeOf`, keys whose values are unioned with undefined | ||
* are still required. The suggested strategy in `io-ts` is to define optional | ||
* keys separately and intersect them with required keys. The `object` type | ||
* addresses this by returning a type that automatically enables optional | ||
* keys where values are unioned with undefined. This combined with the | ||
* `optional` property makes for a much nicer API when defining object shapes. | ||
*/ | ||
type KeysWithValueType<O, T> = { [Key in keyof O]: Key }[Exclude< | ||
keyof O, | ||
KeysWithoutValueType<O, T> | ||
>]; | ||
type KeysWithoutValueType<O, T> = { | ||
[Key in keyof O]: T extends O[Key] ? never : Key; | ||
}[keyof O]; | ||
type RepackKeys<T> = { [Key in keyof T]: T[Key] } & {}; | ||
type EnableOptionalKeys<T> = RepackKeys< | ||
{ [MandatoryKey in KeysWithoutValueType<T, undefined>]: T[MandatoryKey] } & | ||
{ [OptionalKey in KeysWithValueType<T, undefined>]?: T[OptionalKey] } | ||
>; | ||
export class EnGardeAssertionError extends Error { | ||
constructor(public errors: t.Errors) { | ||
super(); | ||
Object.setPrototypeOf(this, EnGardeAssertionError.prototype); | ||
} | ||
} | ||
const assertShape = <Guard extends TypeGuard<any>>(guard: Guard) => ( | ||
obj: unknown | ||
): TypeFromGuard<Guard> => { | ||
if (guard(obj)) return obj; | ||
throw new TypeGuardError('Failed type guard', obj); | ||
type IoTsCodec = t.Any; | ||
export class Codec<Codec extends IoTsCodec> { | ||
constructor(public ioTsCodec: Codec) {} | ||
public name = this.ioTsCodec.name; | ||
public is: Codec['is'] = this.ioTsCodec.is; | ||
public encode: Codec['encode'] = this.ioTsCodec.encode; | ||
public decode: Codec['decode'] = this.ioTsCodec.decode; | ||
public validate: Codec['validate'] = this.ioTsCodec.validate; | ||
public pipe: Codec['pipe'] = this.ioTsCodec.pipe; | ||
public _A: Codec['_A'] = this.ioTsCodec._A; | ||
public _O: Codec['_O'] = this.ioTsCodec._O; | ||
public _I: Codec['_I'] = this.ioTsCodec._I; | ||
public asDecoder: Codec['asDecoder'] = this.ioTsCodec.asDecoder; | ||
public asEncoder: Codec['asEncoder'] = this.ioTsCodec.asEncoder; | ||
public assertDecode = (value: unknown): Codec['_A'] => { | ||
const result = this.ioTsCodec.decode(value); | ||
if (result._tag === 'Left') throw new EnGardeAssertionError(result.left); | ||
return result.right; | ||
}; | ||
public get optional() { | ||
return new Codec(t.union([this.ioTsCodec, t.undefined])); | ||
} | ||
} | ||
type InterfaceTypeWithOptionalKeys<P extends t.Props> = t.InterfaceType< | ||
P, | ||
EnableOptionalKeys<{ [K in keyof P]: t.TypeOf<P[K]> }>, | ||
EnableOptionalKeys<{ [K in keyof P]: t.OutputOf<P[K]> }>, | ||
unknown | ||
>; | ||
export class ObjectCodec< | ||
P extends t.Props, | ||
CodecWithOptionalKeys extends t.InterfaceType< | ||
any, | ||
any, | ||
any, | ||
any | ||
> = InterfaceTypeWithOptionalKeys<P> | ||
> { | ||
constructor(public ioTsCodec: t.TypeC<P>) {} | ||
public name = this.ioTsCodec.name; | ||
public is: CodecWithOptionalKeys['is'] = this.ioTsCodec.is; | ||
public encode: CodecWithOptionalKeys['encode'] = this.ioTsCodec.encode; | ||
public decode: CodecWithOptionalKeys['decode'] = this.ioTsCodec.decode; | ||
public validate: CodecWithOptionalKeys['validate'] = this.ioTsCodec.validate; | ||
public pipe: CodecWithOptionalKeys['pipe'] = this.ioTsCodec.pipe; | ||
public _A: CodecWithOptionalKeys['_A'] = this.ioTsCodec._A; | ||
public _O: CodecWithOptionalKeys['_O'] = this.ioTsCodec._O; | ||
public _I: CodecWithOptionalKeys['_I'] = this.ioTsCodec._I; | ||
public asDecoder: CodecWithOptionalKeys['asDecoder'] = this.ioTsCodec | ||
.asDecoder; | ||
public asEncoder: CodecWithOptionalKeys['asEncoder'] = this.ioTsCodec | ||
.asEncoder; | ||
public assertDecode = (value: unknown): CodecWithOptionalKeys['_A'] => { | ||
const result = this.ioTsCodec.decode(value); | ||
if (result._tag === 'Left') throw new EnGardeAssertionError(result.left); | ||
return result.right; | ||
}; | ||
public get optional() { | ||
return new Codec(t.union([this.ioTsCodec, t.undefined])); | ||
} | ||
readonly _tag: 'InterfaceType' = 'InterfaceType'; | ||
public props: CodecWithOptionalKeys['props'] = this.ioTsCodec.props; | ||
} | ||
const primitiveCodecs = { | ||
string: new Codec(t.string), | ||
number: new Codec(t.number), | ||
boolean: new Codec(t.boolean), | ||
null: new Codec(t.null), | ||
undefined: new Codec(t.undefined), | ||
any: new Codec(t.any), | ||
unknown: new Codec(t.unknown) | ||
}; | ||
const eg = { | ||
string: addOptionalToGuard(primitiveGuards.string), | ||
number: addOptionalToGuard(primitiveGuards.number), | ||
boolean: addOptionalToGuard(primitiveGuards.boolean), | ||
null: addOptionalToGuard(primitiveGuards.null), | ||
/** | ||
* Accepts a guard and returns a guard that checks that the value is an | ||
* array and that each element in the array matches the given guard. | ||
*/ | ||
arrayOf: addOptionalToHigherOrderGuard(higherOrderGuards.arrayOf), | ||
/** | ||
* Accepts any number of guards and returns a type guard that will only | ||
* match if the given value is a tuple where each value matches the guard | ||
* in that same position. | ||
* | ||
* @example | ||
* const isStringStringNumber = eg.tuple( | ||
* eg.string, | ||
* eg.string, | ||
* eg.number, | ||
* ) | ||
* | ||
* if (isStringStringNumber(["hi", "there", 4])) { | ||
* // ... | ||
* } | ||
*/ | ||
tuple: addOptionalToHigherOrderGuard(higherOrderGuards.tuple), | ||
/** | ||
* Accepts an object definition where each key must be a guard. Returns | ||
* a guard that checks that a given value is an object and that each key's | ||
* value passes the definition's guard. Fails if any keys exist on the given | ||
* value that do not exist on the object definition. | ||
*/ | ||
exact: addOptionalToHigherOrderGuard(higherOrderGuards.exact), | ||
/** | ||
* Accepts a constructor and returns a guard that checks whether a value is | ||
* an instance of that constructor using `instanceof` | ||
*/ | ||
instanceOf: addOptionalToHigherOrderGuard(higherOrderGuards.instanceOf), | ||
/** | ||
* Accepts an object definition where each key must be a guard. Returns | ||
* a guard that checks that a given value is an object and that each key's | ||
* value passes the definition's guard | ||
*/ | ||
object: addOptionalToHigherOrderGuard(higherOrderGuards.object), | ||
/** | ||
* Accepts a type guard and returns an index signature where values must | ||
* pass the type guard. | ||
*/ | ||
objectWithValuesOf: addOptionalToHigherOrderGuard( | ||
higherOrderGuards.objectWithValuesOf | ||
), | ||
/** | ||
* Accepts any number of guards as arguments. Returns a guard that checks if | ||
* a given value passes any of the input guards. | ||
*/ | ||
oneOf: addOptionalToHigherOrderGuard(higherOrderGuards.oneOf), | ||
/** | ||
* Accepts any number of guards as arguments. Returns a guard that checks if | ||
* a given value passes all of the input guards. | ||
*/ | ||
all: addOptionalToHigherOrderGuard(higherOrderGuards.all), | ||
/** | ||
* Accepts primitives and infers their types. Returns a guard that checks | ||
* whether a value matches any of the literal values. | ||
*/ | ||
oneOfLiterals: addOptionalToHigherOrderGuard(higherOrderGuards.oneOfLiterals), | ||
const wrapHigherOrderCodec = <Params extends any[], Codec extends IoTsCodec>( | ||
higherOrderCodec: (...hocParams: Params) => Codec | ||
) => (...hocParams: Params) => new Codec(higherOrderCodec(...hocParams)); | ||
// These guards do not require optional decoration | ||
any: primitiveGuards.any, | ||
unknown: primitiveGuards.unknown, | ||
undefined: primitiveGuards.undefined | ||
type NonEmpty<T extends any[]> = Exclude<T, never[]>; | ||
const higherOrderCodecs = { | ||
// Using a special type for object which enables optional keys | ||
object: <P extends t.Props>(p: P, name?: string) => | ||
new ObjectCodec(t.type(p, name)), | ||
tuple: <Codecs extends never[] | [IoTsCodec, ...IoTsCodec[]]>( | ||
codecs: Codecs, | ||
name?: string | ||
): Codec<t.TupleC<NonEmpty<Codecs>>> => | ||
new Codec(t.tuple(codecs as any, name) as any), | ||
intersection: <Codecs extends [IoTsCodec, IoTsCodec, ...IoTsCodec[]]>( | ||
codecs: Codecs, | ||
name?: string | ||
): Codec<t.IntersectionC<Codecs>> => | ||
new Codec(t.intersection(codecs as any, name) as any), | ||
// The types for these higher order codecs work with simple hoc wrapper | ||
array: wrapHigherOrderCodec(t.array), | ||
union: wrapHigherOrderCodec(t.union), | ||
record: wrapHigherOrderCodec(t.record), | ||
partial: wrapHigherOrderCodec(t.partial), | ||
literal: wrapHigherOrderCodec(t.literal), | ||
exact: wrapHigherOrderCodec(t.exact) | ||
}; | ||
export default eg; | ||
export { | ||
TypeGuard, | ||
TypeFromGuard, | ||
TypeGuardWithOptional, | ||
primitiveGuards, | ||
higherOrderGuards, | ||
TypeGuardError, | ||
assertShape, | ||
addOptionalToGuard, | ||
addOptionalToHigherOrderGuard | ||
export type TypeFromCodec<T extends IoTsCodec> = t.TypeOf<T>; | ||
export const eg = { | ||
...primitiveCodecs, | ||
...higherOrderCodecs | ||
}; | ||
export { t }; |
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
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
0
39124
2
7
566
1
+ Addedfp-ts@^2.1.1
+ Addedio-ts@^2.0.1
+ Addedfp-ts@2.16.9(transitive)
+ Addedio-ts@2.2.22(transitive)