Optional chaining e nullish coalescing
Nesta aula, você aprenderá a usar o operador de encadeamento opcional (?.) e o operador de coalescência nula (??) em JavaScript. Exploraremos como eles simplificam o acesso a propriedades aninhadas e fornecem valores padrão seguros, além de discutir pegadinhas e boas práticas.
No desenvolvimento JavaScript moderno, lidar com objetos aninhados e valores indefinidos é uma tarefa comum e, muitas vezes, propensa a erros. Antes do ES2020, acessar propriedades de objetos que poderiam ser null ou undefined exigia verificações manuais ou o uso de operadores lógicos com efeitos colaterais. Com a introdução do optional chaining (?.) e do nullish coalescing (??), o código se torna mais limpo, seguro e expressivo.
Nesta aula, vamos explorar em detalhes como esses operadores funcionam, como combiná-los de forma eficaz e quais armadilhas evitar. Você verá exemplos práticos que ilustram o uso no dia a dia, desde acessos a APIs até configurações de objetos com valores padrão.
?. (Encadeamento Opcional)
O operador de encadeamento opcional (?.) permite acessar propriedades de um objeto de forma segura, sem lançar um erro se a referência for null ou undefined. Ele funciona como o operador de acesso a propriedade (.), mas interrompe a avaliação e retorna undefined se o valor antes do operador for nullish (ou seja, null ou undefined).
Antes do ES2020, para acessar uma propriedade aninhada com segurança, você precisava escrever algo como:
// Antes do ES2020
const nome = usuario && usuario.perfil && usuario.perfil.nome;
Com o encadeamento opcional, o mesmo código fica muito mais limpo:
const nome = usuario?.perfil?.nome;
Se usuario for null ou undefined, a expressão retorna undefined sem lançar erro. O mesmo vale para usuario.perfil. Você também pode usar ?.[] para acessar propriedades dinâmicas e ?.() para chamar funções de forma segura.
Exemplo com acesso dinâmico:
const dados = { lista: [10, 20, 30] };
const primeiro = dados?.lista?.[0]; // 10
const segundo = dados?.lista?.[1]; // 20
const inexistente = dados?.outraLista?.[0]; // undefined
Exemplo com chamada de função:
const objeto = {
metodo: () => 'Olá'
};
const resultado = objeto.metodo?.(); // 'Olá'
const objetoVazio = {};
const resultado2 = objetoVazio.metodo?.(); // undefined
?? (Coalescência Nula)
O operador de coalescência nula (??) é um operador lógico que retorna o operando do lado direito quando o operando do lado esquerdo é null ou undefined. Caso contrário, retorna o operando do lado esquerdo. Ele foi criado para fornecer um valor padrão apenas para valores nullish, ao contrário do operador || que considera também outros valores falsy (como 0, '', false, NaN).
Veja a diferença:
const valor = 0;
const comOu = valor || 'padrão'; // 'padrão' (porque 0 é falsy)
const comNullish = valor ?? 'padrão'; // 0 (porque 0 não é nullish)
Isso é particularmente útil quando você quer preservar valores como 0 ou '' como válidos, mas ainda assim fornecer um fallback para ausência de valor.
Exemplo prático:
function configurar(opcoes) {
const volume = opcoes.volume ?? 50; // Se volume for undefined, usa 50
const nome = opcoes.nome ?? 'Visitante'; // Se nome for undefined, usa 'Visitante'
return { volume, nome };
}
console.log(configurar({ volume: 0 })); // { volume: 0, nome: 'Visitante' }
console.log(configurar({ nome: 'Ana' })); // { volume: 50, nome: 'Ana' }
Combinando com Defaults
Uma das combinações mais poderosas é usar o encadeamento opcional junto com o operador de coalescência nula para acessar propriedades aninhadas e fornecer valores padrão de forma concisa. Isso é muito comum em aplicações que consomem APIs ou configuram objetos com muitos níveis.
Por exemplo, suponha que você tenha um objeto de usuário vindo de uma API e queira obter o nome do primeiro telefone, com um fallback:
const usuario = {
contatos: {
telefones: [{ numero: '123' }]
}
};
const telefone = usuario?.contatos?.telefones?.[0]?.numero ?? 'Sem telefone';
console.log(telefone); // '123'
// Se faltar algum nível:
const usuario2 = {};
const telefone2 = usuario2?.contatos?.telefones?.[0]?.numero ?? 'Sem telefone';
console.log(telefone2); // 'Sem telefone'
Essa combinação elimina a necessidade de múltiplas verificações if e torna o código muito mais legível.
Outro exemplo comum é configurar opções com valores padrão:
const opcoes = {
tema: 'escuro',
notificacoes: {
email: true
}
};
const emailNotif = opcoes?.notificacoes?.email ?? false;
const smsNotif = opcoes?.notificacoes?.sms ?? true; // true, pois não foi definido
console.log(emailNotif); // true
console.log(smsNotif); // true
Pegadinhas
Apesar de serem extremamente úteis, esses operadores têm algumas pegadinhas que você deve conhecer para evitar bugs sutis.
1. Não é possível usar ?. com operadores de atribuição — Você não pode escrever obj?.prop = valor, pois isso lançaria um erro de sintaxe. O encadeamento opcional é apenas para leitura (ou chamada de função), não para atribuição. Para atribuir com segurança, você precisa verificar manualmente.
// Errado: SyntaxError
// usuario?.nome = 'João';
// Correto:
if (usuario) {
usuario.nome = 'João';
}
2. Cuidado com o uso de ?? em expressões com && ou || — O operador ?? tem precedência menor que || e &&, mas não pode ser misturado com eles sem parênteses. Isso gera um erro de sintaxe. Por exemplo:
// Errado: SyntaxError
// const x = a ?? b || c;
// Correto:
const x = (a ?? b) || c;
3. O encadeamento opcional não impede erros em operações subsequentes — Se você acessar uma propriedade que não existe, retorna undefined, mas se você tentar usar esse valor em uma operação aritmética, por exemplo, ainda pode obter NaN ou erro. Por exemplo:
const usuario = { perfil: null };
const idade = usuario?.perfil?.idade * 2; // TypeError: Cannot read properties of null (reading 'idade')
Na verdade, usuario?.perfil?.idade retorna undefined, e undefined * 2 resulta em NaN (não um erro), mas se você tentar acessar uma propriedade de undefined sem o encadeamento, aí sim terá erro. Sempre verifique se o valor retornado é utilizável antes de operar.
4. ?? não pode ser usado com && e || sem parênteses — Já mencionado, mas vale reforçar: a mistura sem parênteses é um erro de sintaxe. Sempre use parênteses para agrupar.
5. Em navegadores antigos, pode ser necessário transpilar — Embora hoje seja suportado em todos os navegadores modernos, se você precisar suportar ambientes mais antigos, use Babel ou TypeScript para transpilar o código.
Boas Práticas
Aqui estão algumas boas práticas ao usar ?. e ??:
- Use
?.para acessar propriedades em cadeias onde qualquer nível pode sernullouundefined, especialmente ao trabalhar com dados de APIs. - Use
??para fornecer valores padrão que devem ser aplicados apenas paranull/undefined, não para outros valores falsy. - Combine ambos para obter código conciso e seguro.
- Evite usar
?.em cadeias muito longas, pois pode tornar o código difícil de ler; considere extrair variáveis intermediárias se necessário. - Sempre teste os casos em que a propriedade não existe para garantir que o valor padrão seja aplicado corretamente.
Referências
- MDN: Optional chaining (?)
- MDN: Nullish coalescing operator (??)
- ECMAScript Specification: Optional Chaining
- ECMAScript Specification: Nullish Coalescing
- JavaScript.info: Optional chaining
- JavaScript.info: Nullish coalescing operator
- W3Schools: JavaScript Comparisons
Exercícios
-
Dado o objeto
const user = { profile: { name: 'Ana', age: 25 } };, escreva uma expressão que retorne o nome do usuário usando optional chaining. Depois, escreva uma expressão que retorne o nome do perfil de um objeto vazio ({}) sem lançar erro.✓ Resposta:// Para o objeto user const nome = user?.profile?.name; // 'Ana' // Para objeto vazio const nomeVazio = {}?.profile?.name; // undefined -
Explique a diferença entre
||e??ao fornecer um valor padrão. Dê um exemplo onde o uso de||seria inadequado.✓ Resposta:||considera qualquer valor falsy (0, '', false, NaN) como ausente, enquanto??considera apenasnulleundefined. Exemplo: se você quer preservar o valor 0 como válido, usar||substituiria por padrão. Exemplo inadequado:const quantidade = 0; const comOu = quantidade || 10; // 10 (incorreto, pois 0 é um valor válido) const comNullish = quantidade ?? 10; // 0 (correto) -
Escreva uma função que receba um objeto de configuração e retorne o valor da propriedade
timeoutcom default 3000, usando??. A propriedade pode estar aninhada emconfig.request.✓ Resposta:function getTimeout(config) { return config?.request?.timeout ?? 3000; } -
Qual é a saída do seguinte código? Explique por quê.
const obj = { a: { b: 0 } }; console.log(obj?.a?.b ?? 'default'); console.log(obj?.x?.b ?? 'default');✓ Resposta:// Saída: 0 e 'default' // No primeiro caso, obj.a.b é 0, que não é nullish, então ?? retorna 0. // No segundo caso, obj.x é undefined, então a cadeia retorna undefined, e ?? retorna 'default'. -
Escreva um código que, dado um objeto
dataque pode ter uma propriedadeitems(um array), retorne o primeiro item ounullse não existir. Use optional chaining e nullish coalescing.✓ Resposta:const primeiroItem = data?.items?.[0] ?? null;