18
Valores null e undefined no JavaScript
Lidar com dados pode vir a ser caótico. Muitas vezes existe um grande dinamismo na forma como vamos recebê-los e valores nulos e não definidos podem ser comuns em alguns projetos. Veja como lidar com eles.
Primeiro, vamos ver os erros que podem ser causados.
let bubble = gum;
O código acima irá retornar o erro Uncaught ReferenceError: gum is not defined
(gum
não foi definida) pois estamos tentando criar a variável bubble
com o valor da variável gum
, que nunca tinha sido criada antes.
let bubble = gum;
TSError: ⨯ Unable to compile TypeScript:
index.ts:1:14 - error TS2304: Cannot find name 'gum'.
1 let bubble = gum;
~~~
No TypeScript recebemos uma mensagem de erro igualmente útil que nos mostra exatamente onde estamos errados e o que fazer para arrumar o erro.
let empresa = {
nome: "Enviadores de Cotonetes",
dono: null
}
console.log(empresa.dono.nome);
Rodando o código acima teremos outro tipo de erro retornado; Uncaught TypeError: can't access property "algo", empresa.algo is undefined
. Pode ter sido imaginado que nós iríamos ter o mesmo erro, pois estávamos criando outro erro de referência como nos exemplos anteriores mas nós antes recebemos um erro no tipo da variável que estamos tentando acessar. "empresa.dono
não pode ter nada pois é nula" vem antes de "empresa.dono.nome
não existe".
Agora que sabemos exatamente que erro nosso código anterior estava causando, é só uma questão de fazer testes o suficiente para que ele não ocorra, certo?
let empresa = {
nome: "Enviadores de Cotonetes",
dono: null
}
if (empresa != null
&& empresa.dono != null
&& empresa.dono.nome != null) {
console.log(empresa.dono.nome)
} else {
console.warn("Empresa, dono ou nome do dono da empresa não existe.")
}
Depende. Muito. Embora o código acima rode sem retornar erros, ele já está muito verboso antes mesmo de termos uma checagem robusta (não é possível saber se apenas o dono é null
ou apenas o nome do dono é null
). E criar todos os testes iria criar algo mais verboso ainda.
let empresa = {
nome: "Enviadores de Cotonetes",
dono: null
}
if (empresa === null) {
console.warn("Empresa não existe.")
} else if (empresa.dono === null) {
console.warn("Dono da empresa não existe.")
} else {
console.log(empresa.dono.nome) // -> irá retornar `undefined`
}
Embora horrendo, o código acima está checando tudo o que nós precisamos que ele cheque. Por sorte geralmente não escrevemos testes assim e a nossa linguagem nos dá uma forma mais elegante de lidar com isso.
let empresa = {
nome: "Enviadores de Cotonetes",
dono: null
}
console.log(empresa?.dono?.nome) // -> irá retornar `undefined`
Certo. O que está acontecendo no código acima? É bem simples. Imagine que toda vez que você está acessando algo dentro de um objeto (usando o .
como em empresa.dono
), você está fazendo isso acreditando que o valor anterior não é nullish
(null
ou undefined
). Vemos isso no código que eu chamei de horrendo. Eu só usei empresa.dono
após um if
que checava que empresa
não era nullish
. É como se eu estivesse deixando a opção aberta de que esse valor pode vir a ser nullish
. E é isso que que optional chaining (?.
) faz. Pense nele como uma pergunta. "O valor da esquerda é nullish
? Não? Então prossiga". Isso faz com que nós não recebamos nenhum tipo de erro, nem de tipo nem de referência, pois já estamos adicionando a possibilidade do valor ser nulo na mesma linha que chamamos a variável.
Os testes dos objetos pai não são mais necessários e nós podemos seguir em frente com a lógica de negócios.
let empresa = {
nome: "Enviadores de Cotonetes",
dono: null
}
if (empresa?.dono?.nome != null) {
console.log(`O nome do dono da empresa é ${empresa.dono.nome}.`)
} else {
console.warn("Nome do dono da empresa não existe.")
}
18