Skip to Content
Arrays

Last Updated: 3/6/2026


Array and Tuple Patterns

Tuple Patterns (Fixed-Length Arrays)

In TypeScript, tuples are arrays with a fixed number of elements that can be of different types. Use array literal patterns to match tuples.

Basic Tuple Matching

import { match, P } from 'ts-pattern'; type Input = | [number, '+', number] | [number, '-', number] | [number, '*', number] | ['-', number]; const input: Input = [3, '*', 4]; const output = match(input) .with([P._, '+', P._], ([x, , y]) => x + y) .with([P._, '-', P._], ([x, , y]) => x - y) .with([P._, '*', P._], ([x, , y]) => x * y) .with(['-', P._], ([, x]) => -x) .exhaustive(); console.log(output); // => 12

Nested Tuples

type Point3D = [number, number, number]; type Line = [Point3D, Point3D]; const line: Line = [ [0, 0, 0], [1, 1, 1], ]; match(line) .with([[0, 0, 0], P._], () => 'Starts at origin') .with([P._, [0, 0, 0]], () => 'Ends at origin') .with([[P._, P._, 0], [P._, P._, 0]], () => 'Line in XY plane') .otherwise(() => 'Other line');

P.array() - Variable-Length Arrays

P.array(subpattern) matches arrays of any length where all elements match the sub-pattern.

Basic Array Matching

type Post = { title: string; content: string }; const posts: Post[] = [ { title: 'Hello', content: 'World' }, { title: 'Foo', content: 'Bar' }, ]; match(posts) .with( P.array({ title: P.string, content: P.string }), (posts) => `Found ${posts.length} posts` ) .otherwise(() => 'Invalid posts');

Empty Arrays

match(value) .with([], () => 'Empty array') .with(P.array(P._), (arr) => `Non-empty array with ${arr.length} items`) .otherwise(() => 'Not an array');

Nested Arrays

type Matrix = number[][]; const matrix: Matrix = [ [1, 2, 3], [4, 5, 6], ]; match(matrix) .with(P.array(P.array(P.number)), (m) => `Matrix with ${m.length} rows` ) .otherwise(() => 'Not a matrix');

Variadic Tuples with P.array()

Match tuples with variable-length portions using ...P.array():

type Input = (number | string)[]; declare const input: Input; match(input) // Match arrays starting with a string .with([P.string, ...P.array()], (arr) => { // arr: [string, ...(number | string)[]] console.log('First element is string:', arr[0]); }) // Match arrays starting with 'print' followed by strings .with(['print', ...P.array(P.string)], (arr) => { // arr: ['print', ...string[]] console.log('Print command:', arr.slice(1)); }) // Match arrays ending with 'end' .with([...P.array(P.string), 'end'], (arr) => { // arr: [...string[], 'end'] console.log('Ends with end'); }) // Match arrays with start and end markers .with(['start', ...P.array(P.string), 'end'], (arr) => { // arr: ['start', ...string[], 'end'] console.log('Has start and end'); }) .otherwise(() => {});

Selecting from Arrays

Select Entire Array

match(data) .with({ items: P.array(P.string).select() }, (items) => { // items: string[] return items.join(', '); }) .otherwise(() => '');

Select Specific Elements

match(value) .with([P.select('first'), P._, P.select('third')], ({ first, third }) => { return `First: ${first}, Third: ${third}`; }) .otherwise(() => '');

Select with Sub-Pattern

type User = { name: string; age: number }; match(users) .with( P.array({ age: P.number.gte(18).select() }), (ages) => { // ages: number[] (all >= 18) return `Adult ages: ${ages.join(', ')}`; } ) .otherwise(() => '');

Array Predicates

Combine P.array() with predicates:

match(value) .with( P.array(P.number.positive()), (nums) => `All positive: ${nums}` ) .with( P.array(P.string.minLength(3)), (strs) => `All strings >= 3 chars` ) .otherwise(() => 'Mixed or invalid');

Checking Array Length

Use P.when() to check array length:

match(value) .with( P.array(P._).when((arr) => arr.length > 0 && arr.length <= 5), (arr) => `Small array: ${arr.length} items` ) .with( P.array(P._).when((arr) => arr.length > 5), (arr) => `Large array: ${arr.length} items` ) .with([], () => 'Empty array') .otherwise(() => 'Not an array');

Real-World Examples

Command Parser

type Command = string[]; const parseCommand = (cmd: Command) => match(cmd) .with(['git', 'commit', '-m', P.select()], (message) => `Commit: ${message}` ) .with(['git', 'push', P.select('remote'), P.select('branch')], ({ remote, branch }) => `Push to ${remote}/${branch}` ) .with(['git', ...P.array()], (args) => `Git command: ${args.join(' ')}` ) .otherwise(() => 'Unknown command');

API Response Validation

const userPattern = { id: P.number, name: P.string, email: P.string, }; type UsersResponse = unknown; const validateResponse = (response: UsersResponse) => match(response) .with( { users: P.array(userPattern) }, ({ users }) => users // users: Array<{ id: number, name: string, email: string }> ) .otherwise(() => null);

Best Practices

  1. Use tuples for fixed-length: When you know the exact number of elements
  2. Use P.array() for variable-length: When the array size is unknown
  3. Combine patterns: Mix literals, wildcards, and predicates
  4. Validate nested structures: Use patterns to ensure data shape
// Good: Clear intent match(value) .with([P.string, P.number], ([s, n]) => `String and number`) .with(P.array(P.string), (strs) => `Array of strings`) .otherwise(() => 'Other'); // Avoid: Unclear pattern match(value) .with(P.array(P._), (arr) => /* What type are the elements? */) .otherwise(() => 'Other');