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

patterns.mddocs/

Pattern Library

Comprehensive pattern construction utilities including wildcards, type-specific patterns, and combinators for building complex matching logic.

Capabilities

Wildcard Patterns

Basic patterns that match entire categories of values.

/**
 * Wildcard pattern matching any value
 */
const any: AnyPattern;

/**
 * Alias for any wildcard pattern
 */
const _: AnyPattern;

/**
 * String wildcard with chainable methods
 */
const string: StringPattern;

/**
 * Number wildcard with chainable methods  
 */
const number: NumberPattern;

/**
 * BigInt wildcard with chainable methods
 */
const bigint: BigIntPattern;

/**
 * Boolean wildcard pattern
 */
const boolean: BooleanPattern;

/**
 * Symbol wildcard pattern
 */
const symbol: SymbolPattern;

/**
 * Matches null or undefined values
 */
const nullish: NullishPattern;

/**
 * Matches any non-null, non-undefined value
 */
const nonNullable: NonNullablePattern;

/**
 * Symbol used for the matcher protocol to create custom patterns
 */
const matcher: unique symbol;

Usage Examples:

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

// Basic wildcards
const handleValue = match(input)
  .with(P.string, (str) => `string: ${str}`)
  .with(P.number, (num) => `number: ${num}`)
  .with(P.boolean, (bool) => `boolean: ${bool}`)
  .with(P.nullish, () => 'null or undefined')
  .with(P._, () => 'something else');

String Patterns

Patterns for matching strings with specific characteristics.

interface StringChainable<pattern> extends Chainable<pattern> {
  /**
   * Matches strings starting with prefix
   * @param prefix - Required string prefix
   */
  startsWith(prefix: string): StringChainable<GuardP<any, `${string}${string}`>>;
  
  /**
   * Matches strings ending with suffix
   * @param suffix - Required string suffix
   */
  endsWith(suffix: string): StringChainable<GuardP<any, `${string}${string}`>>;
  
  /**
   * Matches strings with minimum length
   * @param min - Minimum string length
   */
  minLength(min: number): StringChainable<GuardP<any, string>>;
  
  /**
   * Matches strings with exact length
   * @param len - Exact string length
   */
  length(len: number): StringChainable<GuardP<any, string>>;
  
  /**
   * Matches strings with maximum length
   * @param max - Maximum string length
   */
  maxLength(max: number): StringChainable<GuardP<any, string>>;
  
  /**
   * Matches strings containing substring
   * @param substring - Required substring
   */
  includes(substring: string): StringChainable<GuardP<any, string>>;
  
  /**
   * Matches strings against regular expression
   * @param expression - Regular expression pattern
   */
  regex(expression: string | RegExp): StringChainable<GuardP<any, string>>;
}

Usage Examples:

// String pattern chaining
const validateInput = match(userInput)
  .with(P.string.startsWith('http').includes('://'), (url) => validateUrl(url))
  .with(P.string.regex(/^\d{3}-\d{2}-\d{4}$/), (ssn) => validateSSN(ssn))
  .with(P.string.minLength(8).maxLength(32), (password) => validatePassword(password))
  .otherwise(() => 'invalid input');

Number Patterns

Patterns for matching numbers with specific constraints.

interface NumberChainable<pattern> extends Chainable<pattern> {
  /**
   * Matches numbers between min and max (inclusive)
   * @param min - Minimum value (inclusive)
   * @param max - Maximum value (inclusive)
   */
  between(min: number, max: number): NumberChainable<GuardP<any, number>>;
  
  /**
   * Matches numbers less than max
   * @param max - Maximum value (exclusive)
   */
  lt(max: number): NumberChainable<GuardP<any, number>>;
  
  /**
   * Matches numbers greater than min
   * @param min - Minimum value (exclusive)
   */
  gt(min: number): NumberChainable<GuardP<any, number>>;
  
  /**
   * Matches numbers less than or equal to max
   * @param max - Maximum value (inclusive)
   */
  lte(max: number): NumberChainable<GuardP<any, number>>;
  
  /**
   * Matches numbers greater than or equal to min
   * @param min - Minimum value (inclusive)
   */
  gte(min: number): NumberChainable<GuardP<any, number>>;
  
  /**
   * Matches integer numbers only
   */
  int(): NumberChainable<GuardP<any, number>>;
  
  /**
   * Matches finite numbers (excludes Infinity and -Infinity)
   */
  finite(): NumberChainable<GuardP<any, number>>;
  
  /**
   * Matches positive numbers (> 0)
   */
  positive(): NumberChainable<GuardP<any, number>>;
  
