Dia 3 — Tipos, operadores e comparacao

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

Tipos, operadores e comparacao

Aula 1

Os tipos de dado e typeof

Os tipos de dado e typeof

São poucos tipos, e a linguagem não deixa declarar o tipo da variável. A tipagem é dinâmica: quem tem tipo é o valor, não o nome que o aponta.

O que existe para perguntar o tipo é o operador typeof, que devolve o nome do tipo em texto, em inglês e em minúsculas.

typeof 42;        // 'number'
typeof 'texto';   // 'string'
typeof true;      // 'boolean'
typeof x;         // 'undefined', se x não existe

typeof é o único operador que não quebra em nome inexistente, e por isso é a forma de o código descobrir o que já foi criado.

Os tipos e o que cada typeof devolve

Valortypeof devolveNota
42, 4.5'number'inteiro e decimal são o mesmo tipo
'texto''string'
true'boolean'só true e false
undefined'undefined'nome declarado sem valor, ou não declarado
null'object'o caso que engana
[1, 2, 3]'object'array não tem tipo próprio no typeof
{ nome: 'Ana' }'object'
10n'bigint'inteiro além do limite seguro
Symbol('id')'symbol'identidade única
() => {}'function'função tem tipo próprio

O tipo de dado é a classificação que o typeof devolve, escrita em texto: 'number', 'string', 'object'. Ele responde pelo valor, não pelo nome — typeof idade e typeof 30 dão a mesma resposta, e é por isso que a mesma variável pode ter tipos diferentes em linhas diferentes.

null devolve 'object' desde 1995, quando typeof foi escrito. O tipo se chamava null e a cola de bits que representa o valor vazio era a mesma usada por objeto. Corrigir quebraria código publicado na web, então a linguagem manteve. Nunca decida o tipo de null com typeof.

Array e objeto, a mesma resposta

typeof array não tem resposta própria: typeof [1, 2, 3] devolve 'object', o mesmo que um objeto comum.

Como o typeof não distingue array de objeto, a forma de separar é Array.isArray.

FunçãoAssinaturaExemplo
Array.isArrayArray.isArray(valor: unknown): booleanArray.isArray(lista) devolve true
Object.prototype.toStringObject.prototype.toString.call(valor): stringdevolve '[object Array]'

Os números que não se comportam

NaN e Infinity não se escrevem: nascem de contas. NaN significa "not a number", e é o resultado de uma conta inválida.

0 / 0;              // NaN
'abc' * 2;          // NaN
1 / 0;              // Infinity
-1 / 0;             // -Infinity
NaN === NaN;        // false: nem consigo mesmo ele é igual
FunçãoAssinaturaPara que serve
Number.isNaNNumber.isNaN(valor: unknown): booleandizer se o valor é mesmo NaN
Number.isFiniteNumber.isFinite(valor: unknown): booleandizer se é número finito
Number.isIntegerNumber.isInteger(valor: unknown): booleandizer se é inteiro

O NaN é o único valor que não é igual a si mesmo, e é por isso que NaN === NaN dá false. Comparar NaN com == também dá false; só Number.isNaN responde certo.

Ponto flutuante, e pedir o valor em texto

0.1 + 0.2 não é exatamente 0.3. A representação binária de 0.1 é infinita, e o truncamento deixa um resto. Comparar dinheiro assim dá bug.

0.1 + 0.2 === 0.3;                          // false
Math.abs((0.1 + 0.2) - 0.3) < Number.EPSILON;   // true

Acima de Number.MAX_SAFE_INTEGER (9.007.199.254.740.991), dois inteiros diferentes podem virar o mesmo valor, porque não há bits para diferenciar. Para dinheiro, guarde centavos em inteiro: 1999 em vez de 19.99.

Do número para o texto existem duas formas, e a diferença é o que cada uma aceita.

