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); // => 12Nested 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
- Use tuples for fixed-length: When you know the exact number of elements
- Use P.array() for variable-length: When the array size is unknown
- Combine patterns: Mix literals, wildcards, and predicates
- 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');