Back to blog

Pattern Matching in JavaScript: The TC39 Proposal That Will Change Your Code

Hello HaWkers, one of the most anticipated proposals in TC39 is finally gaining traction: Pattern Matching for JavaScript. This feature, already present in languages like Rust, Scala, and Elixir, promises to revolutionize how we write conditional logic and data destructuring in JavaScript.

Have you ever been frustrated with endless if/else chains or confusing switch cases? Pattern Matching might be the solution you've been waiting for.

What is Pattern Matching?

Pattern Matching is a way to check values against patterns and extract data from complex structures declaratively.

Basic Comparison

Today with switch:

function getAnimalSound(animal) {
  switch (animal.type) {
    case 'dog':
      return 'woof';
    case 'cat':
      return 'meow';
    case 'bird':
      return 'chirp';
    default:
      return 'unknown';
  }
}

With Pattern Matching (proposal):

function getAnimalSound(animal) {
  return match (animal) {
    when ({ type: 'dog' }) -> 'woof',
    when ({ type: 'cat' }) -> 'meow',
    when ({ type: 'bird' }) -> 'chirp',
    else -> 'unknown'
  };
}

Why Does This Matter?

Benefits of Pattern Matching:

Aspect Traditional Pattern Matching
Readability Nested if/else Declarative and clean
Destructuring Separate from condition Integrated
Exhaustiveness Manual Can be checked
Composition Difficult Natural
Types Separate verification Built into pattern

The TC39 Proposal

Let's understand the details of the current proposal.

Status in TC39

History:

  • Stage 1: 2020
  • Stage 2: 2024
  • Stage 3: Under discussion (2026)
  • Stage 4: Expected 2027

Main champions:

  • Mark Miller (Agoric)
  • Tab Atkins (Google)
  • Jordan Harband (Airbnb)

Proposed Syntax

The syntax is still under discussion, but the most likely version is:

// Basic syntax
match (value) {
  when (pattern1) -> result1,
  when (pattern2) -> result2,
  else -> defaultResult
}

// As expression
const result = match (x) {
  when (1) -> 'one',
  when (2) -> 'two',
  else -> 'other'
};

Pattern Types

1. Literal Patterns:

const description = match (code) {
  when (200) -> 'OK',
  when (201) -> 'Created',
  when (404) -> 'Not Found',
  when (500) -> 'Server Error',
  else -> 'Unknown Status'
};

2. Object Patterns:

const message = match (response) {
  when ({ status: 'success', data }) -> `Success: ${data}`,
  when ({ status: 'error', message }) -> `Error: ${message}`,
  when ({ status: 'loading' }) -> 'Loading...',
  else -> 'Unknown state'
};

3. Array Patterns:

const first = match (list) {
  when ([]) -> 'Empty list',
  when ([single]) -> `One item: ${single}`,
  when ([first, second]) -> `Two: ${first}, ${second}`,
  when ([head, ...tail]) -> `First: ${head}, rest: ${tail.length}`,
  else -> 'Unexpected format'
};

4. Patterns with Guards:

const category = match (user) {
  when ({ age }) if (age < 13) -> 'child',
  when ({ age }) if (age < 20) -> 'teenager',
  when ({ age }) if (age < 60) -> 'adult',
  when ({ age }) -> 'senior',
  else -> 'unknown age'
};

Practical Use Cases

Let's see how Pattern Matching improves real code.

Processing API Responses

Before:

function handleResponse(response) {
  if (response.status === 'success') {
    if (response.data && response.data.items) {
      return response.data.items.map(processItem);
    } else if (response.data) {
      return [processItem(response.data)];
    } else {
      return [];
    }
  } else if (response.status === 'error') {
    if (response.code === 401) {
      return redirectToLogin();
    } else if (response.code === 404) {
      return showNotFound();
    } else {
      return showError(response.message);
    }
  } else if (response.status === 'pending') {
    return showLoading();
  }
  return null;
}

With Pattern Matching:

function handleResponse(response) {
  return match (response) {
    when ({ status: 'success', data: { items } }) ->
      items.map(processItem),

    when ({ status: 'success', data }) ->
      [processItem(data)],

    when ({ status: 'success' }) ->
      [],

    when ({ status: 'error', code: 401 }) ->
      redirectToLogin(),

    when ({ status: 'error', code: 404 }) ->
      showNotFound(),

    when ({ status: 'error', message }) ->
      showError(message),

    when ({ status: 'pending' }) ->
      showLoading(),

    else -> null
  };
}

Reducers in State Management

Before (Redux-style):

