Dia 3 — Tipos, operadores e comparação

Informatica · Conteudo · publicado em 05/10/2026
Dia 3 de 13

Tipos, operadores e comparação

Aula 1

Os tipos de dado e typeof

Os tipos de dado e typeof

Cada valor tem um tipo de dado, e o JavaScript não tem tipos declarados: uma variável pode guardar número e, na

linha seguinte, texto. Mas todo valor tem um tipo, e typeof é o operador que

devolve o nome dele como texto.

console.log(typeof 42);        // number
console.log(typeof 'Ana');     // string
console.log(typeof true);      // boolean
console.log(typeof {});        // object

São oito tipos, e a lista inteira cabe numa chamada:

TipoExemplotypeof
string'Ana', ` Ana `string
number42, 3.5, 10nnumber
booleantrue, falseboolean
object{}, [], nullobject
undefinedvariável sem valorundefined
symbolSymbol('id')symbol
bigint10nbigint
function() => {}function

O function não é um tipo primitivo, mas typeof devolve o nome dele mesmo

assim, e é por isso que typeof serve para distinguir função de objeto comum.

A lista da tabela é conferida de uma vez no exemplo: ele junta os dez typeof em

um único objeto e imprime o resultado como `typeof: { string: 'string', number:

'number', ... }`. É a forma de conferir uma tabela sem escrever dez linhas de

console. A linha repara em duas coisas que a tabela deixa implícitas — array

aparece com typeof 'object', e objeto também; funcao aparece com

'function', separado dos dois.

Os três casos que confundem

typeof null devolve 'object'. Isso é um erro histórico do JavaScript, de

1995, que ficou por compatibilidade: a correção quebraria código antigo em todo

o mundo, então ninguém corrigiu. A consequência prática é que `typeof x ===

'object' não significa "tem objeto" — significa "tem objeto ou é null`".

typeof undefined devolve 'undefined', e isso é correto. A diferença entre os

dois importa: undefined é o valor de uma coisa que não existe ainda;

null é um valor que existe e foi posto de propósito para dizer "vazio".

typeof [] devolve 'object', não 'array'. Array é um tipo de objeto

específico, e o jeito de saber se um valor é array é Array.isArray(valor):

console.log(typeof [1, 2]);        // object
console.log(Array.isArray([1, 2])); // true

A linha do exemplo que trata dos três juntos é

typeof null devolve object: object. Ela imprime o nome do tipo de null e a

resposta é a mesma do objeto. A linha seguinte usa Array.isArray nos dois

candidatos e imprime true false: o array é array, o objeto não é. Perceba a

direção da checagem — Array.isArray é a pergunta "isto é array?", e ela não

depende do typeof.

Os primitivos e os números que não são número

Um primitivo é o valor que vem pronto, sem estrutura interna: string,

number, boolean, undefined, symbol e bigint. Objeto e array não são

primitivos.

Dentro de number existem três valores que não se comportam como número:

  • NaN é o resultado de uma conta que não faz sentido — 'abc' * 2 dá

NaN. E NaN não é igual a si mesmo, nem com ===: é o único valor do

JavaScript que não é igual a ele mesmo.

  • Infinity é o infinito. Sai de 1 / 0, e não trava o programa.
  • -0 é zero com sinal, e Object.is(-0, 0) distingue os dois quando isso

importa.

console.log(NaN === NaN);          // false
console.log(Number.isNaN(NaN));    // true
console.log(1 / 0);                // Infinity

O typeof sozinho não serve para testar se um número é válido, porque NaN é do

tipo number. Para isso o método certo é Number.isNaN.

O exemplo junta os três em duas linhas. 'abc' * 2: NaN | typeof NaN: number

mostra o NaN nascendo de uma conta sem sentido e, ao lado, o tipo que ele

declara — number. E NaN === NaN: false | Number.isNaN(NaN): true mostra os

dois lados da mesma pergunta: o === responde que não, o Number.isNaN

responde que sim. A linha seguinte, 1 / 0: Infinity | typeof Infinity: number,

fecha o grupo: o infinito também é number, e também não derruba o programa.

Número de ponto flutuante não é exato

0.1 + 0.2 não dá 0.3, dá 0.30000000000000004, e a comparação

0.1 + 0.2 === 0.3 é false. Isso não é defeito do React Native nem do seu

código: número de ponto flutuante guarda o valor em binário, e 0,1 e 0,2 não têm

representação exata em binário. Aparece em tela como 0.30000000000000004 e

vira Number(x.toFixed(2)) ou parseFloat(x.toFixed(2)) quando o número vai

para a tela.

