Options
All
  • Public
  • Public/Protected
  • All
Menu

Class Decoder<T>

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)
}

Type parameters

  • T

Hierarchy

  • Decoder

Index

Constructors

Private constructor

Properties

Private decoder

decoder: (data: any) => Result<T, DecodeError>

Type declaration

Static any

any: Decoder<unknown> = new Decoder((data) => Result.ok(data))

A decoder that accepts anything.

Static boolean

boolean: Decoder<boolean> = new Decoder((data) => {switch (typeof data) {case 'boolean':return Result.ok(data);case 'string':switch (data) {case 'true':return Result.ok(true);case 'false':return Result.ok(false);default:return Result.fail(makeSingleError('Not a boolean', data));}default: {return Result.fail(makeSingleError('Not a boolean', data));}}})

Create a decoder for booleans.

Example:

Decoder.boolean.run(true) // succeeds
Decoder.boolean.run('false') // succeeds
Decoder.boolean.run(1) // fails

Static buffer

buffer: Decoder<Buffer> = new Decoder((data) =>Buffer.isBuffer(data)? Result.ok(data): Result.fail(makeSingleError('Not Buffer', data)))

A decoder that accepts a Buffer.

Example:

Decoder.buffer.run(undefined) // FAIL
Decoder.buffer.run(5) // FAIL
Decoder.buffer.run(Buffer.from('Hello world')) // OK
Decoder.buffer.run(<Buffer 68 65 6c 6c 6f>) // OK

Static date

date: Decoder<Date> = Decoder.oneOf([Decoder.timestamp.map((n) => new Date(n)),Decoder.isoDate,])

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 isoDate

isoDate: Decoder<Date> = new Decoder((data: any) => {switch (Object.prototype.toString.call(data)) {case '[object Date]':if (isDate(data)) return Result.ok(data);return Result.fail(makeSingleError('Badly formatted date object', data));case '[object String]':return isISO(data)? Result.ok(new Date(data)): Result.fail(makeSingleError(`Not a ISO date`, data));default:return Result.fail(makeSingleError(`Not a ISO date`, data));}})

A decoder for iso dates. Use Decoder.date to also support timestamps.

Example:

Decoder.isoDate.run(new Date()) // OK
Decoder.isoDate.run("abra") // FAIL
Decoder.isoDate.run("2020-01-13T18:27:35.817Z") // OK
Decoder.isoDate.run(123) // FAIL
Decoder.isoDate.run("Mon, 13 Jan 2020 18:28:05 GMT") // FAIL, format is not supported

Static null

null: Decoder<null> = new Decoder((data) =>data === null? Result.ok(data): Result.fail(makeSingleError('Not null', data)))

A decoder that accepts null.

Example:

Decoder.null.run(undefined) // FAIL
Decoder.null.run(5) // FAIL
Decoder.null.run(null) // OK

Static number

number: Decoder<number> = new Decoder((data: any) => {switch (typeof data) {case 'number':if (!isNaN(data)) return Result.ok(data);return Result.fail(makeSingleError('Not a number', data));case 'string':if (isStringNumber(data)) return Result.ok(parseFloat(data));return Result.fail(makeSingleError('Not a number', data));default:return Result.fail(makeSingleError('Not a number', data));}})

A decoder for numbers.

Example:

Decoder.number.run(5) // OK
Decoder.number.run('5') // OK
Decoder.number.run('hi') // FAIL

Static string

string: Decoder<string> = new Decoder((data: any) =>typeof data === 'string'? Result.ok(data): Result.fail(makeSingleError('Not a string', data)))

Decodes a string.

Example:

Decoder.string.run('hi') // OK
Decoder.string.run(5) // Fail

Static timestamp

timestamp: Decoder<number> = Decoder.number.satisfy({predicate: isInteger,failureMessage: 'Not a timestamp',})

A decoder for timestamps.

Example:

Decoder.timestamp.run(123) // OK (Timestamp)
Decoder.timestamp.run(new Date()) // FAIL
Decoder.timestamp.run("abra") // FAIL
Decoder.timestamp.run("2020-01-13T18:27:35.817Z") // FAIL
Decoder.timestamp.run("Mon, 13 Jan 2020 18:28:05 GMT") // FAIL

