Com strictNullChecks ligado (ele faz parte do strict), null e undefined são tipos próprios. Uma string nunca pode ser null; um valor que pode faltar precisa dizer isso no tipo, como string | null, e o TypeScript obriga você a tratar esse caso antes de usar o valor.
Depois da verificação, o compilador sabe que name é uma string, então .split é permitido. O resto desta página mostra os jeitos de fazer essa verificação e os operadores que a encurtam.
strictNullChecks e os erros "possibly null"
Quando um tipo inclui null ou undefined, o TypeScript se recusa a usar o valor como se ele sempre existisse:
index.ts(3,22): error TS18047: 'name' is possibly 'null'.
A versão com undefined é a TS18048, 'x' is possibly 'undefined'. Sem strictNullChecks, null e undefined são aceitos em todo tipo e esse código compila, e depois quebra em tempo de execução com um TypeError na primeira vez em que name for null. Essa classe de bug é o principal motivo para manter o strict ligado.
De onde esses tipos vêm no código do dia a dia:
| Origem | Tipo |
|---|---|
arr.find(...) | T | undefined |
map.get(key) | V | undefined |
propriedade opcional p?: T | T | undefined ao ler |
parâmetro opcional x?: T | T | undefined dentro da função |
str.match(re) | RegExpMatchArray | null |
JSON.parse(text) | any, então nada é verificado |
Verificando null e undefined
Todas as verificações abaixo estreitam o tipo dentro do bloco. Escolha a que corresponde ao que você quer excluir.
| Verificação | Remove do tipo |
|---|---|
x !== undefined | undefined |
x !== null | null |
x != null | null e undefined |
typeof x !== "undefined" | undefined |
if (x) | null e undefined, e também descarta os valores 0, "", false, NaN |
== null é o único lugar em que a igualdade frouxa é idiomática: é verdadeira exatamente para null e undefined, e nada mais. Uma verificação de truthiness teria tratado a string vazia como ausente, o que muitas vezes é um bug.
Optional chaining: ?.
a?.b lê b se a não for null nem undefined; caso contrário, para e retorna undefined. O mesmo operador funciona com índices, a?.[i], e com chamadas, fn?.().
O tipo de bob.address?.city é string | undefined: o optional chaining acrescenta undefined ao resultado, então normalmente você o combina com ??. O comportamento em tempo de execução é JavaScript puro; veja optional chaining para os detalhes do curto-circuito.
A dupla interrogação: ??
a ?? b retorna a, a menos que ele seja null ou undefined; nesse caso retorna b. Ele substitui o idioma antigo a || b, que também descarta 0, "", false e NaN:
| Valor à esquerda | left || "d" | left ?? "d" |
|---|---|---|
null | "d" | "d" |
undefined | "d" | "d" |
0 | "d" | 0 |
"" | "d" | "" |
false | "d" | false |
NaN | "d" | NaN |
Nos tipos, ?? remove null e undefined do lado esquerdo e faz a union do resto com o lado direito, e é por isso que scores.get("Linus") ?? 0 pode ser atribuído a um number.
Atribuição nullish: ??=
a ??= b atribui b a a só quando a é null ou undefined. Os irmãos ||= e &&= atribuem quando o lado esquerdo é falsy ou truthy.
retries: 0 sobrevive ao ??=, enquanto o label vazio é substituído pelo ||=. Depois de opts.retries ??= 3, o TypeScript estreita opts.retries para number no resto da função.
Propriedades opcionais vs | undefined
nickname?: string e nickname: string | undefined se leem igual, mas diferem em se a chave precisa existir:
Use ? quando quem chama pode omitir a propriedade, e | undefined quando você quer que todos a passem explicitamente, mesmo que o valor seja undefined. A opção exactOptionalPropertyTypes (que não faz parte do strict) aperta ainda mais o ?: nickname?: string passa a rejeitar { nickname: undefined } e só aceita uma chave ausente ou uma string. Parâmetros opcionais de função (x?: number) se comportam como propriedades opcionais: dentro da função, x é number | undefined.
null ou undefined: qual usar
O TypeScript não impõe uma escolha, mas misturar os dois em uma base de código faz toda verificação ter que tratar dois casos. Uma convenção comum:
- Use
undefined(e propriedades opcionais) para "não definido" nos seus próprios tipos. É o que o JavaScript produz por padrão: propriedades ausentes, argumentos omitidos,findeMap.getsem resultado. - Aceite
nullonde uma API o entrega:JSONnão temundefined, eString.prototype.matche muitos métodos do DOM retornamnull. - Verifique com
== nullquando o valor pode ser qualquer um dos dois.
A indexação de arrays é a única brecha: users[5] tem o tipo do elemento mesmo quando o índice está fora do intervalo. A opção noUncheckedIndexedAccess (que não faz parte do strict) acrescenta | undefined a todo acesso por índice, para que o compilador pegue isso também.
Perguntas frequentes
O que significa a dupla interrogação no TypeScript?
a ?? b é o operador nullish coalescing do JavaScript. Ele retorna a, a menos que a seja null ou undefined; nesse caso retorna b. Diferente de ||, ele mantém outros valores falsy como 0, "" e false. O TypeScript remove null e undefined do tipo do lado esquerdo, então quando a é string | undefined, a ?? "x" é uma string.
Como verificar se um valor é undefined no TypeScript?
Compare: if (value !== undefined) { ... }. O TypeScript estreita o tipo dentro do bloco. Para descartar null e undefined em uma verificação só, use value != null (igualdade frouxa), o único lugar em que ==/!= é idiomático. Uma verificação de truthiness (if (value)) também faz narrowing, mas descarta 0, "" e false.
O que significa "Object is possibly undefined"?
Os erros TS18048 ('x' is possibly 'undefined') e TS18047 ('x' is possibly 'null'), ou TS2532 (Object is possibly 'undefined') quando o valor não tem um nome simples, como em getUser().address.city, vêm do strictNullChecks: o tipo inclui undefined ou null, e o código usa o valor como se ele não pudesse ser. Verifique antes, use optional chaining (x?.name), forneça um padrão com ?? ou mude o tipo se o valor realmente não puder faltar.
Qual é a diferença entre null e undefined no TypeScript?
São dois tipos separados, cada um com um único valor. O JavaScript usa undefined para coisas que nunca foram definidas (uma propriedade ausente, um argumento omitido, Map.get com uma chave inexistente), e as APIs usam null para um "sem valor" intencional (JSON, muitos métodos do DOM). O TypeScript os rastreia separadamente, então string | null não aceita undefined. Muitas bases de código escolhem undefined para o próprio código e só aceitam null nas fronteiras.
Uma propriedade opcional é o mesmo que | undefined?
Não exatamente. name?: string significa que a propriedade pode faltar por completo, e lê-la dá string | undefined. name: string | undefined significa que a propriedade precisa estar presente, mesmo que o valor seja undefined. Com exactOptionalPropertyTypes ligado, name?: string também deixa de aceitar um valor undefined explícito.