null e undefined em TypeScript
Assim como no JavaScript, no TypeScript existem os tipos especiais undefined e null, que aceitam, respectivamente, os valores undefined e null.
O uso de undefined e null depende da configuração do compilador, especificamente do parâmetro strictNullChecks. Por padrão, esse parâmetro está definido como false, o que significa que podemos usar esses tipos como no JavaScript:
let a: undefined = undefined;
let b: null = null;Na prática, podemos atribuir os valores undefined e null a variáveis de outros tipos, como number:
let x: number = undefined;
console.log(x);
x = null;
console.log(x);
x = 5;
console.log(x);Nesse contexto, null e undefined atuam como subtipos de outros tipos e são úteis em operações onde o resultado é incerto: por exemplo, quando o valor pode ser um número, uma string ou null. Para evitar possíveis erros, podemos verificar se o valor é undefined ou null, da mesma forma que no JavaScript.
Contudo, a ausência de verificações desses tipos pelo compilador pode ser uma fonte de bugs. Por isso, é comum utilizar o parâmetro strictNullChecks na compilação. Isso pode ser feito ao ativar a flag no comando de compilação:
--strictNullChecks
Ou definindo o valor true no arquivo de configuração tsconfig.json:
{
    "compilerOptions": {
        "target": "es2015",
        "noImplicitAny": true,
        "noEmitOnError": true,
        "strictNullChecks": true,
        "outFile": "app.js"
    }
}Nesse cenário, se tentarmos atribuir um valor do tipo undefined ou null a uma variável de outro tipo, o compilador lançará um erro. Por exemplo, ao compilar o código anterior:
let x: number = undefined;
console.log(x);
x = null;
console.log(x);
x = 5;
console.log(x);O compilador produzirá as seguintes mensagens de erro:
app.ts:1:5 - error TS2322: Type 'undefined' is not assignable to type 'number'. let x: number = undefined; app.ts:3:1 - error TS2322: Type 'null' is not assignable to type 'number'. x = null; Found 2 errors.
Também ocorrerá um erro se tentarmos atribuir o valor undefined a uma variável do tipo null, ou vice-versa.
Ainda assim, existem situações em que não sabemos com certeza se uma variável, ou um parâmetro de função, contém um valor ou se ele está ausente. Isso é especialmente comum quando recebemos dados de uma fonte externa, como uma requisição a um recurso de rede. Nesses casos, pode ser necessário permitir que uma variável ou parâmetro aceite o valor null. Para isso, podemos usar tipos de união. Exemplo:
let userId: number | null = null;
function printId(id: number | null) {
  if (id === null) {
    console.log("Usuário ausente");
  } else {
    console.log(`ID do usuário: ${id}`);
  }
}
printId(userId); // Usuário ausente
userId = 45;
printId(userId); // ID do usuário: 45Aqui, a variável userId representa uma união entre os tipos number e null, permitindo que ela receba tanto um valor numérico quanto null.
De maneira similar, a função printId, através do parâmetro id, pode receber tanto um valor null quanto um número.
Operador !
O operador ! (non-null assertion operator) permite indicar que um objeto não é null ou undefined. Veja o exemplo abaixo:
const header: HTMLElement | null = document.getElementById("header");
header.innerText = "Hello TypeScript!";A função embutida document.getElementById() retorna um elemento da página da web pelo seu ID, o que pode ser do tipo HTMLElement ou null, caso o elemento com o ID fornecido não exista. Após obter o elemento, tentamos alterar seu conteúdo de texto usando a propriedade innerText.
Embora o código pareça correto, com a opção strictNullChecks ativada, o compilador lançará um erro durante a compilação:
app.ts:2:1 - error TS2531: Object is possibly 'null'. header.innerText = "Hello TypeScript!"; Found 1 error.
Para evitar o erro, usamos o operador !:
const header: HTMLElement | null = document.getElementById("header");
header!.innerText = "Hello TypeScript!";O operador ! é colocado após o objeto que pode teoricamente ser null, antes de acessar suas propriedades ou métodos:
object!.property;
object!.method();No entanto, é importante lembrar que este operador não altera o valor do objeto. Se o objeto for null ou undefined, o operador não irá impedir erros. O código será compilado, mas, durante a execução, o programa ainda lançará um erro.
Portanto, recomenda-se utilizar esse operador apenas quando tivermos certeza absoluta de que o objeto não é null ou undefined.