Skip to main content

Overview

The sized() decoder validates length constraints on strings, arrays, or ES6 Sets. It accepts an existing decoder and adds size restrictions, rejecting values that don’t meet the specified constraints.

Basic Usage

import { sized, string } from 'decoders';

const usernameDecoder = sized(string, { min: 3, max: 20 });

const username = usernameDecoder.verify('alice');
// Type: string
// Result: 'alice'

usernameDecoder.verify('ab'); // Error: too short (min 3)
usernameDecoder.verify('a'.repeat(25)); // Error: too long (max 20)

Type Signature

function sized<T extends Sized>(
  decoder: Decoder<T>,
  options: SizeOptions
): Decoder<T>;

type Sized = string | unknown[] | Set<unknown>;

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

Parameters

decoder
Decoder<T>
required
The base decoder for strings, arrays, or sets. The validated value must have a .length or .size property.
options
SizeOptions
required
Size constraints object with optional min and max properties.
options.min
number
Minimum allowed length/size (inclusive).
options.max
number
Maximum allowed length/size (inclusive).

Return Value

Returns a Decoder<T> that validates the base type and enforces size constraints.

Examples

String length validation

import { sized, string } from 'decoders';

// Password must be at least 8 characters
const passwordDecoder = sized(string, { min: 8 });

passwordDecoder.verify('secret123'); // OK
passwordDecoder.verify('short'); // Error: too short

// Product code must be exactly 10 characters
const productCodeDecoder = sized(string, { min: 10, max: 10 });

productCodeDecoder.verify('ABC1234567'); // OK
productCodeDecoder.verify('ABC123'); // Error: too short
productCodeDecoder.verify('ABC123456789'); // Error: too long

Array length validation

import { sized, array, string } from 'decoders';

// Tags array must have 1-5 items
const tagsDecoder = sized(array(string), { min: 1, max: 5 });

tagsDecoder.verify(['typescript', 'validation']); // OK
tagsDecoder.verify([]); // Error: too short (min 1)
tagsDecoder.verify(['a', 'b', 'c', 'd', 'e', 'f']); // Error: too long (max 5)

Set size validation

import { sized, setFromArray, number } from 'decoders';

// Need at least 2 unique IDs
const idsDecoder = sized(setFromArray(number), { min: 2 });

idsDecoder.verify([1, 2, 3]); // OK (Set has 3 items)
idsDecoder.verify([1, 1, 1]); // Error: Set has only 1 unique item (min 2)

Non-empty constraints

import { sized, string, array, number } from 'decoders';

// String must not be empty
const nonEmptyString = sized(string, { min: 1 });

nonEmptyString.verify('hello'); // OK
nonEmptyString.verify(''); // Error: too short

// Array must not be empty
const nonEmptyArray = sized(array(number), { min: 1 });

nonEmptyArray.verify([1, 2, 3]); // OK
nonEmptyArray.verify([]); // Error: too short

Maximum size limits

import { sized, array, string } from 'decoders';

// Limit API response items
const limitedItemsDecoder = sized(array(string), { max: 100 });

limitedItemsDecoder.verify(['item1', 'item2']); // OK
limitedItemsDecoder.verify(Array(101).fill('item')); // Error: too long

Combined with other decoders

import { sized, string, regex } from 'decoders';

// Username: 3-20 characters, alphanumeric only
const usernameDecoder = sized(
  regex(/^[a-zA-Z0-9]+$/, 'Must be alphanumeric'),
  { min: 3, max: 20 }
);

usernameDecoder.verify('alice'); // OK
usernameDecoder.verify('ab'); // Error: too short
usernameDecoder.verify('alice@123'); // Error: not alphanumeric

In object decoders

import { object, sized, string, array } from 'decoders';

const postDecoder = object({
  title: sized(string, { min: 1, max: 100 }),
  body: sized(string, { min: 10, max: 5000 }),
  tags: sized(array(string), { min: 1, max: 5 }),
});

const post = postDecoder.verify({
  title: 'My First Post',
  body: 'This is a longer body with at least 10 characters...',
  tags: ['typescript', 'validation'],
});

Error Messages

The sized() decoder provides clear error messages when constraints are violated:
import { sized, string } from 'decoders';

const decoder = sized(string, { min: 5, max: 10 });

try {
  decoder.verify('abc');
} catch (error) {
  console.error(error.message);
  // Indicates the value is too short and what the minimum is
}

try {
  decoder.verify('abcdefghijklmnop');
} catch (error) {
  console.error(error.message);
  // Indicates the value is too long and what the maximum is
}

Implementation Details

The sized() decoder uses the .reject() method to add size validation:
// Simplified implementation
export function sized<T extends Sized>(
  decoder: Decoder<T>,
  options: SizeOptions
): Decoder<T> {
  return decoder.reject(bySizeOptions(options));
}
It checks the .length property for strings and arrays, and the .size property for Sets.

Common Patterns

Username validation

import { sized, regex } from 'decoders';

const username = sized(
  regex(/^[a-zA-Z0-9_]+$/, 'Alphanumeric and underscores only'),
  { min: 3, max: 20 }
);

Email with length limit

import { sized, email } from 'decoders';

const limitedEmail = sized(email, { max: 255 });

Pagination limits

import { sized, array } from 'decoders';

const paginatedResults = sized(array(itemDecoder), { max: 50 });

Required multi-selection

import { sized, array, string } from 'decoders';

// User must select at least 2 options, max 5
const selectedOptions = sized(array(string), { min: 2, max: 5 });

Type Safety

TypeScript preserves the original type through sized():
import { sized, array, number } from 'decoders';

const decoder = sized(array(number), { min: 1 });
type Result = ReturnType<typeof decoder.verify>;
// Type: number[] (not just unknown[])

See Also

Build docs developers (and LLMs) love