Tipos de dados: string, number, boolean, null e undefined

Introdução aos Tipos Primitivos em JavaScript

Em JavaScript, os tipos de dados são a base sobre a qual toda a linguagem é construída. Eles definem que tipo de valor uma variável pode armazenar e quais operações podem ser realizadas com ela. Os tipos primitivos são os blocos fundamentais: string, number, boolean, null e undefined.

Diferentemente de objetos, que são mutáveis e armazenados por referência, os primitivos são imutáveis e armazenados por valor. Essa distinção é crucial em Node.js (para manipulação eficiente de dados) e React (para gerenciamento de estado e renderização). Quando você altera um primitivo, na verdade está criando um novo valor, enquanto objetos mantêm a mesma referência.

String: O Tipo Textual

Strings representam texto e são criadas de três formas:

// Aspas simples
const nome = 'Maria';

// Aspas duplas
const sobrenome = "Silva";

// Template literals (crases) - permitem interpolação
const nomeCompleto = `${nome} ${sobrenome}`;
console.log(nomeCompleto); // "Maria Silva"

Operações comuns incluem concatenação e uso de métodos:

const saudacao = 'Olá, ';
const mensagem = saudacao.concat(nomeCompleto);
console.log(mensagem); // "Olá, Maria Silva"

console.log(mensagem.length); // 18
console.log(mensagem.toUpperCase()); // "OLÁ, MARIA SILVA"

Em Node.js, ao manipular dados de entrada (como parâmetros de API), sempre valide strings:

// Exemplo em uma rota Express
app.post('/usuario', (req, res) => {
  const { nome } = req.body;
  if (typeof nome !== 'string' || nome.trim() === '') {
    return res.status(400).json({ erro: 'Nome inválido' });
  }
});

Em React, strings são renderizadas diretamente no JSX:

function Saudacao({ nome }) {
  return <h1>{`Bem-vindo, ${nome || 'visitante'}!`}</h1>;
}

Number: O Tipo Numérico

JavaScript não diferencia inteiros de decimais — tudo é ponto flutuante de 64 bits:

const inteiro = 42;
const decimal = 3.14;
const notacaoCientifica = 1.5e6; // 1.500.000

console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991

Valores especiais merecem atenção:

console.log(1 / 0); // Infinity
console.log(-1 / 0); // -Infinity
console.log('abc' * 2); // NaN (Not-a-Number)
console.log(typeof NaN); // 'number' (sim, NaN é do tipo number!)

Armadilhas com precisão decimal:

console.log(0.1 + 0.2); // 0.30000000000000004 (não é 0.3!)
// Solução: arredondar ou usar bibliotecas como decimal.js
console.log((0.1 + 0.2).toFixed(2)); // "0.30"

Em Node.js, ao processar dados financeiros, sempre use arredondamento explícito:

function calcularTotal(preco, quantidade) {
  const total = preco * quantidade;
  return parseFloat(total.toFixed(2));
}

Boolean: O Tipo Lógico

Booleanos representam verdadeiro ou falso:

const ativo = true;
const completo = false;

Operadores lógicos e curto-circuito:

const usuario = { nome: 'João' };
const nomeExibido = usuario.nome || 'Anônimo'; // "João"

const admin = true;
const podeAcessar = admin && usuario.nome; // "João" (curto-circuito: se admin for false, não avalia o resto)

Em React, booleanos são essenciais para renderização condicional:

function Painel({ usuario, carregando }) {
  if (carregando) return <p>Carregando...</p>;

  return (
    <div>
      {usuario?.logado ? (
        <p>Bem-vindo, {usuario.nome}!</p>
      ) : (
        <button onClick={login}>Entrar</button>
      )}
    </div>
  );
}

Null e Undefined: Ausência de Valor

undefined é o valor padrão de variáveis não inicializadas:

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

const obj = {};
console.log(obj.propriedadeInexistente); // undefined

null é um valor intencionalmente vazio, atribuído explicitamente:

const resposta = null; // "Não há resposta ainda"

Diferenças cruciais:

console.log(null == undefined); // true (igualdade solta)
console.log(null === undefined); // false (igualdade estrita)
console.log(typeof null); // 'object' (erro histórico do JS)
console.log(typeof undefined); // 'undefined'

Em Node.js, ao lidar com bancos de dados:

