CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-ts-pattern

The exhaustive Pattern Matching library for TypeScript with smart type inference

Pending
Overview
Eval results
Files

pattern-matching.mddocs/

Pattern Matching

Core pattern matching functionality for creating exhaustive conditional logic with type safety and smart type inference.

Capabilities

Match Expression

Creates a chainable pattern matching expression for handling complex conditional logic.

/**
 * Creates a pattern matching expression
 * @param value - Input value to match against patterns
 * @returns Match expression for chaining patterns
 */
function match<const input, output = unknown>(
  value: input
): Match<input, output>;

Usage Examples:

import { match, P } from "ts-pattern";

// Basic pattern matching
const result = match(value)
  .with('hello', () => 'greeting')
  .with('goodbye', () => 'farewell')
  .otherwise(() => 'unknown');

// Object pattern matching
const response = match(apiResponse)
  .with({ status: 'success', data: P.select() }, (data) => data)
  .with({ status: 'error', message: P.select() }, (msg) => { throw new Error(msg) })
  .exhaustive();

Pattern Matching Methods

Methods available on the match expression for building pattern matching logic.

interface Match<input, output> {
  /**
   * Match against a pattern with handler function
   * @param pattern - Pattern to match against input
   * @param handler - Function to execute on match
   * @returns Updated match expression
   */
  with<const pattern extends Pattern<input>>(
    pattern: pattern,
    handler: (selections: P.infer<pattern>, value: input) => output
  ): Match<input, output>;
  
  /**
   * Match against a pattern with guard condition and handler
   * @param pattern - Pattern to match against input
   * @param guard - Additional condition function
   * @param handler - Function to execute on match
   * @returns Updated match expression
   */
  with<const pattern extends Pattern<input>>(
    pattern: pattern,
    guard: (value: input) => unknown,
    handler: (selections: P.infer<pattern>, value: input) => output
  ): Match<input, output>;
  
  /**
   * Match against multiple patterns with single handler
   * @param patterns - Multiple patterns followed by handler function
   * @returns Updated match expression
   */
  with<const patterns extends readonly Pattern<input>[]>(
    ...args: [...patterns, (selections: any, value: input) => output]
  ): Match<input, output>;
  
  /**
   * Match based on predicate function
   * @param predicate - Function returning truthy value for match
   * @param handler - Function to execute on match
   * @returns Updated match expression
   */
  when(
    predicate: (value: input) => unknown,
    handler: (value: input) => output
  ): Match<input, output>;
  
  /**
   * Provide default case and return result
   * @param handler - Default handler function
   * @returns Final result of pattern matching
   */
  otherwise(handler: (value: input) => output): output;
  
  /**
   * Ensure exhaustive matching and return result
   * @param unexpectedValueHandler - Optional error handler for unmatched cases
   * @returns Final result of pattern matching
   * @throws NonExhaustiveError if no patterns match
   */
  exhaustive(unexpectedValueHandler?: (value: input) => never): output;
  
  /**
   * Alias for exhaustive()
   * @returns Final result of pattern matching
   */
  run(): output;
  
  /**
   * Type-level method for return type inference
   * @returns Match expression for type inference
   */
  returnType(): Match<input, output>;
  
  /**
   * Type-level method for input type narrowing
   * @returns Match expression for type narrowing
   */
  narrow(): Match<input, output>;
}

Usage Examples:

// Multiple patterns with single handler
const category = match(statusCode)
  .with(200, 201, 202, () => 'success')
  .with(400, 401, 403, 404, () => 'client-error')
  .with(500, 502, 503, () => 'server-error')
  .otherwise(() => 'unknown');

// Guard conditions
const processUser = match(user)
  .with(
    { type: 'admin' },
    (u) => u.permissions.includes('delete'),
    (u) => deleteUser(u)
  )
  .with({ type: 'user', active: true }, (u) => processActiveUser(u))
  .otherwise(() => 'inactive or unauthorized');

// Predicate matching
const handleAge = match(person)
  .when(p => p.age >= 18, (p) => 'adult: ' + p.name)
  .when(p => p.age >= 13, (p) => 'teen: ' + p.name)
  .otherwise(p => 'child: ' + p.name);

Pattern Types

Core pattern types used in matching expressions.

/**
 * Base pattern type that can match values of type T
 */
type Pattern<T> = 
  | T
  | Matcher<any, any, any, any, any>
  | (T extends readonly (infer U)[] ? Pattern<U>[] : never)
  | (T extends object ? { [K in keyof T]?: Pattern<T[K]> } : never);

/**
 * Matcher protocol interface for custom patterns
 */
interface Matcher<
  input,
  pattern,
  narrowedOrFn,
  exclude = never,
  matcherType = string
> {
  [matcher](): {
    match: (value: input) => { matched: boolean; selections?: Record<string, unknown> };
    getSelectionKeys?: () => string[];
    matcherType?: matcherType;
  };
}

/**
 * Symbol used for matcher protocol
 */
declare const matcher: unique symbol;

Selection and Type Inference

Pattern matching supports value selection and provides smart type inference.

/**
 * Infer the type of value matched by a pattern
 */
type infer<pattern> = InvertPattern<NoInfer<pattern>, unknown>;

/**
 * Narrow input type to pattern-compatible subset
 */  
type narrow<input, pattern> = ExtractPreciseValue<
  input,
  InvertPattern<pattern, input>
>;

Usage Examples:

// Type inference with selections
const extractData = match(response)
  .with({ success: true, data: P.select() }, (data) => {
    // data is properly typed based on the pattern
    return data;
  })
  .with({ success: false, error: P.select() }, (error) => {
    // error is properly typed
    throw error;
  })
  .exhaustive();

// Named selections
const processEvent = match(event)
  .with(
    { type: 'user', action: 'login', userId: P.select('id'), timestamp: P.select('time') },
    ({ id, time }) => logUserLogin(id, time)
  )
  .with(
    { type: 'system', level: 'error', message: P.select() },
    (message) => handleSystemError(message)
  )
  .exhaustive();

Install with Tessl CLI

npx tessl i tessl/npm-ts-pattern

docs

index.md

pattern-matching.md

patterns.md

validation.md

tile.json