Tipagem dinâmica com Javascript

Tipagem dinâmica com Javascript
juliana-negreiros
juliana-negreiros

Compartilhe

Ouvimos muito por aí que o JavaScript não possui tipagem, mas não é bem assim.

Ele é dinamicamente tipado, o que significa que nós não precisamos declarar seu tipo. Mas isso só é possível porque ele mesmo identifica o tipo da variável que estamos declarando e o atribui, mesmo que não explicitamente. Por exemplo:

const cliente = "Juliana"

Nós não precisamos dizer para o JavaScript que cliente é uma String, ele já sabe que é!

E aí você pensa: “Poxa, como ele é bonzinho! Economiza o nosso tempo, né?”. Sim, eu também acho. Mas é muito normal nos depararmos com uns erros e bugs bizarros graças a essa tipagem dinâmica, quando não entendemos muito bem como ela funciona.

Imagine a seguinte situação: trabalhamos em um sistema importantíssimo de finanças pessoais. Nele, a pessoa digita quanto ganha e quanto gasta e nós vamos adicionando e subtraindo esses valores de seu saldo.

Após uma atualização no nosso sistema, recebemos uma ligação desesperada do comercial da empresa: tem uns valores muito loucos aparecendo para os clientes e as contas não batem.

Nessa atualização permitimos que os números sejam retirados diretamente da conta corrente do cliente. O que não havíamos percebido é que esses valores vinham como String e não estávamos tratando isso. Sendo assim, um valor de R$20,00 que entrou em uma conta com mais R$20,00 fez com que essa conta totalizasse R$2020,00!

E é exatamente para entender como tratar e prevenir esse tipo de problema que esse artigo foi feito.

Conhecendo os tipos existentes no JavaScript

Os tipos mais conhecidos hoje no JavaScript são:

string, boolean, string, function, undefined e object.

Acho que a primeira coisa que vem à mente é se realmente só existem esses tipos. Cadê o null e o array? Bem, eles não existem!

Vamos pegar algumas variáveis do nosso sistema de finanças para analisarmos um pouco os tipos das variáveis que existem.

const cliente = ‘Joaquina’;
const premium = false;
let saldo = 20.00;
const atualizaSaldo = (saldo, novoValor) => {
    saldo += novoValor;
}
const transacao = {
    descricao: ‘Almoço Juliano ’,
    tipo: ‘crédito’,
    valor: ‘20.00’,
data: `08/05/2019`
}
const contasBancarias = [
{agência: 1234, conta: 16452-2}, 
{agência: 1234, conta: 16458-5}
];

Para vermos o tipo dessas variáveis vamos usar o operador unário typeof.

typeof cliente //string
// o js reconhece string pelo uso de aspas, simples ou duplas, ou crase

typeof premium //boolean

typeof saldoConta //number

typeof atualizaSaldo //function

A variável atualizaSaldo é uma função e isso é definido pela sintaxe que, no caso, é arrow function.

typeof transacao //object

A variável transacao é um objeto e isso é definido pelas chaves { }.

No nosso sistema, também temos a funcionalidade de pagamento agendado. Você consegue salvar a data de um pagamento para ser debitado do seu saldo.

Por conta dessa funcionalidade, também precisamos avisar o(a) usuário(a), caso esta pessoa esteja tentando realizar uma transação que já foi feita. Para isso, temos essa função:

const verificaTransacao = (transacao, novaTransacao) => {
    return transacao === novaTransacao;
}

Legal! Agora vamos chamar a nossa função:

const novaTransacao = {
    descricao: ‘Almoço Juliano ’,
    tipo: ‘crédito’,
    valor: ‘20.00’,
data: `08/05/2019`
}

verificaTransacao(transacao, novaTransacao) //false

Acabamos de achar um problema no nosso sistema! Ele nunca vai avisar aos nossos usuários que as transações são iguais dessa forma. Mesmo os nossos objetos tendo exatamente os mesmos valores, eles não são iguais. E o que isso significa?

Um pouco sobre objetos

No JavaScript, o objeto é um valor de referência, ou seja, aponta para algum lugar específico na memória e, em comparações, é utilizada essa referência.

Com isso em mente, agora sabemos que ao fazer (transacao === novaTransacao) o JavaScript vai verificar se essas duas referências apontam para o mesmo local na memória e não vai verificar os valores internos dos nossos objetos.

Então, cuidado com comparações com objetos e tente sempre comparar os valores existentes no objeto.

typeof contasBancarias //object

A variável contasBancarias é um objeto e isso é definido pelo… pera, essa variável deveria ser um Array!

