decoders

Objects

Object decoders for validating structured data.

Object shapes

object

function object(
  { field1: Decoder<A>, field2: Decoder<B>, ... }
): Decoder<{ field1: A, field2: B, ... }> (source)

Accepts objects with fields matching the given decoders. Extra fields that exist on the input object are ignored and will not be returned.

Try it
object({ x: number, y: number }).verify(input)
InputResult
{ "x": 1, "y": 2 }
{ "x": 1, "y": 2 }
{ "x": 1, } ^ Missing key: 'y'

Read more here about the differences between object, exact, and inexact.


exact

function exact(
  { field1: Decoder<A>, field2: Decoder<B>, ... }
): Decoder<{ field1: A, field2: B, ... }> (source)

A "strict object" decoder. Like object(), but will reject inputs that contain extra fields that are not specified explicitly.

Try it
exact({ x: number, y: number }).verify(input)
InputResult
{ "x": 1, "y": 2 }
{ "x": 1, "y": 2, "z": 3, } ^ Unexpected extra keys: 'z'
{ "x": 1, } ^ Missing key: 'y'

Read more here about the differences between object, exact, and inexact.


inexact

function inexact(
  { field1: Decoder<A>, field2: Decoder<B>, ... }
): Decoder<{ field1: A, field2: B, ... }> (source)

A "loose object" decoder. Like object(), but will pass through any extra fields on the input object unvalidated that will thus be of unknown type statically.

The one exception is a __proto__ key, which cannot be passed through safely. See Unsafe keys.

Try it
inexact({ x: number, y: number }).verify(input)
InputResult
{ "x": 1, "y": 2 }
{ "x": 1, "y": 2, "z": 3 }
{ "x": 1, } ^ Missing key: 'y'

Read more here about the differences between object, exact, and inexact.


What's the difference?

The three decoders in the "object" family are very similar and differ only in how they treat extra properties on input values.

Try it
const fields = {  x: number,  y: number,};object(fields).verify(input)exact(fields).verify(input)inexact(fields).verify(input)
Inputobjectexactinexact
{ "x": 1, "y": 2 }{ "x": 1, "y": 2, "z": 3, } ^ Unexpected extra keys: 'z'{ "x": 1, "y": 2, "z": 3 }
{ "x": 1, "y": 2 }{ "x": 1, "y": 2 }{ "x": 1, "y": 2 }

Unsafe keys

__proto__ is the only key that cannot be written to a plain object without reassigning its prototype instead of adding a field. Decoded results therefore never contain one:

decoderinput containing a __proto__ key
object()ignored, like any other unknown key
exact()rejected
inexact()rejected, rather than passed through
record()rejected

Where a decoder rejects, the error is reported on the __proto__ field itself, so it reads like any other field error:

{
  "theme": "dark",
  "__proto__": {
    "isAdmin": true,
  },
               ^ Unsafe key
}

Declaring a __proto__ key in a decoder definition throws. If you genuinely need to read a field named __proto__, write a custom decoder with define().


Dictionary shapes

record

function record( values: Decoder<V> ): Decoder<Record<string, V>> (source)
function record( keys: Decoder<K>, values: Decoder<V> ): Decoder<Record<K, V>>

Accepts objects where all values match the given decoder, and returns the result as a Record<string, V>.

This is useful to validate inputs like { [key: string]: V }.

Inputs containing a __proto__ key are rejected. See Unsafe keys.

Decoding values only

The default call takes a single argument and will validate all values. For example, to validate that all values in the object are numbers:

Try it
record(number).verify(input)
InputResult
{ "red": 1, "blue": 2, "green": 3 }
{ "hi": "not a number", ^^^^^^^^^^^^^^ Must be number }

Decoding keys and values

If you also want to validate that keys are of a specific form, use the two-argument form: record(key, value). Note that the given key decoder must return strings.

For example, to enforce that all keys are emails:

Try it
record(email, number).verify(input)
InputResult
{ "me@nvie.com": 1 }
{ "no-email": 1, } ^ Invalid key 'no-email': Must be email

Simple objects

pojo

pojo: Decoder<Record<string, unknown>> (source)

Accepts any "plain old JavaScript object", but doesn't validate its keys or values further.

Try it
pojo.verify(input)
InputResult
{}
{ "name": "hi" }
"hi" ^^^^ Must be an object
[] ^^ Must be an object
new Date('2026-09-28T09:17:44.337Z') ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Must be an object
null ^^^^ Must be an object

On this page