  /**
   * Matches negative numbers (< 0)
   */
  negative(): NumberChainable<GuardP<any, number>>;
}

Usage Examples:

// Number pattern chaining
const categorizeNumber = match(value)
  .with(P.number.int().positive(), (n) => `positive integer: ${n}`)
  .with(P.number.between(0, 100), (n) => `percentage: ${n}%`)
  .with(P.number.finite().negative(), (n) => `negative: ${n}`)
  .otherwise(() => 'other number');

BigInt Patterns

Patterns for matching bigint values with constraints.

interface BigIntChainable<pattern> extends Chainable<pattern> {
  /**
   * Matches bigints between min and max (inclusive)
   */
  between(min: bigint, max: bigint): BigIntChainable<GuardP<any, bigint>>;
  
  /**
   * Matches bigints less than max
   */
  lt(max: bigint): BigIntChainable<GuardP<any, bigint>>;
  
  /**
   * Matches bigints greater than min
   */
  gt(min: bigint): BigIntChainable<GuardP<any, bigint>>;
  
  /**
   * Matches bigints less than or equal to max
   */
  lte(max: bigint): BigIntChainable<GuardP<any, bigint>>;
  
  /**
   * Matches bigints greater than or equal to min
   */
  gte(min: bigint): BigIntChainable<GuardP<any, bigint>>;
  
  /**
   * Matches positive bigints (> 0n)
   */
  positive(): BigIntChainable<GuardP<any, bigint>>;
  
  /**
   * Matches negative bigints (< 0n)
   */
  negative(): BigIntChainable<GuardP<any, bigint>>;
}

Collection Patterns

Patterns for matching arrays, sets, and maps.

/**
 * Matches arrays with optional element pattern
 * @param pattern - Pattern to match array elements against
 * @returns Array pattern that can be chained
 */
function array<input, const pattern extends Pattern<UnwrapArray<input>>>(
  pattern?: pattern
): ArrayChainable<ArrayP<input, pattern>>;

/**
 * Matches sets with optional element pattern
 * @param pattern - Pattern to match set elements against
 * @returns Set pattern that can be chained
 */
function set<input, const pattern extends Pattern<UnwrapSet<input>>>(
  pattern?: pattern
): Chainable<SetP<input, pattern>>;

/**
 * Matches maps with optional key and value patterns
 * @param keyPattern - Pattern to match map keys against
 * @param valuePattern - Pattern to match map values against
 * @returns Map pattern that can be chained
 */
function map<
  input,
  const keyPattern extends Pattern<UnwrapMapKey<input>>,
  const valuePattern extends Pattern<UnwrapMapValue<input>>
>(
  keyPattern?: keyPattern,
  valuePattern?: valuePattern
): Chainable<MapP<input, keyPattern, valuePattern>>;

interface ArrayChainable<pattern> extends Chainable<pattern> {
  /**
   * Makes the array pattern optional
   */
  optional(): ArrayChainable<OptionalP<any, pattern>>;
  
  /**
   * Selects the matched array elements
   */
  select(key?: string): ArrayChainable<SelectP<string, any, pattern>>;
}

Usage Examples:

// Collection patterns
const processData = match(input)
  .with(P.array(P.string), (strings) => strings.join(','))
  .with(P.array(P.number.positive()), (nums) => nums.reduce((a, b) => a + b))
  .with(P.set(P.string.startsWith('user:')), (userIds) => loadUsers(userIds))
  .with(P.map(P.string, P.number), (stringToNum) => Object.fromEntries(stringToNum))
  .otherwise(() => 'unsupported data structure');

Combinatorial Patterns

Patterns that combine multiple patterns using logical operations.

/**
 * Creates a pattern that matches if ALL patterns match (AND logic)
 * @param patterns - Patterns that must all match
 * @returns Intersection pattern
 */
function intersection<
  input,
  const patterns extends readonly [Pattern<input>, ...Pattern<input>[]]
>(...patterns: patterns): Chainable<AndP<input, patterns>>;

/**
 * Creates a pattern that matches if ANY pattern matches (OR logic)
 * @param patterns - Patterns where at least one must match
 * @returns Union pattern
 */
function union<
  input,
  const patterns extends readonly [Pattern<input>, ...Pattern<input>[]]
>(...patterns: patterns): Chainable<OrP<input, patterns>>;

/**
 * Creates a pattern that matches if the sub-pattern does NOT match
 * @param pattern - Pattern to negate
 * @returns Negation pattern
 */
function not<input, const pattern extends Pattern<input>>(
  pattern: pattern
): Chainable<NotP<input, pattern>>;

