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"
endJavaScript (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:
- Pattern Matching permite verificar valores contra padroes de forma declarativa
- A proposta esta em Stage 2 no TC39, caminhando para Stage 3
- Suporta padroes literais, objetos, arrays e guards
- Bibliotecas como ts-pattern permitem usar conceitos similares hoje
- 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.

