Back to Blog
Advanced2026-04-05

Pattern Matching in JavaScript: A First Look at the TC39 Proposal

Explore the TC39 pattern matching proposal that brings switch-like syntax with destructuring, guards, and exhaustiveness.

Why Pattern Matching?

JavaScript's \switch\ statement is limited: it only matches by value equality, falls through by default (a common source of bugs), and can't destructure or match complex shapes. The pattern matching proposal introduces \match\ — a powerful expression that combines matching, destructuring, and guards in one clean syntax.

Basic Syntax

``javascript // Current switch — limited and error-prone switch (response.status) { case 200: handleSuccess(response); break; case 404: handleNotFound(); break; case 500: handleError(); break; default: handleUnknown(response); }

// Pattern matching — expressive and safe const result = match (response) { when ({ status: 200, body }) => processBody(body), when ({ status: 404 }) => 'Not Found', when ({ status: 500, error }) => \Server Error: \${error}\, when ({ status }) if (status >= 300 && status < 400) => 'Redirect', default => 'Unknown response' }; `

Destructuring Patterns

Pattern matching integrates deep destructuring — you can match and extract nested values in one step.

`javascript const describe = (value) => match (value) { when (null) => 'null value', when (undefined) => 'undefined value', when ({ type: 'circle', radius }) if (radius > 0) => \Circle with area \${Math.PI * radius ** 2}\, when ({ type: 'rect', width, height }) => \Rectangle: \${width}x\${height}\, when ([first, ...rest]) => \Array starting with \${first}, \${rest.length} more\, when (Number) if (value > 0) => 'Positive number', when (String) => \String: "\${value}"\, default => 'Something else' };

describe({ type: 'circle', radius: 5 }); // "Circle with area 78.53..." describe([1, 2, 3]); // "Array starting with 1, 2 more" describe('hello'); // 'String: "hello"' `

Real-World Use Cases: Redux Reducers

`javascript function reducer(state, action) { return match (action) { when ({ type: 'ADD_TODO', payload: { text, id } }) => ({ ...state, todos: [...state.todos, { id, text, done: false }] }), when ({ type: 'TOGGLE_TODO', payload: { id } }) => ({ ...state, todos: state.todos.map(t => t.id === id ? { ...t, done: !t.done } : t ) }), when ({ type: 'DELETE_TODO', payload: { id } }) => ({ ...state, todos: state.todos.filter(t => t.id !== id) }), default => state }; } `

Simulating Pattern Matching Today

While waiting for the proposal, you can approximate the pattern:

`javascript function match(value, patterns) { for (const [predicate, handler] of patterns) { if (predicate(value)) return handler(value); } throw new Error('No pattern matched'); }

const result = match(response, [ [r => r.status === 200, r => processBody(r.body)], [r => r.status === 404, () => 'Not Found'], [r => r.status >= 500, r => \Error: \${r.error}\], [() => true, () => 'Unknown'], ]); ``

Pattern matching will make JavaScript code more declarative, less error-prone, and more expressive. It's especially powerful for handling complex state transitions, API responses, and data transformations.