Explicando a coerção de tipos em Javascript pt. 1
Esse texto é uma tradução livre do artigo JavaScript type coercion explained de Alexey Samoshkin.
Coerção de tipos (type coercion) é o processo de conversão de um valor de um tipo, para outro (como a conversão de uma string para um número, um objeto para um boolean e etc). Qualquer tipo, seja primitivo ou um objeto, é um sujeito válido para coerção de tipo. Para recordar, os primitivos são: number, string, boolean, null, undefined + Symbol (adicionado no ES6).
Como um exemplo de coerção de tipo em prática, veja o link JavaScript Comparison Table, que mostra como o operador de igualdade ==
se comporta para diferentes tipos. Essa matriz parece assustadora devido à coerção de tipo implícita que o operador ==
faz, e dificilmente será possível lembrar de todas essas combinações. E você não precisa fazer isso — apenas aprenda os princípios básicos da coerção de tipos.
Esse artigo vai a fundo de como a coerção de tipos funciona no Javascript, e irá prepará-lo com o conhecimento básico para que você possa sentir-se confiante ao explicar sobre as expressões. Ao final do artigo, mostrarei as respostas e as explicarei.
true + false
12 / "6"
"number" + 15 + 3
15 + 3 + "number"
[1] > null
"foo" + + "bar"
'true' == true
false == 'false'
null == ''
!!"false" == !!"true"
[‘x’] == ‘x’
[] + null + 1
[1,2,3] == [1,2,3]
{}+[]+{}+[1]
!+[]+[]+![]
new Date(0) - 0
new Date(0) + 0
Sim, essa é uma lista boba de coisas que você pode fazer como desenvolvedor. Em 90% dos casos é melhor evitar a coerção de tipos implícita. Considere essa lista como exercícios para aprendizagem para testar seu conhecimento de como a coerção de tipos funciona. Se estiver entendiado, você pode encontrar mais em wtfjs.com.
A propósito, você pode encontrar perguntas disso em entrevistas para vagas de Javascript. Então continue lendo 😄.
Coerção Implícita vs Explícita
Coerção de tipos pode ser explícita ou implícita.
Quando um desenvolvedor deseja converter um tipo escrevendo algo como, Number(valor)
, isso é chamado de coerção de tipos explícita (explicit type coercion ou type casting.
Já que o Javascript é uma linguagem fracamente tipada (weakly-typed language), valores também podem ser convertidos entre diferentes tipos automaticamente, e isso é chamado de coerção de tipos implícita (implicit type coercion). Isso acontece quando você atribui operados para valores de diferentes tipos, como 1 == null
, 2/’5'
, null + new Date()
, ou isso pode decorrer do contexto, como usar if (value) {…}
, onde value
é forçado a retornar um booleano.
Um operador que não desencadeia a coerção de tipos implítica é ===,
que é chamado de operador restrito de igualdade (strict equality operator). O operador de igualdade ==
por outro lado, faz a comparação e ativa a coerção de tipos, se necessário.
Coerção de tipo implícito é uma faca de dois gumes: é uma grande fonte de frustração e defeitos, mas também um mecanismo útil que nos permite escrever menos código sem perder a legibilidade.
Três tipos de conversão
A primeira regra que precisamos saber é que existem apenas 3 tipos de conversão no Javascript:
- para string;
- para boolean;
- para number.
A segunda, é que a lógica para conversão de tipos primitivos e objetos funcionam de forma diferente, mas ambos só podem ser convertido nessas 3 maneiras.
Vamos começar primeiro com os primitivos.
Conversão de String
Para indicar a conversão explícita de valores para string use a função String()
. A coerção implícita é ativada pelo operador binário +
, quando qualquer operando é uma string:
String(123) // explícito
123 + '' // implícito
Todos os valores primitivos são convertidos em strings naturalmente, como você poderia esperar:
String(123) // '123'
String(-12.3) // '-12.3'
String(null) // 'null'
String(undefined) // 'undefined'
String(true) // 'true'
String(false) // 'false'
A conversão de Symbol é um pouco complicada, porque só pode ser convertida explicitamente, mas não implicitamente. Leia mais nas regras de coerção de tipos do Symbol.
String(Symbol('my symbol')) // 'Symbol(my symbol)'
'' + Symbol('my symbol') // TypeError é lançado
Conversão de Boolean
Para indicar a conversão explícita de valores para boolean use a função Boolean()
. A conversão implícita ocorre no contexto lógico ou é ativada por operadores lógicos (|| && !)
.
Boolean(2) // explícito
if (2) { ... } // implícito devido ao contexto lógico
!!2 // implícito devido ao operador lógico
2 || 'hello' // implícito devido ao operador lógico
Observação: Operadores lógicos como ||
e &&
fazem conversões booleanas internamente, mas na verdade retornam o valor dos operadores originais, mesmo que eles não sejam booleanos.
// retorna o número 123 ao invés de true
// 'hello' e 123 são convertidos para boolean internamente para calcular a expressão
let x = 'hello' && 123; //x === 123 é true
Assim que houver apenas dois resultados possíveis da conversão booleana: true
ou false
, é mais fácil lembrar a lista de valores falsos (falsy values).
Boolean('') // false
Boolean(0) // false
Boolean(-0) // false
Boolean(NaN) // false
Boolean(null) // false
Boolean(undefined) // false
Boolean(false) // false
Qualquer valor não inserido nessa lista ao ser convertido será true, incluindo objetos, funções, Array
, Date
, tipos definidos pelo usuário e assim por diante. Symbols são considerados como valores verdadeiros (truthy values). Objetos vazios e arrays também:
Boolean({}) // true
Boolean([]) // true
Boolean(Symbol()) // true
!!Symbol() // true
Boolean(function() {}) // true
Conversão Numérica
Para uma conversão explícita aplique a função Number()
, assim como feito com Boolean()
e String()
.
A conversão implícita é complicada, pois é acionada em mais casos:
- operadores de comparação (comparison operators)(
>
,<
,<=
,>=
) - operadores bitwise (
|
&
^
~
) - operadores aritméticos (
-
+
*
/
%
). Saiba que usar+
não irá ativar a conversão numérica quando qualquer operando for uma string - operador unário
+
- operador de igualdade
==
(incl.!=
) - perceba que
==
não ativa a conversão numérica quando ambos operandos são strings.
Number('123') // explícito
+'123' // implícito
123 != '456' // implícito
4 > '5' // implícito
5/null // implícito
true | 0 // implícito
Abaixo como valores primitivos são convertido para números:
Number(null) // 0
Number(undefined) // NaN
Number(true) // 1
Number(false) // 0
Number(" 12 ") // 12
Number("-12.34") // -12.34
Number("\n") // 0
Number(" 12s ") // NaN
Number(123) // 123
Ao converter uma string em número, a engine primeiro remove os espaços em branco com os caracteres \n
e \t
, retornando NaN
se a string tratada não representar um número válido. Se a string estiver vazia, retornará 0
.
null
e undefined
são tratados de forma diferentes: null
vira 0
, enquanto undefined
se torna NaN
.
Symbols não podem ser convertidos em números nem explicitamente nem implicitamente. Além disse, TypeError
é lançado ao invés de silenciosamente converter para NaN
, como acontece para undefined
. Veja mais sobre as regras de conversão de símbolo no MDN.
Number(Symbol('my symbol')) // TypeError é lançado
+Symbol('123') // TypeError é lançado
Existem duas regras especiais para relembrar:
- Quando aplicamos
==
paranull
ouundefined
, a conversão numérico não ocorre.null
é apenas igual anull
ouundefined
, e não é igual a mais nada.
null == 0 // false, null is not converted to 0
null == null // true
undefined == undefined // true
null == undefined // true
NaN
não é igual a nada que não seja ele mesmo:
if (value !== value) {
console.log("we're dealing with NaN here")
}