Pular para o conteúdo
Cursos

DevClub

LógicaFront-endBack-endMobile

IA Club

IA na prática
Estudar programaçãoEstudar IA
LiçãoIniciantecódigo testado

Truthy e falsy em JavaScript: os 8 valores falsos

A lista fechada dos oito valores falsy, por que array vazio e objeto vazio são truthy, e o if que sempre passa — com cada conversão executada no Node 24.

Rodolfo Mori6 min de leitura

Todo valor em JavaScript vira true ou false quando entra num if, num && ou num while. Os que viram false são oito, e só oito: false, 0, -0, 0n, '', null, undefined e NaN. Todo o resto é truthy.

Decorar essa lista é mais útil do que parece, porque ela é curta e fechada. A consequência que pega todo mundo é o outro lado: array vazio e objeto vazio são truthy, então if (carrinho.itens) passa mesmo com o carrinho zerado.

O porteiro do if trabalha com uma lista curta

Pense num porteiro que precisa responder apenas “entra” ou “não entra”. O JavaScript entrega qualquer valor a ele — número, texto, objeto — e o porteiro faz a coerção booleana, isto é, converte temporariamente aquele valor em true ou false para tomar a decisão. A lista dos que não entram é fechada: são os oito valores falsy. Todo o resto passa.

O valor original não vira booleano para sempre; a conversão vale naquele contexto. É por isso que if (carrinho.itens) pode entrar e ainda deixar você com um array vazio: o porteiro só viu que existe um objeto. Antes de seguir, classifique 0, '0', '', [] e {} sem executar. Depois confira com Boolean(valor). Os pares que parecem iguais visualmente, mas recebem decisões diferentes, são justamente os que causam bugs de formulário e carrinho.

A lista fechada dos oito falsy

js
const falsy = [
  ['false', false],
  ['0', 0],
  ['-0', -0],
  ['0n', 0n],
  ["'' (string vazia)", ''],
  ['null', null],
  ['undefined', undefined],
  ['NaN', NaN],
];

for (const [rotulo, valor] of falsy) {
  console.log(rotulo.padEnd(18), '->', Boolean(valor));
}
false -> false 0 -> false -0 -> false 0n -> false '' (string vazia) -> false null -> false undefined -> false NaN -> false

Três deles são variações de zero (0, -0, 0n), dois são ausência (null, undefined), um é o resultado de conta inválida (NaN), um é a string sem caracteres e um é o próprio false. Não há mais nada.

Todo o resto é truthy — inclusive o que parece vazio

js
const suspeitos = [
  ['[] (array vazio)', []],
  ['{} (objeto vazio)', {}],
  ["'0'", '0'],
  ["'false'", 'false'],
  ["' ' (só espaço)", ' '],
  ['-1', -1],
  ['Infinity', Infinity],
  ['function () {}', function () {}],
];

for (const [rotulo, valor] of suspeitos) {
  console.log(rotulo.padEnd(18), '->', Boolean(valor));
}
[] (array vazio) -> true {} (objeto vazio) -> true '0' -> true 'false' -> true ' ' (só espaço) -> true -1 -> true Infinity -> true function () {} -> true

A regra por trás disso é simples: todo objeto é truthy, sem exceção, e array e função são objetos. Para string, o critério é ter pelo menos um caractere — '0', 'false' e ' ' têm. Para número, o critério é ser diferente de zero e não ser NaN; -1 e Infinity passam.

Boolean() e a dupla negação

Duas formas de forçar a conversão, com resultado idêntico:

js
const digitado = '';
const digitadoZero = '0';
const digitadoTexto = 'três';

console.log(Boolean(digitado), Boolean(Number(digitado)));
console.log(Boolean(digitadoZero), Boolean(Number(digitadoZero)));
console.log(Boolean(digitadoTexto), Boolean(Number(digitadoTexto)));
console.log(Number(''), Number('0'), Number('três'));
false false true false true false 0 0 NaN

A segunda linha é o resumo da lição: a string '0' é truthy, e o número 0 que ela vira é falsy. Um campo de quantidade que chega como texto muda de comportamento dependendo de você ter convertido antes ou não. Sobre por que Number('') é 0 e não NaN, a lição de operadores aritméticos mostra a mesma conversão acontecendo dentro das contas.