O exemplo reproduz os três passos em três linhas: `0.1 + 0.2:

0.30000000000000004, igual a 0.3? false e arredondado para a tela: 0.3`. O

toFixed(2) sozinho resolveria a exibição, mas devolveria texto — e é por isso

que o Number(...) em volta existe: o console mostra 0.3 como número, e a

conta seguinte do app continua funcionando.

O detalhe que fecha a aula é a função testar do exemplo. Ela devolve

objeto para {}, nulo para null e objeto para [] — e o texto da

linha é testar({}): objeto | testar(null): nulo | testar([]): objeto. A

checagem do null vem antes do typeof, porque typeof null é

'object' e a ordem invertida daria a resposta errada. É o mesmo cuidado que a

tabela acima avisa: a pergunta certa sobre objeto nunca é só o typeof.

Exemplo

// Os tipos de dado e o que typeof devolve. O exemplo inclui os tres casos
// que confundem: typeof null, typeof array e NaN.
const valores = {
  nome: 'Ana',
  idade: 30,
  ativo: true,
  tarefas: ['estudar'],
  nada: null,
  semValor: undefined,
};

console.log('typeof:', {
  string: typeof valores.nome,
  number: typeof valores.idade,
  boolean: typeof valores.ativo,
  objeto: typeof valores,
  array: typeof valores.tarefas,
  nulo: typeof valores.nada,
  indefinido: typeof valores.semValor,
  funcao: typeof (() => {}),
  simbolo: typeof Symbol('id'),
  bigint: typeof 10n,
});

console.log('typeof null devolve object:', typeof null);
console.log('array e object, o jeito de saber e Array.isArray:',
  Array.isArray(valores.tarefas), Array.isArray(valores));

// os numeros que nao se comportam como numero
console.log("'abc' * 2:", 'abc' * 2, '| typeof NaN:', typeof NaN);
console.log('NaN === NaN:', NaN === NaN, '| Number.isNaN(NaN):', Number.isNaN(NaN));
console.log('1 / 0:', 1 / 0, '| typeof Infinity:', typeof Infinity);

// ponto flutuante: 0.1 + 0.2 nao e 0.3
console.log('0.1 + 0.2:', 0.1 + 0.2);
console.log('igual a 0.3?', 0.1 + 0.2 === 0.3);
console.log('arredondado para a tela:', Number((0.1 + 0.2).toFixed(2)));

// o typeof nao distingue objeto de null, entao a checagem precisa ser melhor
function testarObjetoMal(valor) { return typeof valor === 'object' && valor !== null; }
function testar(valor) {
  if (valor === null) return 'nulo';
  return typeof valor === 'object' ? 'objeto' : typeof valor;
}
console.log('testar({}):', testar({}), '| testar(null):', testar(null), '| testar([]):', testar([]));

Saída real

typeof: {
  string: 'string',
  number: 'number',
  boolean: 'boolean',
  objeto: 'object',
  array: 'object',
  nulo: 'object',
  indefinido: 'undefined',
  funcao: 'function',
  simbolo: 'symbol',
  bigint: 'bigint'
}
typeof null devolve object: object
array e object, o jeito de saber e Array.isArray: true false
'abc' * 2: NaN | typeof NaN: number
NaN === NaN: false | Number.isNaN(NaN): true
1 / 0: Infinity | typeof Infinity: number
0.1 + 0.2: 0.30000000000000004
igual a 0.3? false
arredondado para a tela: 0.3
testar({}): objeto | testar(null): nulo | testar([]): objeto
Aula 2

Operadores, comparação e o caso de == contra ===

Operadores, comparação e o caso de == contra ===

A comparação tem duas formas, e a diferença entre elas é a origem de uma

quantidade grande de bug em JavaScript. == compara só o valor, convertendo

um lado para o tipo do outro — a conversão implícita ou type coercion. ===

compara tipo e valor, sem converter nada.

console.log('0' == 0);      // true
console.log('0' === 0);     // false

Usar === sempre é a regra. == não tem caso em que seja a única escolha, e a

lista de valores que ele considera iguais é longa demais para ser memorizada —

ela inclui '0' e 0 — o caso "0" == 0, o mais citado —, '' e 0, [] e

false, null e undefined. Cada um

desses pares passa no == e falha no ===, e nenhum deles é o que o autor do

código queria dizer.

O exemplo percorre a lista inteira em duas linhas, uma por operador. A de ==

imprime '0' == 0: true | 0 == false: true | '' == 0: true e, na linha seguinte,

null == undefined: true | [] == false: true. A de === imprime

'0' === 0: false | 0 === false: false e

