Valores null e undefined no JavaScript
Tags: javascript, typescript, null, undefined
Published: 2021-11-22 02:59:29

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.

### Acessando um valor não definido no JavaScript

```js
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.

#### No TypeScript

```ts
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.

### Acessando um valor nulo no JavaScript

```js
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".

### Arrumando o erro

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?

```js
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.

```js
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.

### Optional chaining

```js
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.

### E os testes?

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.

```js
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.")
}
```