Voltar para o Blog

Pattern Matching em JavaScript: A Proposta TC39 Que Vai Mudar Seu Codigo

Ola HaWkers, uma das propostas mais aguardadas no TC39 esta finalmente ganhando tracao: Pattern Matching para JavaScript. Esta feature, ja presente em linguagens como Rust, Scala e Elixir, promete revolucionar a forma como escrevemos logica condicional e desestruturacao de dados em JavaScript.

Voce ja se frustrou com cadeias infinitas de if/else ou switch cases confusos? Pattern Matching pode ser a solucao que voce estava esperando.

O Que e Pattern Matching?

Pattern Matching e uma forma de verificar valores contra padroes e extrair dados de estruturas complexas de forma declarativa.

Comparacao Basica

Hoje com switch:

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

Com Pattern Matching (proposta):

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

Por Que Isso Importa?

Beneficios do Pattern Matching:

Aspecto Tradicional Pattern Matching
Legibilidade if/else aninhados Declarativo e limpo
Desestruturacao Separada da condicao Integrada
Exhaustividade Manual Pode ser checada
Composicao Dificil Natural
Tipos Verificacao separada Embutida no padrao

A Proposta TC39

Vamos entender os detalhes da proposta atual.

Status no TC39

Historico:

  • Stage 1: 2020
  • Stage 2: 2024
  • Stage 3: Em discussao (2026)
  • Stage 4: Previsto para 2027

Principais defensores:

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

Sintaxe Proposta

A sintaxe ainda esta em discussao, mas a versao mais provavel e:

// Sintaxe basica
match (valor) {
  when (padrao1) -> resultado1,
  when (padrao2) -> resultado2,
  else -> resultadoPadrao
}

// Como expressao
const resultado = match (x) {
  when (1) -> 'um',
  when (2) -> 'dois',
  else -> 'outro'
};

Tipos de Padroes

1. Padroes Literais:

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

2. Padroes de Objeto:

const mensagem = match (response) {
  when ({ status: 'success', data }) -> `Sucesso: ${data}`,
  when ({ status: 'error', message }) -> `Erro: ${message}`,
  when ({ status: 'loading' }) -> 'Carregando...',
  else -> 'Estado desconhecido'
};

3. Padroes de Array:

const primeiro = match (lista) {
  when ([]) -> 'Lista vazia',
  when ([unico]) -> `Um item: ${unico}`,
  when ([primeiro, segundo]) -> `Dois: ${primeiro}, ${segundo}`,
  when ([head, ...tail]) -> `Primeiro: ${head}, resto: ${tail.length}`,
  else -> 'Formato inesperado'
};

4. Padroes com Guards:

const categoria = match (usuario) {
  when ({ idade }) if (idade < 13) -> 'crianca',
  when ({ idade }) if (idade < 20) -> 'adolescente',
  when ({ idade }) if (idade < 60) -> 'adulto',
  when ({ idade }) -> 'senior',
  else -> 'idade desconhecida'
};

Casos de Uso Praticos

Vamos ver como Pattern Matching melhora codigo real.

Processamento de API Responses

Antes:

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;
}

Com 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 em State Management

Antes (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)
      };
    case 'SET_FILTER':
      return {
        ...state,
        filter: action.payload
      };
    default:
      return state;
  }
}

Com 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)
    }),

    when ({ type: 'SET_FILTER', payload }) -> ({
      ...state,
      filter: payload
    }),

    else -> state
  };
}

Parsing de AST

Antes:

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;
    } else if (node.operator === '*') {
      return left * right;
    } else if (node.operator === '/') {
      return left / right;
    }
  } else if (node.type === 'UnaryExpression') {
    const argument = evaluate(node.argument);

    if (node.operator === '-') {
      return -argument;
    } else if (node.operator === '!') {
      return !argument;
    }
  }

  throw new Error(`Unknown node type: ${node.type}`);
}

Com 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),

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

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

    when ({ type: 'UnaryExpression', operator: '-', argument }) ->
      -evaluate(argument),

    when ({ type: 'UnaryExpression', operator: '!', argument }) ->
      !evaluate(argument),

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

Comparacao com Outras Linguagens

JavaScript se inspirou em implementacoes existentes.

Rust

match numero {
    1 => println!("Um"),
    2 | 3 => println!("Dois ou tres"),
    4..=10 => println!("Quatro a dez"),
    _ => println!("Outro"),
}

Scala

x match {
  case 1 => "um"
  case 2 => "dois"
  case n if n > 10 => "grande"
  case _ => "outro"
}

Elixir

case {a, b, c} do
  {1, x, 3} when x > 0 -> "matched"
  _ -> "not matched"
end

JavaScript (Proposta)

match (x) {
  when (1) -> 'um',
  when (2 | 3) -> 'dois ou tres',
  when (n) if (n > 10) -> 'grande',
  else -> 'outro'
}

💡 Observacao: A sintaxe JavaScript busca ser familiar enquanto se adapta a natureza dinamica da linguagem.

