Skip to main content
Accepts whatever the given decoder accepts, or null.

Signature

function nullable<T>(decoder: Decoder<T>): Decoder<T | null>

function nullable<T, C extends Scalar>(
  decoder: Decoder<T>,
  defaultValue: (() => C) | C
): Decoder<NonNullable<T> | C>

function nullable<T, V>(
  decoder: Decoder<T>,
  defaultValue: (() => V) | V
): Decoder<NonNullable<T> | V>

Type Inference

nullable() creates a union type that includes null:
const decoder = nullable(string);
// Type: Decoder<string | null>

const result = decoder.verify('hello');
// result is 'hello' | null

Basic Usage

import { nullable, string } from 'decoders';

const decoder = nullable(string);

decoder.verify('hello');     // 'hello'
decoder.verify(null);        // null
decoder.verify(undefined);   // DecodeError

With Default Value

If a default value is explicitly provided, return that instead in the null case:
import { nullable, number } from 'decoders';

const decoder = nullable(number, 0);

decoder.verify(42);          // 42
decoder.verify(null);        // 0
decoder.verify(undefined);   // DecodeError

Lazy Default Values

Default values can be functions that are evaluated lazily:
import { nullable, object, string } from 'decoders';

const defaultUser = () => ({ name: 'Guest' });
const decoder = nullable(object({ name: string }), defaultUser);

decoder.verify({ name: 'Alice' });   // { name: 'Alice' }
decoder.verify(null);                 // { name: 'Guest' }

Union Behavior

nullable() is implemented using either(null_, decoder), creating a true union type:
// These are equivalent:
nullable(string)
either(null_, string)

// Example with API response:
const apiResponseDecoder = object({
  data: nullable(string),
  error: nullable(string),
});

type ApiResponse = DecoderType<typeof apiResponseDecoder>;
// { data: string | null; error: string | null }

Nullable vs Optional

  • nullable(): accepts null but rejects undefined
  • optional(): accepts undefined but rejects null
  • nullish(): accepts both null and undefined
import { nullable, optional, string } from 'decoders';

nullable(string).verify(null);        // ✓ null
nullable(string).verify(undefined);   // ✗ DecodeError

optional(string).verify(undefined);   // ✓ undefined
optional(string).verify(null);        // ✗ DecodeError

Implementation

Source: ~/workspace/source/src/basics.ts:52-63
export function nullable<T, V>(
  decoder: Decoder<T>,
  defaultValue?: (() => V) | V,
): Decoder<T | V | null> {
  const rv = either(null_, decoder);
  return arguments.length >= 2
    ? rv.transform((value) => value ?? lazyval(defaultValue as (() => V) | V))
    : rv;
}

Build docs developers (and LLMs) love