Skip to main content

Overview

The record() decoder validates objects where all values match a given decoder, returning a Record<string, V> type. It also supports an optional key decoder for validating and transforming object keys.

Basic Usage

Record with value decoder only

import { record, string, number } from 'decoders';

const scoresDecoder = record(number);

const result = scoresDecoder.verify({
  alice: 95,
  bob: 87,
  charlie: 92,
});
// Type: Record<string, number>
// Result: { alice: 95, bob: 87, charlie: 92 }

Record with key and value decoders

import { record, string, number } from 'decoders';

// Validate that keys are valid email addresses
const emailScoresDecoder = record(email, number);

const result = emailScoresDecoder.verify({
  '[email protected]': 95,
  '[email protected]': 87,
});
// Type: Record<string, number>

Type Signature

// Value decoder only
function record<V>(valueDecoder: Decoder<V>): Decoder<Record<string, V>>;

// Key and value decoders
function record<K extends string, V>(
  keyDecoder: Decoder<K>,
  valueDecoder: Decoder<V>
): Decoder<Record<K, V>>;

Parameters

valueDecoder
Decoder<V>
required
Decoder to validate each value in the object. When used as the first parameter, all keys are accepted as strings.
keyDecoder
Decoder<K extends string>
Optional decoder to validate and transform object keys. The decoder must return strings.

Return Value

Returns a Decoder<Record<K, V>> that validates objects with consistent key-value types.

Examples

Configuration objects

import { record, string } from 'decoders';

const configDecoder = record(string);

const config = configDecoder.verify({
  apiUrl: 'https://api.example.com',
  apiKey: 'secret123',
  environment: 'production',
});
// Type: Record<string, string>

Nested objects

import { record, object, string, number } from 'decoders';

const userStatsDecoder = record(
  object({
    score: number,
    level: number,
  })
);

const stats = userStatsDecoder.verify({
  alice: { score: 1500, level: 10 },
  bob: { score: 2300, level: 15 },
});
// Type: Record<string, { score: number; level: number }>

With key validation

import { record, string, number, regex } from 'decoders';

// Only allow alphanumeric keys
const alphanumericKey = regex(/^[a-zA-Z0-9]+$/, 'Must be alphanumeric');
const validatedRecordDecoder = record(alphanumericKey, number);

// This will succeed
validatedRecordDecoder.verify({
  user123: 42,
  abc: 99,
});

// This will fail (key contains special characters)
validatedRecordDecoder.verify({
  'user@123': 42, // Error: Invalid key "user@123"
});

Error Handling

When validation fails, record() provides detailed error messages:
import { record, number } from 'decoders';

const decoder = record(number);

try {
  decoder.verify({
    a: 1,
    b: 'invalid', // Not a number
    c: 3,
  });
} catch (error) {
  console.error(error.message);
  // Error includes the specific key that failed: "b"
}

Invalid key errors

import { record, email, string } from 'decoders';

const decoder = record(email, string);

try {
  decoder.verify({
    '[email protected]': 'value',
    'not-an-email': 'value', // Invalid key
  });
} catch (error) {
  console.error(error.message);
  // Error: Invalid key "not-an-email": Must be valid email
}

Behavior

  • Validates each key-value pair in the object
  • Collects all errors if multiple values fail validation
  • Stops processing on first invalid key (if key decoder is provided)
  • Returns a new object with validated values
  • Does not modify the original input
  • mapping - Returns an ES6 Map<string, T> instead of a plain object
  • object - For objects with known, fixed keys
  • pojo - For validating plain JavaScript objects

See Also

Build docs developers (and LLMs) love