O filter(Boolean) e o que ele leva junto

Passar Boolean direto para o filter é um idioma que todo mundo reconhece — mas ele filtra os oito falsy, não só os que você tinha em mente:

js
const cupons = ['PRIMEIRACOMPRA', '', null, 'FRETEGRATIS', undefined, 0, 'BLACKFRIDAY'];

console.log(cupons.filter(Boolean));
console.log(cupons.filter((c) => c).length);

const quantidades = [3, 0, 2, 0, 1];
console.log(quantidades.filter(Boolean));
console.log(quantidades.filter((q) => q !== 0));
[ 'PRIMEIRACOMPRA', 'FRETEGRATIS', 'BLACKFRIDAY' ] 3 [ 3, 2, 1 ] [ 3, 2, 1 ]

Nos cupons, o resultado é o desejado. Nas quantidades, as duas linhas coincidem por acaso — e a segunda diz o que quer dizer. Quando o array pode conter zero como valor válido, escreva a condição explícita.

Zero é um valor, não uma ausência

Numa loja, 0 aparece o tempo todo como resultado legítimo: frete grátis, desconto nenhum, estoque zerado. Todo if que trata o zero como “não veio” esconde informação do cliente:

js
function linhaDoResumo(pedido) {
  if (pedido.desconto) {
    return `desconto de R$ ${pedido.desconto.toFixed(2)}`;
  }
  return 'sem desconto';
}

console.log(linhaDoResumo({ desconto: 30 }));
console.log(linhaDoResumo({ desconto: 0 }));
console.log(linhaDoResumo({}));

function linhaCorreta(pedido) {
  if (pedido.desconto !== undefined) {
    return `desconto de R$ ${pedido.desconto.toFixed(2)}`;
  }
  return 'sem desconto';
}

console.log(linhaCorreta({ desconto: 0 }));
desconto de R$ 30.00 sem desconto sem desconto desconto de R$ 0.00

As linhas 2 e 3 saíram iguais para situações diferentes: um pedido em que o desconto foi calculado e deu zero, e um pedido em que o campo nem existe. A versão de baixo separa os dois casos com uma comparação explícita — e é a mesma distinção que separa || de ?? em operadores lógicos.

Erros comuns

if (carrinho.itens) é sempre verdadeiro

js
const carrinho = { cliente: 'Ana', itens: [] };

if (carrinho.itens) {
  console.log('o carrinho tem itens');
} else {
  console.log('carrinho vazio');
}

console.log('quantidade:', carrinho.itens.length);
o carrinho tem itens quantidade: 0

O programa anunciou que o carrinho tem itens e em seguida contou zero. A condição testou se carrinho.itens existe, não se ele tem conteúdo — e um array vazio existe. Num checkout real, isso é o botão “Finalizar compra” habilitado num carrinho sem nada dentro.

A correção é dizer o que você quer medir:

js
const carrinho = { cliente: 'Ana', itens: [] };

if (carrinho.itens.length > 0) {
  console.log('o carrinho tem itens');
} else {
  console.log('carrinho vazio');
}

console.log(Boolean(carrinho.itens));
console.log(Boolean(carrinho.itens.length));
carrinho vazio true false

As duas últimas linhas mostram o par lado a lado: o array é truthy, o length dele é falsy. Testar o length é o hábito a pegar.

O objeto vazio que passa no if e quebra na linha seguinte

Este é o mesmo erro com consequência pior, porque agora ele lança:

js
const pedido = { id: 1042, entrega: {} };

if (pedido.entrega) {
  console.log('endereço cadastrado, montando etiqueta');
  console.log(pedido.entrega.cidade.toUpperCase());
}
endereço cadastrado, montando etiqueta file:///private/tmp/club-store/etiqueta.mjs:5 console.log(pedido.entrega.cidade.toUpperCase()); ^

TypeError: Cannot read properties of undefined (reading ‘toUpperCase’) at file:///private/tmp/club-store/etiqueta.mjs:5:37 at ModuleJob.run (node:internal/modules/esm/module_job:439:25) at async node:internal/modules/esm/loader:633:26 at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:101:5)

Node.js v24.16.0