FormaAssinaturaNota
StringString(valor?: any): stringfunciona com qualquer valor, até null
toStringtoString(): stringprecisa de um valor que tenha o método
toStringtoString(radix: number): stringcom argumento, muda a base

(255).toString(16) devolve 'ff', (42).toString(2) devolve '101010'. É a forma de ler número em outra base, e não existe equivalente em PHP.

String(42) funciona em null e undefined; (null).toString() quebra com TypeError. Quando o valor pode ser qualquer um, o caminho é String().

Exemplo

'use strict';
// ============================================================
// AULA 1 - Os tipos de dado e typeof
//
// São poucos tipos, e a linguagem não deixa declarar o tipo da
// variável. O que existe é `typeof`: um operador que devolve o
// nome do tipo em texto. Este exemplo lista os tipos, mostra o
// caso que engana (`typeof null`) e os números que não se
// comportam como número.
// ============================================================

// Os tipos primitivos, um por linha. O nome que `typeof` devolve
// já vem pronto, em inglês e em minúsculas.
console.log('--- typeof dos valores básicos ---');
console.log('42            ->', typeof 42);
console.log("'texto'       ->", typeof 'texto');
console.log('true          ->', typeof true);
console.log('10n           ->', typeof 10n, '(número inteiro enorme)');
console.log("Symbol('id')  ->", typeof Symbol('id'));
console.log('função        ->', typeof (function () {}));

// `semValor` nunca foi declarada e `vazio` foi declarada sem valor.
// Nos dois casos o valor é undefined, e o `typeof` não quebra:
// perguntar o tipo de um nome inexistente é seguro.
const vazio = null;
console.log('sem valor     ->', typeof semValor, '(nome nunca declarado)');
console.log('nulo          ->', typeof vazio, '(const vazio = null)');

// Array e objeto são os dois `object`. O `typeof` não distingue:
// devolve a mesma palavra para os dois.
const lista = [1, 2, 3];
const ficha = { nome: 'Ana' };
console.log('');
console.log('--- dois tipos, a mesma resposta ---');
console.log('typeof lista:', typeof lista);
console.log('typeof ficha:', typeof ficha);
console.log('Array.isArray(lista):', Array.isArray(lista));
console.log('Array.isArray(ficha):', Array.isArray(ficha));
console.log('Object.prototype.toString.call(lista):',
            Object.prototype.toString.call(lista));

// O caso que faz perder nota: `typeof null` devolve 'object'.
// `null` não é um objeto, é o valor que significa "vazio de
// propósito". O tipo se chama `null` e devolve `object`; é assim
// que o `typeof` foi escrito, e a linguagem mantém o
// comportamento para não quebrar código antigo.
console.log('');
console.log('--- o caso que engana ---');
console.log('typeof null:', typeof null);
console.log('typeof undefined:', typeof undefined);
console.log('null e undefined são o mesmo valor?', null === undefined);
console.log('são do mesmo tipo?', typeof null === typeof undefined);

// NaN e Infinity não se escrevem: nascem de contas. NaN significa
// "not a number", e é o resultado de uma conta inválida.
console.log('');
console.log('--- números que não se comportam ---');
console.log('0 / 0 =', 0 / 0, '(NaN)');
console.log("'abc' * 2 =", 'abc' * 2, '(NaN)');
console.log('1 / 0 =', 1 / 0, '(Infinity)');
console.log('-1 / 0 =', -1 / 0, '(-Infinity)');
console.log('NaN === NaN:', NaN === NaN, '(nem consigo mesmo ele é igual)');
console.log('Number.isNaN(NaN):', Number.isNaN(NaN));
console.log('Number.isFinite(1 / 0):', Number.isFinite(1 / 0));
console.log('Number.isInteger(4.0):', Number.isInteger(4.0));
console.log('Number.isInteger(4.5):', Number.isInteger(4.5));

