Guides

Validate input

Move string validation and transformation into reusable types so handlers receive domain values or do not run.

A Type<Input, Output> converts parser input to a handler value. Its from method may return a value or promise and should throw an Error when decoding fails.

Extend a bundled type

Use extendType to preserve the base type's metadata while adding another decoding step:

import { command, extendType, number, option } from '@alloc/cmd-ts';

const Port = extendType(number, (value) => {
  if (!Number.isInteger(value) || value < 1 || value > 65_535) {
    throw new Error('Expected an integer from 1 to 65535');
  }
  return value;
});

const serve = command({
  name: 'serve',
  args: {
    port: option({ long: 'port', type: Port }),
  },
  handler({ port }) {
    // port is a number that passed the range check.
    console.log(`Listening on ${port}`);
  },
});

Given --port 70000, cmd-ts reports the thrown message and does not call the handler.

Define a type directly

Implement Type directly when the decoder starts from the parser's raw input or needs its own help metadata:

import type { Type } from '@alloc/cmd-ts';

const JsonObject: Type<string, Record<string, unknown>> = {
  displayName: 'json',
  description: 'A JSON object',
  async from(input) {
    const value: unknown = JSON.parse(input);
    if (typeof value !== 'object' || value === null || Array.isArray(value)) {
      throw new Error('Expected a JSON object');
    }
    return value as Record<string, unknown>;
  },
};

displayName appears in usage placeholders. description supplies help text when the parser itself has no description.

Restrict strings to known values

Use oneOf for a fixed literal set:

const environment = oneOf(['development', 'production'] as const);

The output type is 'development' | 'production', and any other string produces an error listing the allowed values.

Use union when several decoders accept the same input but produce different output types:

const id = union([number, string]);

Decoders run from left to right. The first success wins; if every decoder fails, their error messages are joined with newlines unless combineErrors is provided.

See the type reference for bundled decoders and combinators.