UNPKG

elm-decoders

Version:

A powerful, well tested, data decoder for Typescript.

403 lines (402 loc) 12.4 kB
/// <reference types="node" /> import { DecodeError } from './error'; export { DecodeError }; export declare class ValidationFailedError extends Error { error: DecodeError; constructor(error: DecodeError); } /** * Decode data and check it's validity using Decoder. Useful when you want to * check that data from clients or other outgoing sources is valid. * * To create a decoder, use one of the primitive decoders provided as a static method. * Then call it's deocde method on the data you want to decode. * * ``` * const idDecoder<{id: string}> = Decoder.object({id: Decoder.string}) * * idDecoder.run("2913088") // Failure, must have a field id. * * const result = idDecoder.run({id: 2913088}) // OK * * // To access the result value * switch(result.type) { * case "OK": * doThingWithId(result.value) * case "FAIL": * // Or if it fails you can find the reason by accessing error * throw new Error(result.error) * } * ``` * */ export declare class Decoder<T> { private decoder; /** * Transform a decoder from T to S. * * Example: * ``` * const setDecoder: Decoder<Set<number>> = * Decoder.array(Decoder.number).map(numberArray => new Set(numberArray)) * ``` */ map: <S>(mapFunction: (arg: T) => S) => Decoder<S>; /** * Sets a default value to the decoder if it fails. * * ``` * const nrDecoder = Decoder.number.default(0) * * nrDecoder.run(5) // OK 5 * nrDecoder.run('hi') // OK 0 * ``` */ default: <E>(value: T | E) => Decoder<T | E>; private static createOneOf; /** * Attempt multiple decoders in order until one succeeds. The type signature is informally: * ``` * oneOf = (decoders: [Decoder<A>, Decoder<B>, ...]) => Decoder<A | B | ...> * ``` * * Example: * ``` * const enums: Decoder<"Jack" | "Sofia"> = Decoder.oneOf([ * Decoder.literalString("Jack"), * Decoder.literalString("Sofia") * ]) * enums.run("Jack") // OK * enums.run("Sofia") // OK * enums.run("Josefine") // Fail * ``` */ static oneOf: <T_1 extends Decoder<any>>(decoders: T_1[]) => Decoder<T_1 extends Decoder<infer R> ? R : never>; private constructor(); /** * Run a decoder on data. The result is a [discriminated union](https://www.typescriptlang.org/docs/handbook/advanced-types.html#discriminated-unions). * * * Example: * ``` * const userCredentials: Decoder<Credentials> = Decoder.object({...}) * //... Somewhere else * const result = userCredentials.run(request.query) * switch(result.type) { * case "OK": * login(result.value) * case "FAIL": * throw new Error(result.error) * } * ``` */ run: (data: any) => { type: "OK"; value: T; } | { type: "FAIL"; error: DecodeError; }; /** * Run a decoder on data. It will either succeed and yield the value or throw * an ValidationFailed error */ guard: (data: any) => T; /** * Create decoders that is dependent on previous results. * * Example: * ``` * const version = Decoder.field('version, Decoder.number) * const api = ({ version }: { version: number }): Decoder<{...}> => { * switch (version) { * case 0: * return myFirstDecoder; * case 1: * return mySecondDecoder; * default: * return Decoder.fail('Version ${version} not supported') * } * }; * const versionedApi = version.then(api); * ``` */ then: <S>(dependentDecoder: (res: T) => Decoder<S>) => Decoder<S>; /** * Add an extra predicate to the decoder. Optionally add a failure message * that overrides the earlier failure message. * * Example: * ``` * const naturalNumber = Decoder.number.satisfy({ * predicate: n => n>0 * failureMessage: `Not a natural number` * }) * naturalNumber.run(5) // OK, 5 * naturalNumber.run(-1) // FAIL, Not a natural number * ``` */ satisfy: ({ predicate, failureMessage, }: { predicate: (arg: T) => boolean; failureMessage?: string | undefined; }) => Decoder<T>; /** * A decoder for numbers. * * Example: * ``` * Decoder.number.run(5) // OK * Decoder.number.run('5') // OK * Decoder.number.run('hi') // FAIL * ``` */ static number: Decoder<number>; /** * A decoder for iso dates. Use `Decoder.date` to also support timestamps. * * Example: * ``` * Decoder.date.run(new Date()) // OK * Decoder.date.run("abra") // FAIL * Decoder.date.run("2020-01-13T18:27:35.817Z") // OK * Decoder.date.run(123) // FAIL * Decoder.date.run("Mon, 13 Jan 2020 18:28:05 GMT") // FAIL, format is not supported * ``` */ static isoDate: Decoder<Date>; /** * A decoder for timestamps. * * Example: * ``` * Decoder.date.run(123) // OK (Timestamp) * Decoder.date.run(new Date()) // FAIL * Decoder.date.run("abra") // FAIL * Decoder.date.run("2020-01-13T18:27:35.817Z") // FAIL * Decoder.date.run("Mon, 13 Jan 2020 18:28:05 GMT") // FAIL * ``` */ static timestamp: Decoder<number>; /** * A decoder for dates. Decoding UTC time that is formatted using * `toUTCString()` is not supported; Javascript's date parser parses UTC strings * wrong. * * Example: * ``` * Decoder.date.run(123) // OK (Timestamp) * Decoder.date.run(new Date()) // OK * Decoder.date.run("abra") // FAIL * Decoder.date.run("2020-01-13T18:27:35.817Z") // OK * Decoder.date.run("Mon, 13 Jan 2020 18:28:05 GMT") // FAIL, format is not supported * ``` */ static date: Decoder<Date>; /** * A decoder that accepts undefined. * * Example: * ``` * Decoder.undefined.run(null) // FAIL * Decoder.undefined.run(5) // FAIL * Decoder.undefined.run(undefined) // OK *``` */ static undefined: Decoder<undefined>; /** * A decoder that accepts null. * * Example: * ``` * Decoder.null.run(undefined) // FAIL * Decoder.null.run(5) // FAIL * Decoder.null.run(null) // OK *``` */ static null: Decoder<null>; /** * A decoder that accepts a Buffer. * * Example: * ``` * Decoder.null.run(undefined) // FAIL * Decoder.null.run(5) // FAIL * Decoder.null.run(Buffer.from('Hello world')) // OK * Decoder.null.run(<Buffer 68 65 6c 6c 6f>) // OK *``` */ static buffer: Decoder<Buffer>; /** * Decodes a string. * * Example: * ``` * Decoder.string.run('hi') // OK * Decoder.string.run(5) // Fail * ``` */ static string: Decoder<string>; /** * Decodes the exact string and sets it to a string literal type. Useful for * parsing unions. * * Example: * ``` * const jackOrSofia: Decoder<'Jack' | 'Sofia'> = Decoder.oneOf([ * Decoder.literalString('Jack'), * Decoder.literalString('Sofia') * ]) * jackOrSofia.run('Jack') // OK * jackOrSofia.run('Josephine') // FAIL * ``` */ static literalString: <T_1 extends string>(str: T_1) => Decoder<T_1>; /** * Takes a decoder and returns an optional decoder. * * Example: * ``` * const optionalNumber = Decoder.optional(Decoder.number) * optionalNumber.run(5) //OK * optionalNumber.run(undefined) //OK * optionalNumber.run(null) //OK * optionalNumber.run('hi') //FAIL * ``` */ static optional: <T_1>(decoder: Decoder<T_1>) => Decoder<T_1 | undefined>; /** * Create a decoder that always fails with a message. */ static fail: <T_1>(message: string) => Decoder<T_1>; /** * Create a decoder that always suceeds and returns T. */ static ok: <T_1>(value: T_1) => Decoder<T_1>; /** * Decodes the exact number and sets it to a number literal type. * * Example: * ``` * const versionDecoder: Decoder<1 | 2> = Decoder.oneOf([ * Decoder.literalNumber(1), * Decoder.literalNumber(2) * ]) * * versionDecoder.run(1) // OK * versionDecoder.run(3) // FAIL * ``` */ static literalNumber: <T_1 extends number>(number: T_1) => Decoder<T_1>; /** * Create an array decoder given a decoder for the elements. * * Example: * ``` * Decoder.array(Decoder.string).run(['hello','world']) // OK * Decoder.array(Decoder.string).run(5) // Fail * ``` */ static array: <T_1>(decoder: Decoder<T_1>) => Decoder<T_1[]>; /** * Create a decoder for booleans. * * Example: * ``` * Decoder.boolean.run(true) // succeeds * Decoder.boolean.run('false') // succeeds * Decoder.boolean.run(1) // fails * ``` */ static boolean: Decoder<boolean>; /** * Decode the value of a specific key in an object using a given decoder. * * Example: * ``` * const versionDecoder = Decoder.field("version", Decoder.number) * * versionDecoder.run({version: 5}) // OK, 5 * versionDecoder.run({name: "hi"}) // fail * ``` * */ static field: <T_1>(key: string, decoder: Decoder<T_1>) => Decoder<T_1>; /** * A decoder that accepts anything. */ static any: Decoder<unknown>; /** * run a decoder on each item in an array, collecting the amount of successful and failed decodings. * * ``` * example: * * Decoder.number.sequence([1,3,4,"hello"]) // => {successful: [1,2,3], failed: ["hello"]} * ``` */ sequence: (array: unknown[]) => { successful: T[]; failed: unknown[]; }; /** * Decode values of an object where the keys are unknown. * * Example: * ``` * const userDecoder = Decoder.object({age: Decoder.number}) * const usersDecoder = Decoder.dict(userDecoder) * * usersDecoder.run({emelie: {age: 32}, bob: {age: 50}}) // OK, {emelie: {age: 32}, bob: {age: 50}} * usersDecoder.run({name: 'emelie', age: 32}) // fail * ``` * */ static dict: <T_1>(decoder: Decoder<T_1>) => Decoder<{ [key: string]: T_1; }>; /** * Create a decoder for a type T. * * Argument "object" is a [Mapped * type](https://www.typescriptlang.org/docs/handbook/advanced-types.html#mapped-types), * an object containing only decoders for each field. * ```typescript * {name: Decoder.string} // Ok parameter * {name: Decoder.string, email: 'email@email'} // Type error, email must be decoder * ``` * * Example: * ``` * interface User { * name: string * email: string * } * * // typechecks * const decodeUser: Decoder<User> = Decoder.object({name: Decoder.string, email: Decoder.email}) * decodeUser.run({name: "Jenny", email: "fakemail@fake.com"}) // OK * decodeUser.run({nm: "Bad"}) // FAIL * * // will not typecheck, object must have the same field names as user and * // only contain decoders. * const decodeUser: Decoder<User> = Decoder.object({nem: 'Hi'}) * ``` * */ static object: <T_1>(object: { [K in keyof T_1]: Decoder<T_1[K]>; }) => Decoder<T_1>; } /** * Deduce the return type of a decoder. If there is a deep nesting of objects * the type will be inferred but will not display correctly in the signature. * * Example: * ``` * const paramDecoder = Decoder.object({ * body: userDecoder * }) * const handleRequest = (params: DecodedValue<typeof paramDecoder>) => { * // params.body is inferred * } * ``` * */ export declare type DecodedValue<T> = T extends Decoder<infer U> ? U : never;