Pois é, aqui é onde começa a ficar um pouco estranho. Arrays são do tipo object para o JavaScript, o que significa que não podemos saber se uma variável é um array ou não apenas com o typeof. Para sabermos se uma variável é um array, precisamos fazer da seguinte forma:

Array.isArray(contasBancarias) //true
Array.isArray(transacao) //false

Agora sim! Mas temos que lembrar que, por ser do tipo objeto, um array também é um valor de referência e um array nunca vai ser igual ao outro (a menos que eles apontem para o mesmo lugar). Então, assim como no objeto, é bom sempre compararmos os valores do array e não o array em si.

Resultados numéricos inesperados

Agora vamos tentar resolver o nosso problema inicial e ajustar a nossa função atualizaSaldo() para que ela só nos retorne o valor se for um número, usando o operador typeof:

const atualizaSaldo = (saldo, novoValor) => {
    const novoSaldo = saldo + novoValor;
    if(typeof novoSaldo == number) {
    saldo = novoSaldo;
} else {
    return ‘Não foi possível calcular o resultado’;
}
}

Pronto! Com essas alterações estamos garantindo que nossa aplicação não apresente erros inesperados, certo? Vamos testar:

atualizaSaldo (20.00, 20) //20
atualizaSaldo (‘vinte’, 20) //NaN

No segundo exemplo ele me retornou o valor NaN, que significa Not a Number. Mas, se não é um número, não deveria ter retornado o texto que eu pedi?

Nesse caso não, porque o tipo de NaN também é number, por mais confuso que isso possa parecer. Isso acontece porque ele é o resultado de uma operação numérica e, na matemática, podemos associar com o ∄ (não existe).

E ainda temos mais uma complicação com esse valor:

NaN == NaN //false

Um NaN nunca vai ser igual ao outro, visto que ele pode ser qualquer coisa que não seja um número. Se precisarmos saber se esse valor é NaN, podemos usar a seguinte função:

const resultado = atualizaSaldo(‘vinte, 20);
isNaN(resultado) //true

Usando o booleano false ao seu favor

Sabemos que undefined é um valor não definido e tem seu próprio tipo. Mas e o null? Vamos testar:

typeof null //object

Ele é do tipo objeto (????), mais um tipo que precisamos tomar cuidado ao usar.

É importante também saber quais valores são considerados falsos pelo JavaScript e usarmos isso ao nosso favor. Os valores considerados falsos são: 0, ‘’, undefined, NaN e null. Isso significa que, se na nossa regra de negócio formos considerar o 0 e string vazia como valores falsos, podemos fazer da seguinte forma:

const atualizaSaldo= (saldo, novoValor) => {
    const novoSaldo = saldo + novoValor;
    f(novoSaldo) {
    saldo = novoSaldo;
} else {
    return ‘Não foi possível calcular o resultado’;
}
}

Pronto, dessa forma garantimos que o saldo só seja atualizado se o valor realmente for um número. Como o if é uma operação condicional, ele sempre vai converter as expressões para valores booleanos, ou seja, vai verificar se a variável novoSaldo representa um valor verdadeiro ou falso.

Entendendo a coerção automática de tipos

Isso só acontece porque também temos a coerção automática de dados no JavaScript. O que isso significa? Que sempre que fizermos operações com valores de tipos diferentes, ele vai converter um dos dois para poder realizar a operação. Por exemplo:

1 + ‘1//11
1 - ‘1//0
1 == ‘1//true

Dicas finais para escapar desses problemas

A primeira dica (provavelmente a mais falada também) é sempre que possível usar === ao invés de ==. Isso é importante porque o === retorna verdadeiro apenas se o valor E o tipo forem iguais, cancelando a coerção automática do JS.

1 == ‘1//true
1 === ‘1//false

Também é melhor sempre converter os valores antes de fazermos as operações, para evitarmos valores indesejados.

Por exemplo, nós sempre recebemos o valor de cursos finalizados como string, mas precisamos calcular esses valores como números. Podemos fazer:

const atualizaSaldo= (saldo, novoValor) => {
    const novoSaldo = saldo + Number(novoValor);
    f(novoSaldo) {
    saldo = novoSaldo;
} else {
    return ‘Não foi possível calcular o resultado’;
}
}

A próxima dica é para, sempre que tiver dúvida, [consultar a precedência dos operadores])https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Operators/Operator_Precedence_ (ordem em que eles são processados).

E por fim, sempre lembrar de não confiar no typeof de objetos, não usar typeof para arrays e usar seus valores para comparações, não sua referência. Se quiserem saber mais sobre, gosto bastante do artigo do @Diego Pinho, que fala mais sobre esse assunto.

Veja outros artigos sobre Front-end