Usage Examples:

// Combinatorial patterns
const validateUser = match(user)
  .with(
    P.intersection(
      { age: P.number.gte(21) },
      { permissions: P.array(P.string.includes('admin')) },
      { active: true }
    ),
    (u) => 'admin user'
  )
  .with(
    P.union(
      { type: 'guest' },
      { type: 'trial', expiresAt: P.when(d => d > new Date()) }
    ),
    (u) => 'temporary access'
  )
  .with(
    P.not({ banned: true }),
    (u) => 'regular user'
  )
  .otherwise(() => 'access denied');

Conditional Patterns

Patterns that use predicates and guards for custom matching logic.

/**
 * Creates a pattern that matches based on predicate function
 * @param predicate - Function that returns truthy value for match
 * @returns Guard pattern with type narrowing
 */
function when<input, predicate extends (value: input) => unknown>(
  predicate: predicate
): GuardP<
  input,
  predicate extends (value: any) => value is infer narrowed ? narrowed : never
>;

/**
 * Creates a pattern that matches instances of a class
 * @param classConstructor - Constructor function to match against
 * @returns Instance pattern
 */
function instanceOf<T extends AnyConstructor>(
  classConstructor: T
): Chainable<GuardP<unknown, InstanceType<T>>>;
  
/**
 * Enables chainable methods on structural patterns  
 * @param pattern - Structural pattern to make chainable
 * @returns Chainable pattern with structural matching
 */
function shape<input, const pattern extends Pattern<input>>(
  pattern: pattern
): Chainable<GuardP<input, InvertPattern<pattern, input>>>;

Usage Examples:

// Conditional patterns
const handleError = match(error)
  .with(P.instanceOf(TypeError), (err) => handleTypeError(err))
  .with(P.instanceOf(RangeError), (err) => handleRangeError(err))
  .with(P.when(err => err.code === 'ENOENT'), (err) => handleFileNotFound(err))
  .with(P.shape({ status: P.number.between(400, 499) }).select(), (err) => handleClientError(err))
  .otherwise(err => handleGenericError(err));

Optional and Selection Patterns

Patterns for handling optional values and capturing selections.

/**
 * Makes a pattern optional (matches undefined or the pattern)
 * @param pattern - Pattern to make optional
 * @returns Optional pattern
 */
function optional<input, const pattern extends Pattern<input>>(
  pattern: pattern
): Chainable<OptionalP<input, pattern>, 'optional'>;

/**
 * Captures the matched value for use in handler
 * @param key - Optional key name for named selection
 * @param pattern - Optional pattern to match before selection
 * @returns Selection pattern
 */
function select(): Chainable<AnonymousSelectP, 'select' | 'or' | 'and'>;
function select<input, const pattern extends Pattern<input>>(
  pattern: pattern
): Chainable<SelectP<symbols.anonymousSelectKey, input, pattern>, 'select' | 'or' | 'and'>;
function select<input, const pattern extends Pattern<input>>(
  key: string,
  pattern: pattern
): Chainable<SelectP<string, input, pattern>, 'select' | 'or' | 'and'>;

Usage Examples:

// Optional and selection patterns
const processConfig = match(config)
  .with(
    { 
      host: P.string, 
      port: P.number.optional(), 
      credentials: P.select('creds', { username: P.string, password: P.string }) 
    },
    ({ creds }) => connectWithCredentials(creds.username, creds.password)
  )
  .with(
    { host: P.string, apiKey: P.select() },
    (apiKey) => connectWithApiKey(apiKey)
  )
  .otherwise(() => 'invalid configuration');

Universal Chainable Methods

Methods available on most patterns for additional composition.

interface Chainable<pattern, excluded = never> {
  /**
   * Makes the pattern optional (if not excluded)
   */
  optional(): excluded extends 'optional' ? never : Chainable<OptionalP<any, pattern>>;
  
  /**
   * Creates intersection with another pattern (if not excluded)
   */
  and<P2>(pattern: P2): excluded extends 'and' ? never : Chainable<AndP<any, [pattern, P2]>>;
  
  /**
   * Creates union with another pattern (if not excluded)
   */
  or<P2>(pattern: P2): excluded extends 'or' ? never : Chainable<OrP<any, [pattern, P2]>>;
  
  /**
   * Captures matched value (if not excluded)
   */
  select(key?: string): excluded extends 'select' ? never : Chainable<SelectP<string, any, pattern>>;
}

Install with Tessl CLI

npx tessl i tessl/npm-ts-pattern

docs

index.md

pattern-matching.md

patterns.md

validation.md

tile.json