decoders

Utilities

Utility decoders for custom logic, preprocessing, and advanced use cases.

unknown

unknown: Decoder<unknown> (source)
anything: Decoder<unknown> alias

Accepts anything and returns it unchanged.

Useful for situation in which you don't know or expect a specific type. Of course, the downside is that you won't know the type of the value statically and you'll have to further refine it yourself.

Try it
unknown.verify(input)
InputResult
"hello"
false
undefined
[1, 2]

always

function always( value: T ): Decoder<T> (source)
function always( value: () => T ): Decoder<T>

Accepts any input, ignores it, and instead returns the provided value instead.

This is useful to manually add extra fields to object decoders.

Try it
always(42).verify(input)
InputResult
42
42
42

Or use it with a function instead of a constant:

Try it
always(() => Math.random().toFixed(2)).verify(input)
InputResult
"0.27"
"0.69"

never

never: Decoder<never> (source)
fail: Decoder<never> alias

Rejects all inputs, and always fails with the given error message. May be useful for explicitly disallowing keys, or for testing purposes.

Try it
never('Computer says no').verify(input)
InputResult
"hello" ^^^^^^^ Computer says no
42 ^^ Computer says no
true ^^^^ Computer says no
null ^^^^ Computer says no
undefined ^^^^^^^^^ Computer says no

instanceOf

function instanceOf( klass: Klass<T> ): Decoder<T> (source)

Accepts any value that is an instanceof the given class.

Try it
instanceOf(Error).verify(input)
InputResult
Error { "foo" }
"foo" ^^^^^ Must be Error instance
3 ^ Must be Error instance

lazy

function lazy( decoderFn: () => Decoder<T> ): Decoder<T> (source)

Lazily evaluate the given decoder. Useful to build recursive decoders.

Try it
// A recursive decoderconst treeDecoder = object({  value: string,  children: array(lazy(() => treeDecoder)),});treeDecoder.verify(input)
InputResult
{ "value": "root", } ^ Missing key: 'children'
"not a tree" ^^^^^^^^^^^^ Must be an object

sized

function sized( decoder: Decoder<string>, options: SizeOptions ): Decoder<string> (source)
function sized( decoder: Decoder<T[]>, options: SizeOptions ): Decoder<T[]>
function sized( decoder: Decoder<Set<T>>, options: SizeOptions ): Decoder<Set<T>>

Available since 2.9.

Rejects strings, arrays, or sets that don't match the given size constraints. Works on any type that has a .length or .size property.

type SizeOptions =
  | { size: number }
  | { min: number; max?: number }
  | { min?: number; max: number };

For strings, error messages refer to "chars". For arrays and sets, error messages refer to "items".

Try it
sized(string, { min: 2, max: 10 }).verify(input)
InputResult
"hello"
"hi"
"x" ^^^ Too short, must be at least 2 chars
"this string is too long" ^^^^^^^^^^^^^^^^^^^^^^^^^ Too long, must be at most 10 chars
42 ^^ Must be string

define

function define( fn: (blob: unknown, ok, err) => DecodeResult<T> ): Decoder<T> (source)

Defines a new Decoder<T>, by implementing a custom acceptance function. The function receives three arguments:

  1. blob - the raw/unknown input (aka your external data)
  2. ok - Call ok(value) to accept the input and return value
  3. err - Call err(message) to reject the input with error message

The expected return value should be a DecodeResult<T>, which can be obtained by returning the result of calling the provided ok or err helper functions. Please note that ok() and err() don't perform side effects! You'll need to return those values.

You probably don't need this!

This is the lowest-level API to define a new decoder, and not recommended unless you have no alternative. In almost all cases starting from an existing decoder and using .transform() or .refine() will be preferable.

//
// NOTE: This example just shows how define() works.
// Please do NOT implement an uppercase decoder like this! 🙈
//
const uppercase = define<string>((blob, ok, err) => {
  if (typeof blob === 'string') {
    // Accept the input
    return ok(blob.toUpperCase());
  } else {
    // Reject the input
    return err('Must be string');
  }
});
// 👍
uppercase.verify('hi there') === 'HI THERE';

// 👎
uppercase.verify(123); // throws: Must be string

prep

function prep( mapperFn: (raw: unknown) => unknown, decoder: Decoder<T> ): Decoder<T> (source)

Pre-process the data input before passing it into the decoder. This gives you the ability to arbitrarily customize the input on the fly before passing it to the decoder. Of course, the input value at that point is still of unknown type, so you will have to deal with that accordingly.

const decoder = prep(
  // Will convert any input to an int first, before feeding it to
  // positiveInteger. This will effectively also allow numeric strings
  // to be accepted (and returned) as integers. If this ever throws,
  // then the error message will be what gets annotated on the input.
  (x) => parseInt(x),
  positiveInteger,
);

// 👍
decoder.verify(42) === 42;
decoder.verify('3') === 3;

// 👎
decoder.verify('-3'); // throws: not a positive number
decoder.verify('hi'); // throws: not a number

On this page