// Ponto flutuante tem limite de precisão: 0.1 + 0.2 não é
// exatamente 0.3. Acima de Number.MAX_SAFE_INTEGER, dois números
// inteiros diferentes podem virar o mesmo valor.
console.log('');
console.log('--- o limite do ponto flutuante ---');
console.log('0.1 + 0.2 =', 0.1 + 0.2);
console.log('0.1 + 0.2 === 0.3:', 0.1 + 0.2 === 0.3);
console.log('comparando com tolerância:', Math.abs((0.1 + 0.2) - 0.3) < Number.EPSILON);
console.log('Number.MAX_SAFE_INTEGER:', Number.MAX_SAFE_INTEGER);

// String() é a forma de transformar qualquer valor em texto. Vale
// para todos, inclusive null e undefined.
console.log('');
console.log('--- do número para o texto ---');
console.log('String(42):', String(42), typeof String(42));
console.log('String(4.5):', String(4.5));
console.log('String(true):', String(true));
console.log('(42).toString():', (42).toString());
console.log('(42).toString(2):', (42).toString(2), '(42 em binário)');
console.log('(255).toString(16):', (255).toString(16), '(255 em hexadecimal)');
console.log('(255).toString(2).length:', (255).toString(2).length, '(bits do número)');

Saída real

--- typeof dos valores básicos ---
42            -> number
'texto'       -> string
true          -> boolean
10n           -> bigint (número inteiro enorme)
Symbol('id')  -> symbol
função        -> function
sem valor     -> undefined (nome nunca declarado)
nulo          -> object (const vazio = null)

--- dois tipos, a mesma resposta ---
typeof lista: object
typeof ficha: object
Array.isArray(lista): true
Array.isArray(ficha): false
Object.prototype.toString.call(lista): [object Array]

--- o caso que engana ---
typeof null: object
typeof undefined: undefined
null e undefined são o mesmo valor? false
são do mesmo tipo? false

--- números que não se comportam ---
0 / 0 = NaN (NaN)
'abc' * 2 = NaN (NaN)
1 / 0 = Infinity (Infinity)
-1 / 0 = -Infinity (-Infinity)
NaN === NaN: false (nem consigo mesmo ele é igual)
Number.isNaN(NaN): true
Number.isFinite(1 / 0): false
Number.isInteger(4.0): true
Number.isInteger(4.5): false

--- o limite do ponto flutuante ---
0.1 + 0.2 = 0.30000000000000004
0.1 + 0.2 === 0.3: false
comparando com tolerância: true
Number.MAX_SAFE_INTEGER: 9007199254740991

--- do número para o texto ---
String(42): 42 string
String(4.5): 4.5
String(true): true
(42).toString(): 42
(42).toString(2): 101010 (42 em binário)
(255).toString(16): ff (255 em hexadecimal)
(255).toString(2).length: 8 (bits do número)
Aula 2

Operadores, comparacao e o caso de == contra ===

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

Operador é o símbolo que opera sobre valores. A aritmética é conhecida; a parte que perde nota é a comparação, porque existem dois operadores de igualdade e eles não fazem a mesma coisa.

A regra da aula cabe numa frase: use === e nunca ==.

Aritmética e precedência

OperadorAssinaturaExemploNota
+a + b10 + 3 → 13soma ou junta texto
-a - b10 - 3 → 7
*a * b10 * 3 → 30
/a / b10 / 3 → 3.3333…divisão real, sempre com casas
%a % b10 % 3 → 1resto da divisão
**a ** b2 ** 10 → 1024potência

O % devolve o resto, e é assim que se descobre se o número é par (n % 2 === 0) ou se a divisão é exata. O resto leva o sinal do dividendo: -7 % 3 dá -1, não 2.

O % é o operador de módulo da divisão — em inglês módulo, que é o nome que a documentação usa. Em português o resultado é o resto, e é por isso que as duas palavras aparecem lado a lado.

Divisão nunca é inteira. 10 / 2 dá 5, e 10 / 4 dá 2.5. Para cortar a parte decimal existe Math.floor, assunto da aula 3.