null === undefined: false | null == undefined: true. A última linha é a mais

instruativa do exemplo, porque mistura os dois operadores na mesma comparação:

null == undefined é true ao lado de null === undefined que é false, e as

duas afirmações são verdadeiras ao mesmo tempo.

Os operadores aritméticos

OperadorO que fazExemploResultado
+soma2 + 35
-subtração7 - 25
*multiplicação4 * 312
/divisão7 / 23.5
%resto (módulo)7 % 31
**potenciação2 ** 38

% é o resto da divisão, não o resto de resto: 7 % 3 é 1, e

-7 % 3 é -1, porque o resto herda o sinal do dividendo. O uso mais comum dele

é checar se um número é par (n % 2 === 0) e é o que separa item par de item

ímpar numa lista.

Precedência é a ordem em que o JavaScript resolve os operadores: *, / e

% antes de + e -, e exponenciação antes de todos. 2 + 3 * 4 é 14, não

20. Quando a ordem pretendida é outra, os parênteses escrevem a ordem, e

usar parênteses mesmo quando não é preciso é o estilo que evita a discussão.

A tabela e a precedência são conferidas lado a lado no exemplo. A aritmética

aparece em três linhas, com resto negativo: -1 ao lado de resto: 1 para

mostrar o sinal, e 2 ** 0.5: 1.4142135623730951 para lembrar que a

potenciação aceita fração. A precedência vem na linha seguinte, e é

2 + 3 4: 14 | (2 + 3) 4: 20 — os dois lados da frase do parágrafo, com a

resposta ao lado do resultado que não muda.

O + que concatena e o + que soma

O + é o único operador que decide entre somar e juntar texto conforme os tipos

dos dois lados. Com dois números, soma. Se qualquer lado for string, junta:

console.log('5' + 2);   // '52'
console.log('5' - 2);   // 3
console.log('a' * 2);   // NaN

Por isso '10' + 5 - 2 dá 13: o + junta em '105', e o - converte de

volta para número. Esse detalhe é a origem do bug clássico de contador que

incrementa errado, e a saída é converter explicitamente com Number() ou

parseInt() antes de somar.

O exemplo separa os três casos em três linhas com o motivo escrito ao lado de

cada resultado: '5' + 2: 52 (o + concatena), `'5' - 2: 3 (o - forca a

conversao) e 'a' * 2: NaN (letra nao vira numero). O mesmo '5'` é número

na segunda linha e texto na primeira, e a diferença do resultado veio do

operador, não do valor.

Truthy, falsy e o operador duplo

Quando o JavaScript precisa de um valor verdadeiro, ele testa o valor como se

fosse uma condição. Só sete valores são falsy — todos os outros são

truthy:

false, 0, '' (string vazia), null, undefined, NaN e document.all.

Os casos que mais enganam estão no meio dessa lista: a string '0' é truthy

(não é a string vazia), a string ' ' com um espaço é truthy, e o array []

vazio é truthy. A razão é que o JavaScript converte o array em objeto, e objeto

é truthy. O operador ! nega, e !! nega duas vezes, servindo para

transformar o teste em true ou false de verdade.

console.log(!!'', !!'0', !![], !!null);   // false true true false

O exemplo testa a lista inteira de uma vez, com o resultado impresso como

objeto: `!! valor: { zero: false, stringVazia: false, textoZero: true, espaco:

true, arrayVazio: true, nulo: false, indefinido: false }`. As quatro linhas que

mais confundem estão todas nesse objeto — textoZero, espaco e arrayVazio

saem true, e stringVazia sai false. O !! é o que produz true/false

em vez do valor original, e é por isso que a linha do exemplo pode ser lida como

uma lista de veredito.

Os operadores unários e os de comparação

Um operador unário age em um lado só: -5 nega o sinal, !x nega o valor,

typeof x devolve o tipo e delete remove a propriedade de um objeto.

typeof funciona com nome não declarado, o que faz dele o teste seguro:

console.log(typeof naoDeclarada);   // 'undefined'

As três linhas do exemplo cobrem os quatro operadores unários e mostram por que

o typeof é o teste seguro. A primeira imprime `negativo: -5 | nao: false |

typeof: string — o ! em true dá false, e o typeof do texto 'a'` dá

'string'. A segunda é typeof de quem nao existe: undefined, e nenhuma

declaração de naoDeclarada existe no arquivo: o typeof é a única consulta

que não quebra com nome ausente. A terceira usa o delete e mostra o efeito:

indice de "Ana": A | delete em objeto: e, na linha seguinte,

depois do delete: {} — o objeto existia com nome e ficou sem nenhum campo.

