elm-decoders
Version:
A powerful, well tested, data decoder for Typescript.
403 lines (402 loc) • 12.4 kB
TypeScript
/// <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;