A precedência vem antes: da mais forte para a mais fraca, **, depois * e /, depois + e -. Depois vêm ===, !== e os operadores lógicos.

2 + 3 * 4;      // 14: a multiplicação roda antes
(2 + 3) * 4;    // 20: o parêntese manda
2 ** 3 ** 2;    // 512: a potência é da direita para a esquerda
20 - 5 - 3;     // 12: a subtração é da esquerda para a direita

A potência é a única exceção: ela se junta pela direita, então 2 3 2 vale 2 (3 2). Quando a intenção não for óbvia, os parênteses resolvem — e são eles que mudam o resultado de 2 + 3 * 4 para 20.

O + que muda de sentido

O + é o único operador que aceita dois tipos: com dois números soma, com algum texto junta. E a soma vai da esquerda para a direita, então o tipo muda no meio da expressão.

1 + 1 + '2';        // '22': 1+1 fez 2, e 2+'2' juntou
'1' + 1 + 1;        // '11': o texto puxou o resto
'Soma: ' + 2 + 2;   // 'Soma: 22': o texto engoliu a soma
'Soma: ' + (2 + 2); // 'Soma: 4'

'Resultado: ' + 10 / 2 dá 'Resultado: 5', e não 'Resultado: 102': a divisão tem precedência maior e roda antes. Quando a intenção é concatenar números, use a crase, que não tem esse problema.

== contra ===

=== compara valor e tipo. == compara só o valor, depois de converter um dos lados — a conversão implícita, o *type coercion*. É ela que produz as linhas que o aluno vê e não acredita.

Comparação=====O que aconteceu no ==
'0' == 0truefalsetexto convertido para número
0 == falsetruefalsefalse virou 0
'' == 0truefalsetexto vazio virou 0
[] == falsetruefalsearray vazio virou texto vazio
null == undefinedtruefalseos dois viraram 0

A regra da conversão: quando os tipos são diferentes, o == converte o número para o tipo do outro, não o contrário. Por isso '0' == 0 é true e '1' == '0' é false.

Como comparar do jeito certo é deixar os dois lados no mesmo tipo antes:

Number('0') === 0;         // true
String(0) === '0';         // true
' 10 '.trim() === '10';    // true

Os operadores != e !== são a negação de == e ===, com a mesma diferença de comportamento entre eles.

O <= e o >= fecham a lista: são "menor ou igual" e "maior ou igual", e aceitam os dois tipos sem converter nada. nota >= 7 é true para 7, 8 e 9, e a forma curta de escrever "nota não passou" é nota < 7. O que a linguagem não tem é um "entre": min <= x <= max não funciona, porque o JavaScript avalia comparação por comparação, e o resultado do primeiro <= seria comparado com max. A forma correta é usar dois if aninhados, ou comparar as duas condições com &&.

Comparação de texto, ordem e operadores unários

Texto é comparado por ordem de caractere, não por valor de número, mesmo quando parece número: '10' < '9' é true, porque o 1 vem antes do 9. Com maiúscula e minúscula, 'a' < 'B' é false: as maiúsculas vêm antes.

Operador unário é o que pega un valor: ! nega o booleano, - nega o número, typeof pergunta o tipo, void devolve undefined, e delete remove propriedade de objeto devolvendo true ou false.

const ficha = { nome: 'Ana', idade: 30, cidade: 'Recife' };
delete ficha.idade;          // true: removeu
delete ficha.inexistente;    // false: não havia o que remover

!! é a dupla negação que entrega o booleano: !!'texto' é true e !!0 é false. Converte qualquer valor em true ou false, e é o atalho para a lista de valores falsos da aula 5.

O que essa negação negaria é a lista falsy: os poucos valores que valem false sozinhos — 0, '', null, undefined e NaN. Qualquer outro valor é truthy, inclusive o texto '0' e o array []. A lista completa, com o porquê de cada caso, é a aula 5.