Os de comparação são dois: os de igualdade (==, !=, ===, !==) e os de

ordem (<, >, <=, >=). Para texto, ordem é ordem de letra — `'Ana' <

'Bruno' é true. Para número com texto, use Number()` antes de comparar, ou

a comparação vai comparar string.

Os quatro operadores de igualdade cabem em uma linha, e o exemplo confirma:

'1' != 0: true | '1' !== 0: true. O != nega o ==, e ambos negam o ===,

então os dois são true para o mesmo par. Os quatro de ordem vêm na linha

seguinte, com os quatro casos: `2 > 1: true | 2 >= 2: true | 1 < 2: true |

1 <= 1: true`.

Exemplo

// Operadores, comparacao e a diferenca entre == e ===.
const a = '0';
const b = 0;
const c = false;
const nulo = null;
const indefinido = undefined;

console.log('--- aritmetica ---');
console.log('soma:', 2 + 3, '| subtracao:', 7 - 2, '| multiplicacao:', 4 * 3);
console.log('divisao:', 7 / 2, '| resto:', 7 % 3, '| resto negativo:', -7 % 3);
console.log('potencia:', 2 ** 3, '| 2 ** 0.5:', 2 ** 0.5);

console.log('--- precedencia ---');
console.log('2 + 3 * 4:', 2 + 3 * 4, '| (2 + 3) * 4:', (2 + 3) * 4);

console.log('--- comparacao frouxa: converte antes de comparar ---');
console.log("'0' == 0:", a == b, "| 0 == false:", 0 == c, "| '' == 0:", '' == 0);
console.log('null == undefined:', nulo == indefinido, "| [] == false:", [] == c);

console.log('--- comparacao estrita: compara tipo e valor ---');
console.log("'0' === 0:", a === b, "| 0 === false:", 0 === c);
console.log('null === undefined:', nulo === indefinido, '| null == undefined:', nulo == indefinido);

console.log('--- os tres iguais da comparacao ---');
console.log("'1' != 0:", '1' != 0, "| '1' !== 0:", '1' !== 0);
console.log('2 > 1:', 2 > 1, '| 2 >= 2:', 2 >= 2, '| 1 < 2:', 1 < 2, '| 1 <= 1:', 1 <= 1);

console.log('--- conversao implicita na conta ---');
console.log("'5' + 2:", '5' + 2, "(o + concatena)");
console.log("'5' - 2:", '5' - 2, "(o - forca a conversao)");
console.log("'a' * 2:", 'a' * 2, "(letra nao vira numero)");

console.log('--- truthy e falsy ---');
console.log('!! valor:', {
  zero: !!0,
  stringVazia: !!'',
  textoZero: !!'0',
  espaco: !!' ',
  arrayVazio: !![],
  nulo: !!null,
  indefinido: !!undefined,
});

console.log('--- operadores unarios ---');
console.log('negativo:', -5, '| nao:', !true, '| typeof:', typeof 'a');
console.log('typeof de quem nao existe:', typeof naoDeclarada);
console.log('indice de "Ana":', 'Ana'[0], '| delete em objeto:');
const usuario = { nome: 'Ana' };
delete usuario.nome;
console.log('  depois do delete:', usuario);

Saída real

--- aritmetica ---
soma: 5 | subtracao: 5 | multiplicacao: 12
divisao: 3.5 | resto: 1 | resto negativo: -1
potencia: 8 | 2 ** 0.5: 1.4142135623730951
--- precedencia ---
2 + 3 * 4: 14 | (2 + 3) * 4: 20
--- comparacao frouxa: converte antes de comparar ---
'0' == 0: true | 0 == false: true | '' == 0: true
null == undefined: true | [] == false: true
--- comparacao estrita: compara tipo e valor ---
'0' === 0: false | 0 === false: false
null === undefined: false | null == undefined: true
--- os tres iguais da comparacao ---
'1' != 0: true | '1' !== 0: true
2 > 1: true | 2 >= 2: true | 1 < 2: true | 1 <= 1: true
--- conversao implicita na conta ---
'5' + 2: 52 (o + concatena)
'5' - 2: 3 (o - forca a conversao)
'a' * 2: NaN (letra nao vira numero)
--- truthy e falsy ---
!! valor: {
  zero: false,
  stringVazia: false,
  textoZero: true,
  espaco: true,
  arrayVazio: true,
  nulo: false,
  indefinido: false
}
--- operadores unarios ---
negativo: -5 | nao: false | typeof: string
typeof de quem nao existe: undefined
indice de "Ana": A | delete em objeto:
  depois do delete: {}