Em JavaScript, entender os tipos de dados é fundamental para escrever código correto e previsível. A linguagem possui um sistema de tipos dinâmico e fracamente tipado, o que significa que as variáveis podem conter qualquer tipo de dado e que a coerção automática entre tipos é comum. Nesta aula, vamos detalhar os tipos primitivos e o tipo objeto, suas características, exemplos e as pegadinhas que frequentemente confundem desenvolvedores.

Dominar esses conceitos evita bugs sutis e ajuda a aproveitar melhor os recursos da linguagem. Vamos começar com a classificação dos tipos.

Tipos primitivos e objeto

JavaScript define sete tipos primitivos: string, number, boolean, undefined, null, symbol (ES6) e bigint (ES2020). Todos os demais valores são objetos, incluindo arrays, funções, datas, expressões regulares, etc. Objetos são coleções de pares chave-valor e podem ser criados com a sintaxe literal ou com construtores.

Primitivos são imutáveis e não têm métodos próprios (quando você chama um método em um primitivo, o JavaScript o envolve temporariamente em um objeto wrapper). Já objetos são mutáveis e armazenam referências.

// Primitivos
let nome = 'João';
let idade = 25;
let ativo = true;
let indefinido;
let vazio = null;
let id = Symbol('id');
let big = 123n;

// Objetos
let pessoa = { nome: 'Maria', idade: 30 };
let numeros = [1, 2, 3];
function soma(a, b) { return a + b; }

Particularidades de cada tipo

Cada tipo tem comportamentos específicos. String pode ser delimitada por aspas simples, duplas ou crases (template literals). Number inclui inteiros e ponto flutuante, além de valores especiais como Infinity, -Infinity e NaN. Boolean tem dois valores: true e false. Undefined indica ausência de valor, enquanto null é uma atribuição intencional de vazio. Symbol é único e imutável, usado como chave de propriedade. BigInt permite números inteiros arbitrariamente grandes.

Os objetos, por sua vez, têm protótipos e podem ser estendidos. Arrays são objetos com índices numéricos e métodos especiais. Funções são objetos chamáveis.

// Strings
let str1 = 'Olá';
let str2 = "Mundo";
let str3 = `Oi ${str1}`; // template literal

// Numbers
let int = 42;
let float = 3.14;
let inf = Infinity;
let nan = NaN;

// Boolean
let verdade = true;

// Undefined
let x;
console.log(x); // undefined

// Null
let y = null;

// Symbol
let sym = Symbol('chave');

// BigInt
let bigInt = 9007199254740991n;

Exemplos

Vamos ver exemplos práticos que mostram como os tipos se comportam em operações comuns. Note a diferença entre primitivos e objetos na comparação e atribuição.

// Comparação de primitivos (por valor)
let a = 'abc';
let b = 'abc';
console.log(a === b); // true

// Comparação de objetos (por referência)
let obj1 = { valor: 10 };
let obj2 = { valor: 10 };
console.log(obj1 === obj2); // false

// Atribuição de primitivos (cópia)
let num1 = 5;
let num2 = num1;
num2 = 10;
console.log(num1); // 5 (não afetado)

// Atribuição de objetos (referência)
let arr1 = [1, 2];
let arr2 = arr1;
arr2.push(3);
console.log(arr1); // [1, 2, 3] (afetado)

Pegadinhas (typeof null, NaN, coerção)

JavaScript tem comportamentos surpreendentes. typeof null retorna "object", um erro histórico. NaN (Not-a-Number) é do tipo number e é o único valor que não é igual a si mesmo. A coerção automática pode levar a resultados inesperados, como "5" - 2 resultando em 3 (string convertida para número), mas "5" + 2 resulta em "52" (número convertido para string).

// typeof null
console.log(typeof null); // "object" (pegadinha)

// NaN
console.log(typeof NaN); // "number"
console.log(NaN === NaN); // false
console.log(isNaN(NaN)); // true

// Coerção
console.log('5' - 2); // 3 (string '5' vira número)
console.log('5' + 2); // "52" (número 2 vira string)
console.log('5' * '2'); // 10 (ambos viram números)
console.log(true + 1); // 2 (true vira 1)
console.log(false + 1); // 1 (false vira 0)
console.log(null + 1); // 1 (null vira 0)
console.log(undefined + 1); // NaN (undefined vira NaN)

Boas práticas

Para evitar problemas, use sempre === (igualdade estrita) em vez de ==, pois o == faz coerção de tipos. Verifique NaN com Number.isNaN() em vez de isNaN() global. Lembre-se de que null e undefined são diferentes: null é atribuído intencionalmente, undefined indica ausência de atribuição. Ao trabalhar com objetos, cuidado com mutações indesejadas.

Referências

Exercícios

  1. Qual é o resultado de typeof null? Por que isso é considerado uma pegadinha?

    ✓ Resposta: typeof null retorna "object". Isso é uma pegadinha porque null é um tipo primitivo, mas o operador typeof retorna incorretamente "object" devido a um bug histórico no JavaScript que não pode ser corrigido por questões de compatibilidade.
  2. Explique por que NaN === NaN retorna false.

    ✓ Resposta: De acordo com a especificação IEEE 754 para números de ponto flutuante, NaN (Not-a-Number) não é igual a si mesmo. Isso é intencional para indicar que o resultado de uma operação inválida não é um número. Para verificar se um valor é NaN, use Number.isNaN().
  3. Qual o resultado de "10" + 5 e "10" - 5? Explique a diferença.

    ✓ Resposta: "10" + 5 resulta em "105" (string), pois o operador + com uma string realiza concatenação. Já "10" - 5 resulta em 5 (número), porque o operador - força a conversão da string para número.
  4. O que é um tipo primitivo? Dê exemplos.

    ✓ Resposta: Tipos primitivos são tipos de dados imutáveis e não são objetos. Em JavaScript, são: string, number, boolean, undefined, null, symbol e bigint. Exemplos: 'Olá', 42, true, undefined, null, Symbol('id'), 123n.
  5. Qual a diferença entre undefined e null?

    ✓ Resposta: undefined é o valor padrão de variáveis declaradas mas não inicializadas, e indica ausência de valor. null é um valor atribuído intencionalmente para representar vazio ou nenhum objeto. Eles são diferentes: undefined === null retorna false, mas undefined == null retorna true devido à coerção.