UNPKG

io-ts-extra

Version:

Adds pattern matching, optional properties, and several other helpers and types, to io-ts.

137 lines (131 loc) 4.36 kB
import * as t from 'io-ts' import {RegExpCodec, regexp} from './combinators' export type ShorthandPrimitive = typeof String | typeof Number | typeof Boolean export type ShorthandLiteral = string | number | boolean | null | undefined export type ShorthandInput = | ShorthandPrimitive | ShorthandLiteral | RegExp | typeof Array | typeof Object | [ShorthandInput] | [1, [ShorthandInput]] | [2, [ShorthandInput, ShorthandInput]] | [3, [ShorthandInput, ShorthandInput, ShorthandInput]] | [4, [ShorthandInput, ShorthandInput, ShorthandInput, ShorthandInput]] | {[K in string]: ShorthandInput} | t.Type<any, any, any> export type Shorthand<V extends ShorthandInput> = V extends string | number | boolean ? t.LiteralC<V> : V extends null ? t.NullC : V extends undefined ? t.UndefinedC : V extends typeof String ? t.StringC : V extends typeof Number ? t.NumberC : V extends typeof Boolean ? t.BooleanC : V extends typeof Array ? t.UnknownArrayC : V extends typeof Object ? t.ObjectC : V extends RegExp ? RegExpCodec : V extends [ShorthandInput] ? t.ArrayC<Shorthand<V[0]>> : V extends [1, [ShorthandInput]] ? t.TupleC<[Shorthand<V[1][0]>]> : V extends [2, [ShorthandInput, ShorthandInput]] ? t.TupleC<[Shorthand<V[1][0]>, Shorthand<V[1][1]>]> : V extends [3, [ShorthandInput, ShorthandInput, ShorthandInput]] ? t.TupleC<[Shorthand<V[1][0]>, Shorthand<V[1][1]>, Shorthand<V[1][2]>]> : V extends [4, [ShorthandInput, ShorthandInput, ShorthandInput, ShorthandInput]] ? t.TupleC<[Shorthand<V[1][0]>, Shorthand<V[1][1]>, Shorthand<V[1][2]>, Shorthand<V[1][3]>]> : V extends t.Type<any, any, any> ? V : V extends Record<string, any> ? t.TypeC<{[K in keyof V]: Shorthand<V[K]>}> : never export type CodecFromShorthand = { (): t.UnknownC <V extends ShorthandInput>(v: V): Shorthand<V> } /* eslint-disable complexity */ /** * Gets an io-ts codec from a shorthand input: * * |shorthand|io-ts type| * |-|-| * |`String`, `Number`, `Boolean`|`t.string`, `t.number`, `t.boolean`| * |Literal raw strings, numbers and booleans e.g. `7` or `'foo'`|`t.literal(7)`, `t.literal('foo')` etc.| * |Regexes e.g. `/^foo/`|see [regexp](#regexp)| * |`null` and `undefined`|`t.null` and `t.undefined`| * |No input (_not_ the same as explicitly passing `undefined`)|`t.unknown`| * |Objects e.g. `{ foo: String, bar: { baz: Number } }`|`t.type(...)` e.g. `t.type({foo: t.string, bar: t.type({ baz: t.number }) })` * |`Array`|`t.unknownArray`| * |`Object`|`t.object`| * |One-element arrays e.g. `[String]`|`t.array(...)` e.g. `t.array(t.string)`| * |Tuples with explicit length e.g. `[2, [String, Number]]`|`t.tuple` e.g. `t.tuple([t.string, t.number])`| * |io-ts codecs|unchanged| * |Unions, intersections, partials, tuples with more than 3 elements, and other complex types|not supported, except by passing in an io-ts codec| */ export const codecFromShorthand: CodecFromShorthand = (...args: unknown[]): any => { if (args.length === 0) { return t.unknown } const v = args[0] if (v === String) { return t.string } if (v === Number) { return t.number } if (v === Boolean) { return t.boolean } if (v === Array) { return t.UnknownArray } if (v === Object) { return t.object } if (v === null) { return t.null } if (typeof v === 'undefined') { return t.undefined } if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean') { return t.literal(v) } if (v instanceof RegExp) { return regexp(v) } if (Array.isArray(v) && v.length === 0) { return t.array(t.unknown) } if (Array.isArray(v) && v.length === 1) { return t.array(codecFromShorthand(v[0])) } if (Array.isArray(v) && v.length === 2 && typeof v[0] === 'number' && Array.isArray(v[1])) { return t.tuple(v[1].map(codecFromShorthand) as any) } if (Array.isArray(v)) { throw new TypeError( `Invalid type. Arrays should be in the form \`[shorthand]\`, and tuples should be in the form \`[3, [shorthand1, shorthand2, shorthand3]]\`` ) } if (v instanceof t.Type) { return v } if (typeof v === 'object' && v) { return t.type( Object.entries(v).reduce((acc, [prop, val]) => { return {...acc, [prop]: codecFromShorthand(val)} }, {}) ) } return t.unknown }