'0' é truthy: texto não vazio, mesmo com o caractere 0 dentro. O que é falso é o texto vazio. '0' == 0 dá true por conversão, não porque os dois valham a mesma coisa — e essa diferença é o que a aula 5 abre.

Exemplo

'use strict';
// ============================================================
// AULA 2 - Operadores, comparação e o caso de == contra ===
//
// Operador é o símbolo que opera sobre valores. A aula inteira
// cabe numa regra: use === e nunca ==. O exemplo mostra a
// aritmética, a precedência e a tabela de comparação frouxa que
// faz o aluno perder nota.
// ============================================================

// Aritmética: os quatro operadores, mais o resto e a potência.
console.log('--- aritmética ---');
console.log('10 + 3 =', 10 + 3);
console.log('10 - 3 =', 10 - 3);
console.log('10 * 3 =', 10 * 3);
console.log('10 / 3 =', 10 / 3, '(divisão real, sempre com casas decimais)');
console.log('10 % 3 =', 10 % 3, '(resto da divisão)');
console.log('-7 % 3 =', -7 % 3, '(o resto leva o sinal do dividendo)');
console.log('2 ** 10 =', 2 ** 10, '(potência)');
console.log('17 / 5 =', 17 / 5, '| 17 % 5 =', 17 % 5, '| o mesmo corte');

// Precedência: ** vem antes de * e /, que vem antes de + e -.
// Os operadores vão da esquerda para a direita, com uma exceção: a
// potência é da direita para a esquerda.
console.log('');
console.log('--- precedência ---');
console.log('2 + 3 * 4 =', 2 + 3 * 4);
console.log('(2 + 3) * 4 =', (2 + 3) * 4);
console.log('2 ** 3 ** 2 =', 2 ** 3 ** 2, '(da direita para a esquerda)');
console.log('20 - 5 - 3 =', 20 - 5 - 3, '(subtração é da esquerda para a direita)');

// O `+` é o único operador que aceita dois tipos: com dois números
// soma, com algum texto junta. É por isso que 1 + 1 + '2' dá '22'.
console.log('');
console.log('--- o + que muda de sentido ---');
console.log('1 + 1 + "2" =', 1 + 1 + '2');
console.log('"1" + 1 + 1 =', '1' + 1 + 1);
console.log('"Resultado: " + 10 / 2 =', 'Resultado: ' + 10 / 2, '(a divisão roda antes do +)');
console.log('String(10 / 2) + "" =', String(10 / 2) + '');

// === compara valor E tipo. == compara só o valor, depois de
// converter um dos lados: é essa conversão implícita, o type
// coercion, que produz as linhas que o aluno vê e não acredita.
console.log('');
console.log('--- == contra === ---');
console.log('"0" == 0    ->', '0' == 0, '(texto convertido para número)');
console.log('"0" === 0   ->', '0' === 0, '(tipos diferentes, não são o mesmo valor)');
console.log('0 == false   ->', 0 == false, '(false convertido para 0)');
console.log('0 === false  ->', 0 === false);
console.log('"" == 0      ->', '' == 0, '(texto vazio convertido para 0)');
console.log('[] == false  ->', [] == false, '(array vazio convertido para texto vazio)');
console.log('null == undefined ->', null == undefined, '(os dois viram 0)');
console.log('null === undefined ->', null === undefined);

// A forma de comparar de verdade é deixar os dois lados no mesmo
// tipo antes: Number(), String() ou .trim() na ponta.
console.log('');
console.log('--- comparar do jeito certo ---');
console.log('Number("0") === 0:', Number('0') === 0);
console.log('String(0) === "0":', String(0) === '0');
console.log('" 10 ".trim() === "10":', ' 10 '.trim() === '10');
console.log('"a" !== "A":', 'a' !== 'A', '(maiúscula e minúscula são textos diferentes)');