// MongoDB: null vs undefined
const usuario = await db.collection('usuarios').findOne({ email });
if (usuario === null) {
  // Usuário não encontrado (null do banco)
} else if (usuario.telefone === undefined) {
  // Campo telefone não existe no documento
}

Em React, use null para indicar que um componente não deve renderizar nada:

function ListaItens({ itens }) {
  if (itens === null || itens === undefined) {
    return null; // Não renderiza nada
  }
  return <ul>{itens.map(item => <li key={item.id}>{item.nome}</li>)}</ul>;
}

Verificação e Conversão entre Tipos

O operador typeof é essencial para identificar tipos:

console.log(typeof 'texto'); // 'string'
console.log(typeof 42); // 'number'
console.log(typeof true); // 'boolean'
console.log(typeof null); // 'object' (cuidado!)
console.log(typeof undefined); // 'undefined'

Conversão explícita (coerção):

const numero = String(123); // "123"
const texto = Number('42'); // 42
const booleano = Boolean(0); // false (0, '', null, undefined, NaN viram false)

A armadilha do == vs ===:

console.log(5 == '5'); // true (coerção implícita)
console.log(5 === '5'); // false (tipos diferentes)

console.log(null == undefined); // true
console.log(null === undefined); // false

Boas práticas: sempre use === (igualdade estrita) para evitar bugs sutis.

Casos Práticos em Node.js e React

Node.js: Validação de tipos em API

// API REST com Express
app.post('/produto', (req, res) => {
  const { nome, preco, quantidade } = req.body;

  if (typeof nome !== 'string' || nome.length < 3) {
    return res.status(400).json({ erro: 'Nome deve ter pelo menos 3 caracteres' });
  }

  const precoNumerico = Number(preco);
  if (isNaN(precoNumerico) || precoNumerico <= 0) {
    return res.status(400).json({ erro: 'Preço inválido' });
  }

  const quantidadeNumerica = Number(quantidade) || 0;
  // null ou undefined viram 0
});

React: Componente com tipagem e estados iniciais

import PropTypes from 'prop-types';

function PerfilUsuario({ usuario }) {
  // Estado inicial com null
  const [dados, setDados] = React.useState(null);
  const [erro, setErro] = React.useState(undefined);

  React.useEffect(() => {
    fetch(`/api/usuarios/${usuario.id}`)
      .then(res => res.json())
      .then(data => setDados(data))
      .catch(err => setErro(err.message));
  }, [usuario.id]);

  if (dados === null) return <p>Carregando...</p>;
  if (erro !== undefined) return <p>Erro: {erro}</p>;

  return (
    <div>
      <h2>{dados.nome}</h2>
      <p>Email: {dados.email || 'Não informado'}</p>
    </div>
  );
}

PerfilUsuario.propTypes = {
  usuario: PropTypes.shape({
    id: PropTypes.number.isRequired,
    nome: PropTypes.string
  }).isRequired
};

Exemplo integrado: Componente React consumindo API Node.js

// Backend (Node.js)
app.get('/api/dashboard/:id', (req, res) => {
  const id = Number(req.params.id);
  if (isNaN(id)) return res.status(400).json({ erro: 'ID inválido' });

  const dados = {
    nome: 'Ana',
    idade: 28,
    ativo: true,
    ultimoAcesso: null,
    preferencias: undefined
  };
  res.json(dados);
});

// Frontend (React)
function Dashboard() {
  const [dados, setDados] = useState(null);

  useEffect(() => {
    fetch('/api/dashboard/1')
      .then(res => res.json())
      .then(data => setDados(data));
  }, []);

  if (dados === null) return <p>Carregando...</p>;

  return (
    <div>
      <h1>{dados.nome}</h1>
      <p>Idade: {typeof dados.idade === 'number' ? dados.idade : 'N/A'}</p>
      <p>Status: {dados.ativo ? 'Ativo' : 'Inativo'}</p>
      <p>Último acesso: {dados.ultimoAcesso ?? 'Nunca acessou'}</p>
      <p>Preferências: {dados.preferencias ?? 'Padrão'}</p>
    </div>
  );
}

Dominar os tipos primitivos em JavaScript é essencial para escrever código robusto em Node.js e React. A compreensão de como strings, números, booleanos, null e undefined se comportam evita bugs comuns e melhora a qualidade do software.


Referências