A primeira linha imprimiu: o if passou, porque {} é truthy. Só que pedido.entrega.cidade é undefined, e undefined.toUpperCase() derruba o processo. A guarda deu uma falsa sensação de segurança — que é o pior tipo de guarda. Esse rastro é o mesmo do TypeError: Cannot read properties of undefined, o erro mais comum da linguagem.

Duas correções, dependendo do que você quer:

js
const pedido = { id: 1042, entrega: {} };

const temEndereco = Object.keys(pedido.entrega).length > 0;

console.log('tem endereço?', temEndereco);
console.log(pedido.entrega.cidade?.toUpperCase() ?? 'RETIRADA NA LOJA');
tem endereço? false RETIRADA NA LOJA

Object.keys(obj).length responde “esse objeto tem alguma coisa dentro”. O encadeamento opcional com ?? responde “me dê isso ou um substituto”. A escolha entre ?? e || aqui importa, e é o assunto de operadores lógicos.

Objetos empacotados: new Boolean(false) é truthy

Raro, mas quando aparece consome uma tarde:

js
console.log(Boolean(new Boolean(false)));
console.log(Boolean(''), Boolean(new String('')));
console.log(Boolean(0), Boolean(new Number(0)));
console.log(typeof new Boolean(false));
true false true false true object

new Boolean(false) não é o valor false: é um objeto que embrulha false — e todo objeto é truthy. O mesmo vale para new String('') e new Number(0). Nunca use os construtores new Boolean, new String ou new Number. Sem o new, Boolean(x), String(x) e Number(x) são funções de conversão perfeitas.

Checklist para escrever a condição certa

o que você quer saber condição errada condição certa
o array tem itens if (itens) if (itens.length > 0)
o objeto tem campos if (dados) if (Object.keys(dados).length)
o campo foi preenchido if (nome) if (nome.trim() !== '')
o número foi informado if (quantidade) if (quantidade !== undefined)
a conta deu certo if (total) if (!Number.isNaN(total))
o elemento existe no DOM if (elemento) é o correto aqui

A última linha é a exceção honesta: document.querySelector devolve null quando não acha, e null é falsy, então testar o valor direto é idiomático. Fora desse caso, prefira dizer o que está medindo.

Um bom hábito de leitura: sempre que encontrar um if com uma variável solta dentro, pergunte “quais dos oito falsy podem chegar aqui?”. Se a resposta incluir um valor legítimo do seu domínio — um frete zero, um comentário vazio — a condição está errada mesmo que o teste esteja passando. A lição de variáveis em JavaScript mostra o outro lado disso: o valor que ainda não existe versus o valor que existe e está vazio. O guia de JavaScript encadeia os dois assuntos no caminho completo.

  • truthy
  • falsy
  • booleanos
  • conversao
  • condicionais

Perguntas frequentes

Por que array vazio é truthy se ele não tem nada dentro?
Porque a conversão para booleano olha o tipo, não o conteúdo. Todo objeto é truthy, e array é objeto. O JavaScript não abre a caixa para contar o que tem dentro — quem precisa disso escreve a condição explícita, com .length ou Object.keys.
A lista de valores falsy pode crescer com uma versão nova?
Na prática, não. Ela ficou fechada em oito e o único item novo em mais de vinte anos foi o 0n, que veio junto com o BigInt no ES2020. Qualquer valor falsy futuro teria que ser um tipo primitivo novo, o que é raríssimo.
Usar !! para converter é considerado ruim?
Não é ruim, é econômico demais para código que outra pessoa lê. Boolean(x) diz a mesma coisa por extenso e não some no meio de uma expressão longa. Em arr.filter(Boolean), porém, a forma curta é idiomática e todo mundo reconhece.
document.querySelector devolve falsy quando não acha?
Devolve null, que é falsy — por isso if (elemento) é o padrão para checar se o elemento existe antes de mexer nele. É um dos poucos lugares em que testar o valor direto, sem comparação, é a forma correta e idiomática.

Dúvidas e comentários

Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.

Todo o código deste artigo foi executado em Node 24.16.0, e as saídas exibidas são as reais — como produzimos este conteúdo.

Fontes consultadas

  1. MDN — Falsy — developer.mozilla.org
  2. MDN — Truthy — developer.mozilla.org
  3. ECMAScript 2026 Language Specification — ToBoolean — tc39.es

Continue por aqui