Tipos Primitivos: A Base de Tudo
Em JavaScript, existem sete tipos primitivos: string, number, boolean, null, undefined, symbol e bigint. Eles são imutáveis e armazenados diretamente na memória, o que os torna eficientes para operações básicas. Quando você trabalha em produção, entender como esses tipos se comportam é crucial para evitar bugs sutis.
// Exemplos de tipos primitivos
const nome = "João"; // string
const idade = 30; // number
const ativo = true; // boolean
const vazio = null; // null (ausência intencional)
const naoDefinido = undefined; // undefined (não foi atribuído)
const id = Symbol('user'); // symbol (único e imutável)
const grandeNumero = 900719925474099n; // bigint
// Verificar tipos com typeof
console.log(typeof nome); // "string"
console.log(typeof idade); // "number"
console.log(typeof ativo); // "boolean"
console.log(typeof null); // "object" (bug histórico do JS!)
console.log(typeof naoDefinido); // "undefined"
Um erro comum em produção é confundir null com undefined. Use null quando você intencionalmente quer representar ausência, e undefined aparece quando uma variável não foi atribuída ou uma função não retorna nada. Para verificar com segurança se algo é nulo, prefira valor === null || valor === undefined ou use o operador nullish coalescing (??).
Objetos e Referências: Comportamento em Memória
Diferente dos primitivos, objetos são tipos de referência. Eles ocupam mais memória e são armazenados como referências, não valores diretos. Em aplicações grandes, isso afeta performance e pode gerar comportamentos inesperados se não for bem compreendido.
// Primitivo: cópia por valor
let a = 10;
let b = a;
b = 20;
console.log(a); // 10 (não foi afetado)
// Objeto: cópia por referência
const usuario1 = { nome: "Ana", idade: 28 };
const usuario2 = usuario1;
usuario2.idade = 29;
console.log(usuario1.idade); // 29 (ambos apontam para o mesmo objeto!)
// Para copiar objetos com segurança, use spread operator
const usuario3 = { ...usuario1 };
usuario3.idade = 25;
console.log(usuario1.idade); // 29 (agora sim, são independentes)
// Arrays também são objetos
const numeros = [1, 2, 3];
const copia = [...numeros]; // cópia segura
copia[0] = 999;
console.log(numeros[0]); // 1 (seguro)
Em produção, essa diferença causa problemas reais. Se você passa um objeto para uma função e a função o modifica, o original também muda. Para evitar isso, sempre faça cópias profundas quando necessário: use JSON.parse(JSON.stringify(obj)) para casos simples ou bibliotecas como lodash.clonedeep() para estruturas complexas.
Coerção de Tipos: O Perigo Invisível
A coerção de tipos é quando JavaScript converte automaticamente um tipo em outro. É poderosa, mas perigosa em produção se não for controlada. O JavaScript faz isso em comparações (==), operações aritméticas e contextos lógicos.
// Comparação solta (==) causa coerção
console.log(0 == false); // true (ambos são "falsy")
console.log("5" == 5); // true (string convertida para number)
console.log(null == undefined); // true (coerção estranha!)
// Comparação estrita (===) NÃO causa coerção
console.log(0 === false); // false
console.log("5" === 5); // false
console.log(null === undefined); // false
// Operações aritméticas forçam coerção
console.log("10" - "5"); // 5 (strings convertidas)
console.log("10" + 5); // "105" (concatenação, não soma!)
console.log(true + 1); // 2 (true vira 1)
// Valores "falsy" em JavaScript
const falsy = [false, 0, "", null, undefined, NaN];
falsy.forEach(val => {
if (!val) console.log(`${val} é falsy`);
});
// Conversão explícita é melhor para produção
const entrada = "42";
const numero = Number(entrada); // 42
const string = String(100); // "100"
const booleano = Boolean("texto"); // true
Boas Práticas com Coerção
Sempre use === em vez de == — essa é a regra de ouro. Ferramentas como ESLint forçam isso automaticamente. Converta tipos explicitamente quando necessário e documente a razão. Isso torna o código mais seguro e facilita manutenção futura por outros desenvolvedores.
Tipos em Produção: Aplicação Prática
Um exemplo real: ao processar dados de uma API, você recebe strings que precisam virar números. Se usar == para comparar, pode levar horas debugando comportamentos estranhos.
// Simulando dados de uma API
const dados = {
id: "123",
preco: "99.99",
em_estoque: "true"
};
// ❌ Errado em produção
if (dados.em_estoque == true) {
console.log("Disponível");
}
// ✅ Certo
const preco = parseFloat(dados.preco);
const emEstoque = dados.em_estoque === "true";
if (emEstoque && preco > 50) {
console.log(`Produto caro: R$ ${preco.toFixed(2)}`);
}
// Validação de tipos com guard clauses
function processocriarPedido(usuario, itens) {
if (typeof usuario !== "object" || usuario === null) {
throw new Error("Usuário deve ser um objeto válido");
}
if (!Array.isArray(itens) || itens.length === 0) {
throw new Error("Itens deve ser um array não-vazio");
}
// Continua a lógica...
return { sucesso: true };
}
console.log(processocriarPedido({ id: 1 }, [{ id: 1, qtd: 2 }]));
Conclusão
Os tipos em JavaScript definem como seu código se comporta em produção. Primeiro, domine os sete tipos primitivos e quando usar null versus undefined. Segundo, entenda que objetos são referências, não cópias, e implemente cópias seguras quando necessário. Terceiro, evite coerção implícita usando === e conversões explícitas — isso previne bugs sutis que custam caro em produção. Aplique estas práticas desde o início e seu código será mais confiável e manutenível.