function todoReducer(state, action) {
  switch (action.type) {
    case 'ADD_TODO':
      return {
        ...state,
        todos: [...state.todos, action.payload]
      };
    case 'TOGGLE_TODO':
      return {
        ...state,
        todos: state.todos.map(todo =>
          todo.id === action.payload
            ? { ...todo, completed: !todo.completed }
            : todo
        )
      };
    case 'DELETE_TODO':
      return {
        ...state,
        todos: state.todos.filter(todo => todo.id !== action.payload)
      };
    default:
      return state;
  }
}

With Pattern Matching:

function todoReducer(state, action) {
  return match (action) {
    when ({ type: 'ADD_TODO', payload }) -> ({
      ...state,
      todos: [...state.todos, payload]
    }),

    when ({ type: 'TOGGLE_TODO', payload: id }) -> ({
      ...state,
      todos: state.todos.map(todo =>
        match (todo) {
          when ({ id: ^id }) -> ({ ...todo, completed: !todo.completed }),
          else -> todo
        }
      )
    }),

    when ({ type: 'DELETE_TODO', payload: id }) -> ({
      ...state,
      todos: state.todos.filter(todo => todo.id !== id)
    }),

    else -> state
  };
}

AST Parsing

Before:

function evaluate(node) {
  if (node.type === 'NumberLiteral') {
    return node.value;
  } else if (node.type === 'BinaryExpression') {
    const left = evaluate(node.left);
    const right = evaluate(node.right);

    if (node.operator === '+') {
      return left + right;
    } else if (node.operator === '-') {
      return left - right;
    }
  }
  throw new Error(`Unknown node type: ${node.type}`);
}

With Pattern Matching:

function evaluate(node) {
  return match (node) {
    when ({ type: 'NumberLiteral', value }) ->
      value,

    when ({ type: 'BinaryExpression', operator: '+', left, right }) ->
      evaluate(left) + evaluate(right),

    when ({ type: 'BinaryExpression', operator: '-', left, right }) ->
      evaluate(left) - evaluate(right),

    else -> throw new Error(`Unknown node: ${JSON.stringify(node)}`)
  };
}

How to Use Today

While the proposal is not approved, there are alternatives.

Current Libraries

1. ts-pattern (TypeScript/JavaScript):

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

const result = match(value)
  .with({ type: 'dog' }, () => 'woof')
  .with({ type: 'cat' }, () => 'meow')
  .with({ type: P.string }, ({ type }) => `${type} sound`)
  .otherwise(() => 'unknown');

2. match-iz:

import { match, when, otherwise } from 'match-iz';

const result = match(animal)(
  when({ type: 'dog' }, () => 'woof'),
  when({ type: 'cat' }, () => 'meow'),
  otherwise(() => 'unknown')
);

DIY with Functions

// Simple pattern matching implementation
function match(value) {
  return {
    when(pattern, result) {
      const patterns = [{ pattern, result }];

      const chain = {
        when(p, r) {
          patterns.push({ pattern: p, result: r });
          return chain;
        },
        otherwise(defaultResult) {
          for (const { pattern, result } of patterns) {
            if (matches(value, pattern)) {
              return typeof result === 'function' ? result(value) : result;
            }
          }
          return typeof defaultResult === 'function'
            ? defaultResult(value)
            : defaultResult;
        }
      };

      return chain;
    }
  };
}

Timeline and Expectations

When can we expect Pattern Matching in production?

Estimated Roadmap

Phase Estimated Date Status
Stage 3 Q2 2026 Under review
Browser implementation Q4 2026 After Stage 3
Stage 4 Q1 2027 Pending
Available in Node LTS Q3 2027 Expected

How to Prepare

1. Learn the concepts:

  • Study Pattern Matching in other languages
  • Practice with libraries like ts-pattern
  • Understand advanced destructuring

2. Refactor existing code:

  • Identify complex if/else chains
  • Rewrite switches as pattern matches
  • Document where you would use the feature

3. Follow the proposal:

  • GitHub: tc39/proposal-pattern-matching
  • Read TC39 meeting notes
  • Participate in discussions

Conclusion

Pattern Matching is one of the most anticipated additions to JavaScript and has the potential to significantly transform how we write code. The feature brings more expressiveness, readability, and safety to conditional logic and data processing.

Key points:

  1. Pattern Matching allows checking values against patterns declaratively
  2. The proposal is at Stage 2 in TC39, moving toward Stage 3
  3. Supports literal, object, array patterns and guards
  4. Libraries like ts-pattern allow using similar concepts today
  5. Native availability expected between 2026-2027

For JavaScript developers, it's worth starting to familiarize yourself with the concept now. When the feature is released, you'll be ready to take full advantage of its potential.

For more on the future of JavaScript, read: ESM (ES Modules): Complete Adoption in JavaScript 2026.

Let's go! 🦅

Comments (0)

This article has no comments yet 😢. Be the first! 🚀🦅

Add comments