Como Usar Hoje

Enquanto a proposta nao e aprovada, existem alternativas.

Bibliotecas Atuais

1. ts-pattern (TypeScript/JavaScript):

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

const resultado = match(valor)
  .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 resultado = match(animal)(
  when({ type: 'dog' }, () => 'woof'),
  when({ type: 'cat' }, () => 'meow'),
  otherwise(() => 'unknown')
);

3. Babel Plugin (experimental):

// babel.config.js
module.exports = {
  plugins: ['@babel/plugin-proposal-pattern-matching']
};

// Uso com sintaxe proposta
const som = match (animal) {
  when ({ type: 'dog' }) -> 'woof',
  when ({ type: 'cat' }) -> 'meow',
  else -> 'unknown'
};

DIY com Funcoes

// Implementacao simples de pattern matching
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;
    }
  };
}

function matches(value, pattern) {
  if (typeof pattern === 'function') {
    return pattern(value);
  }
  if (typeof pattern === 'object' && pattern !== null) {
    return Object.entries(pattern).every(([key, val]) =>
      value && matches(value[key], val)
    );
  }
  return value === pattern;
}

// Uso
const resultado = match({ type: 'dog', name: 'Rex' })
  .when({ type: 'dog' }, animal => `${animal.name} says woof`)
  .when({ type: 'cat' }, animal => `${animal.name} says meow`)
  .otherwise(() => 'unknown');

Impacto na Comunidade

O que a adicao de Pattern Matching significa para o ecossistema.

Frameworks e Bibliotecas

React:

// Render condicional mais limpo
function UserStatus({ user }) {
  return match (user) {
    when ({ status: 'loading' }) -> <Spinner />,
    when ({ status: 'error', message }) -> <Error message={message} />,
    when ({ status: 'success', data }) -> <Profile data={data} />,
    else -> <NotFound />
  };
}

Node.js/Express:

// Handling de rotas
app.use((err, req, res, next) => {
  const response = match (err) {
    when ({ name: 'ValidationError', details }) ->
      { status: 400, body: { error: 'Validation failed', details } },

    when ({ name: 'NotFoundError' }) ->
      { status: 404, body: { error: 'Not found' } },

    when ({ name: 'UnauthorizedError' }) ->
      { status: 401, body: { error: 'Unauthorized' } },

    else ->
      { status: 500, body: { error: 'Internal server error' } }
  };

  res.status(response.status).json(response.body);
});

TypeScript Integration

// Pattern matching com tipos
type Shape =
  | { kind: 'circle'; radius: number }
  | { kind: 'square'; side: number }
  | { kind: 'rectangle'; width: number; height: number };

function area(shape: Shape): number {
  return match (shape) {
    when ({ kind: 'circle', radius }) -> Math.PI * radius ** 2,
    when ({ kind: 'square', side }) -> side ** 2,
    when ({ kind: 'rectangle', width, height }) -> width * height
  };
}
// TypeScript pode verificar exhaustividade!

Timeline e Expectativas

Quando podemos esperar Pattern Matching em producao?

Roadmap Estimado

Fase Data Estimada Status
Stage 3 Q2 2026 Em revisao
Implementacao browsers Q4 2026 Apos Stage 3
Stage 4 Q1 2027 Pendente
Disponivel em Node LTS Q3 2027 Previsto

Como Se Preparar

1. Aprenda os conceitos:

  • Estude Pattern Matching em outras linguagens
  • Pratique com bibliotecas como ts-pattern
  • Entenda desestruturacao avancada

2. Refatore codigo existente:

  • Identifique cadeias if/else complexas
  • Reescreva switches como pattern matches
  • Documente onde usaria a feature

3. Acompanhe a proposta:

  • GitHub: tc39/proposal-pattern-matching
  • Leia meeting notes do TC39
  • Participe de discussoes

Conclusao

Pattern Matching e uma das adicoes mais esperadas ao JavaScript e tem potencial para transformar significativamente como escrevemos codigo. A feature traz mais expressividade, legibilidade e seguranca para logica condicional e processamento de dados.

Pontos principais:

  1. Pattern Matching permite verificar valores contra padroes de forma declarativa
  2. A proposta esta em Stage 2 no TC39, caminhando para Stage 3
  3. Suporta padroes literais, objetos, arrays e guards
  4. Bibliotecas como ts-pattern permitem usar conceitos similares hoje
  5. Espera-se disponibilidade nativa entre 2026-2027

Para desenvolvedores JavaScript, vale a pena comecar a se familiarizar com o conceito agora. Quando a feature for lancada, voce estara pronto para aproveitar todo seu potencial.

Para mais sobre o futuro do JavaScript, leia: ESM (ES Modules): A Adocao Completa no JavaScript em 2026.

Bora pra cima! 🦅

Comentários (0)

Esse artigo ainda não possui comentários 😢. Seja o primeiro! 🚀🦅

Adicionar comentário