Executando verificação de segurança...
1

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:

  1. Quando aplicamos == para null ou undefined, a conversão numérico não ocorre. null é apenas igual a null ou undefined, 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
  1. NaN não é igual a nada que não seja ele mesmo:
if (value !== value) {
  console.log("we're dealing with NaN here")
}
Carregando publicação patrocinada...