// Texto é comparado por ordem de caractere, não por valor de
// número, mesmo quando o texto parece um número.
console.log('');
console.log('--- ordem de comparação ---');
console.log('"10" < "9":', '10' < '9', '(o 1 vem antes do 9 na ordem dos caracteres)');
console.log('10 < 9:', 10 < 9, '(aqui são números)');
console.log('"a" < "b":', 'a' < 'b');
console.log('"a" < "B":', 'a' < 'B', '(maiúscula vem antes de minúscula)');
console.log("'Ana' <= 'Bia':", 'Ana' <= 'Bia');

// Operador unário é o que pega UM valor, em vez de dois.
console.log('');
console.log('--- operadores unários ---');
console.log('!true =', !true, '(! nega o booleano)');
console.log('!0 =', !0, '(0 é um valor falso, então !0 é true)');
console.log('!!"texto" =', !!'texto', '(dupla negação entrega o booleano)');
console.log('!!0 =', !!0);
console.log('-5 =', -5, '(- nega o número)');
console.log('typeof "x" =', typeof 'x', '(typeof pergunta o tipo)');
console.log('void 0 =', void 0, '(void sempre devolve undefined)');

// `delete` remove a propriedade de um objeto e devolve true ou
// false dizendo se conseguiu.
console.log('');
console.log('--- delete ---');
const ficha = { nome: 'Ana', idade: 30, cidade: 'Recife' };
console.log('antes:', ficha);
console.log('delete ficha.idade devolve:', delete ficha.idade);
console.log('depois:', ficha);
console.log('delete ficha.inexistente devolve:', delete ficha.inexistente,
            '(false: não havia o que remover)');

Saída real

--- aritmética ---
10 + 3 = 13
10 - 3 = 7
10 * 3 = 30
10 / 3 = 3.3333333333333335 (divisão real, sempre com casas decimais)
10 % 3 = 1 (resto da divisão)
-7 % 3 = -1 (o resto leva o sinal do dividendo)
2 ** 10 = 1024 (potência)
17 / 5 = 3.4 | 17 % 5 = 2 | o mesmo corte

--- precedência ---
2 + 3 * 4 = 14
(2 + 3) * 4 = 20
2 ** 3 ** 2 = 512 (da direita para a esquerda)
20 - 5 - 3 = 12 (subtração é da esquerda para a direita)

--- o + que muda de sentido ---
1 + 1 + "2" = 22
"1" + 1 + 1 = 111
"Resultado: " + 10 / 2 = Resultado: 5 (a divisão roda antes do +)
String(10 / 2) + "" = 5

--- == contra === ---
"0" == 0    -> true (texto convertido para número)
"0" === 0   -> false (tipos diferentes, não são o mesmo valor)
0 == false   -> true (false convertido para 0)
0 === false  -> false
"" == 0      -> true (texto vazio convertido para 0)
[] == false  -> true (array vazio convertido para texto vazio)
null == undefined -> true (os dois viram 0)
null === undefined -> false

--- comparar do jeito certo ---
Number("0") === 0: true
String(0) === "0": true
" 10 ".trim() === "10": true
"a" !== "A": true (maiúscula e minúscula são textos diferentes)

--- ordem de comparação ---
"10" < "9": true (o 1 vem antes do 9 na ordem dos caracteres)
10 < 9: false (aqui são números)
"a" < "b": true
"a" < "B": false (maiúscula vem antes de minúscula)
'Ana' <= 'Bia': true

--- operadores unários ---
!true = false (! nega o booleano)
!0 = true (0 é um valor falso, então !0 é true)
!!"texto" = true (dupla negação entrega o booleano)
!!0 = false
-5 = -5 (- nega o número)
typeof "x" = string (typeof pergunta o tipo)
void 0 = undefined (void sempre devolve undefined)

--- delete ---
antes: { nome: 'Ana', idade: 30, cidade: 'Recife' }
delete ficha.idade devolve: true
depois: { nome: 'Ana', cidade: 'Recife' }
delete ficha.inexistente devolve: true (false: não havia o que remover)