Static undefined

undefined: Decoder<undefined> = new Decoder((data) =>data === undefined? Result.ok(data): Result.fail(makeSingleError('Not undefined', data)))

A decoder that accepts undefined.

Example:

Decoder.undefined.run(null) // FAIL
Decoder.undefined.run(5) // FAIL
Decoder.undefined.run(undefined) // OK

Methods

default

  • default<E>(value: T | E): Decoder<T | E>
  • 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

    Type parameters

    • E

    Parameters

    • value: T | E

    Returns Decoder<T | E>

guard

  • guard(data: any): T
  • Run a decoder on data. It will either succeed and yield the value or throw an ValidationFailed error

    Parameters

    • data: any

    Returns T

map

  • map<S>(mapFunction: (arg: T) => S): Decoder<S>
  • Transform a decoder from T to S.

    Example:

    const setDecoder: Decoder<Set<number>> =
         Decoder.array(Decoder.number).map(numberArray => new Set(numberArray))

    Type parameters

    • S

    Parameters

    • mapFunction: (arg: T) => S
        • (arg: T): S
        • Parameters

          • arg: T

          Returns S

    Returns Decoder<S>

run

  • run(data: any): { type: "OK"; value: T } | { error: E; type: "FAIL" }
  • Run a decoder on data. The result is a discriminated union. *

    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)
    }

    Parameters

    • data: any

    Returns { type: "OK"; value: T } | { error: E; type: "FAIL" }

satisfy

  • satisfy(__namedParameters: { failureMessage: undefined | string; predicate: (arg: T) => boolean }): Decoder<T>
  • 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

    Parameters

    • __namedParameters: { failureMessage: undefined | string; predicate: (arg: T) => boolean }
      • failureMessage: undefined | string
      • predicate: (arg: T) => boolean
          • (arg: T): boolean
          • Parameters

            • arg: T

            Returns boolean

    Returns Decoder<T>

sequence

  • sequence(array: unknown[]): { failed: unknown[]; successful: T[] }
  • 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"]}

    Parameters

    • array: unknown[]

    Returns { failed: unknown[]; successful: T[] }

    • failed: unknown[]
    • successful: T[]

then

  • 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);

    Type parameters

    • S

    Parameters

    Returns Decoder<S>

Static array

  • 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

    Type parameters

    • T

    Parameters

    Returns Decoder<T[]>

Static Private createOneOf

Static dict

  • 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

    Type parameters

    • T

    Parameters

    Returns Decoder<{}>

Static fail

  • fail<T>(message: string): Decoder<T>
  • Create a decoder that always fails with a message.

    Type parameters

    • T

    Parameters

    • message: string

    Returns Decoder<T>

Static field

  • 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

    Type parameters

    • T

    Parameters

    Returns Decoder<T>

Static literalNumber

  • literalNumber<T>(number: T): Decoder<T>
  • 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

    Type parameters

    • T: number

    Parameters

    • number: T

    Returns Decoder<T>

Static literalString

  • literalString<T>(str: T): Decoder<T>
  • 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

    Type parameters

    • T: string

    Parameters

    • str: T

    Returns Decoder<T>

Static object

  • object<T>(object: {}): Decoder<T>
  • Create a decoder for a type T.

    Argument "object" is a Mapped type, an object containing only decoders for each field.

    {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'})

    Type parameters

    • T

    Parameters

    • object: {}

    Returns Decoder<T>

Static ok

  • Create a decoder that always suceeds and returns T.

    Type parameters

    • T

    Parameters

    • value: T

    Returns Decoder<T>

Static oneOf

  • oneOf<T>(decoders: T[]): Decoder<T extends Decoder<infer R> ? R : never>
  • 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

    Type parameters

    Parameters

    • decoders: T[]

    Returns Decoder<T extends Decoder<infer R> ? R : never>

Static optional

  • 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

    Type parameters

    • T

    Parameters

    Returns Decoder<T | undefined>

Generated using TypeDoc