Menu

Exclamação em TypeScript: o non-null assertion (!)

Um ponto de exclamação depois de um valor, como user!, é o operador non-null assertion: ele remove null e undefined do tipo sem nenhuma verificação em tempo de execução. Veja o que x! faz, as formas de definite assignment let x!: T e prop!: T, por que são arriscadas e alternativas mais seguras.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

Um ponto de exclamação depois de uma expressão, value!, é o operador non-null assertion. Ele remove null e undefined do tipo, então um number | undefined pode ser usado como number. É uma promessa ao compilador, não uma verificação: nada acontece em tempo de execução.

Sem o !, tea.toFixed(2) gera o erro TS18048, 'tea' is possibly 'undefined'. Com ele, o código compila porque você disse ao compilador que a chave existe.

O que x! faz em tempo de execução: nada

O ! é apagado da saída. O JavaScript compilado de prices.get("coffee")! é só prices.get("coffee"). Se a afirmação estiver errada, o erro aparece depois, no primeiro lugar em que o valor ausente é usado:

O programa imprime Cannot read properties of undefined (reading 'toFixed'). A quebra acontece na linha depois do !, e em código real pode ficar bem mais longe: o undefined pode ser guardado em um objeto e estourar em outro arquivo. Essa distância é o que torna o ! arriscado.

Definite assignment: let x!: T

O mesmo símbolo em uma declaração significa algo parecido. O TypeScript acompanha se uma variável é atribuída antes de ser lida, e não consegue seguir uma atribuição feita dentro de outra função:

index.ts(9,13): error TS2454: Variable 'config' is used before being assigned.

let config!: { port: number }; é uma definite assignment assertion: "isto será atribuído antes de qualquer leitura". Ela resolve o erro, com o mesmo porém do x!: se init() for pulado algum dia, a leitura recebe undefined. Reestruturar costuma ser melhor, por exemplo const config = init(); com init retornando o objeto.

Propriedades de classe: prop!: T

Com strictPropertyInitialization (parte do strict), toda propriedade de classe precisa ser inicializada na declaração ou no construtor. Senão você recebe o TS2564: Property 'socket' has no initializer and is not definitely assigned in the constructor. Quando uma propriedade é definida depois por um método ou por um framework, prop!: T diz ao compilador para aceitá-la:

O primeiro console.log mostra a brecha: o tipo diz que socket sempre existe, mas antes de open() ele é undefined. Chamar c.socket.send nesse momento compila e lança um erro. Se a propriedade pode mesmo faltar, declare-a como socket?: ... e verifique, ou crie o objeto no construtor. O lugar principal em que prop! é prática padrão é um framework que preenche uma propriedade depois da construção: o @ViewChild(...) child!: ChildDirective do Angular (definido antes de ngAfterViewInit rodar), ou classes de entidade de ORM cujas colunas a biblioteca preenche ao carregar uma linha (a documentação do MikroORM escreve @Property() title!: string).

Alternativas mais seguras

A maioria dos ! pode ser trocada por algo que o compilador verifica, ou por uma verificação que falha de forma clara no lugar certo:

Os dois helpers estreitam o tipo como o ! faz, mas uma suposição errada produz missing HOST na hora, em vez de um TypeError em outro lugar. assertDefined é uma assertion function (asserts value is ...): depois da chamada, o compilador trata host como string. Mais padrões em type guards.

Em vez deEscrevaO que acontece quando o valor falta
user!.nameif (user) { user.name }o bloco é pulado
user!.nameuser?.nameundefined
count!count ?? 0o padrão é usado
map.get(k)!must(map.get(k), "k")um erro claro naquela linha
let x!: Tconst x = compute()nada pode dar errado

Os outros pontos de exclamação

O ! significa coisas diferentes dependendo de onde está:

CódigoSignificado
value! (depois de uma expressão)non-null assertion, só no TypeScript
let x!: T, prop!: Tdefinite assignment assertion, só no TypeScript
!value (antes de uma expressão)NOT lógico, JavaScript puro
!!valueconverte para boolean, JavaScript puro
a !== b, a != bdesigualdade, JavaScript puro

Só os dois primeiros são apagados na compilação. !value e !!value rodam em tempo de execução e retornam um boolean.

Perguntas frequentes

O que significa um ponto de exclamação depois de uma variável no TypeScript?

value! é o operador non-null assertion. Ele diz ao compilador que value não é null nem undefined, então o tipo perde esses dois membros: string | undefined vira string. Ele é removido do JavaScript gerado e não acrescenta nenhuma verificação em tempo de execução, então, se você estiver errado, o programa falha depois com um TypeError.

Qual é a diferença entre ! e ? no TypeScript?

x! afirma que o valor existe e dá a você o tipo sem null, sem verificar nada. x?.y verifica em tempo de execução: se x for null ou undefined, ele para e retorna undefined. Em uma declaração, name?: string torna uma propriedade opcional, enquanto name!: string diz que uma propriedade obrigatória será atribuída em algum lugar que o compilador não enxerga.

O que significa let x!: string?

É uma definite assignment assertion. Ela diz ao compilador que a variável será atribuída antes de ser lida, mesmo que ele não consiga provar isso (por exemplo, a atribuição acontece dentro de outra função). Sem ela, ler a variável gera o erro TS2454, Variable 'x' is used before being assigned.

Como corrigir "has no initializer and is not definitely assigned in the constructor"?

Esse é o erro TS2564 do strictPropertyInitialization. Dê um valor inicial à propriedade, atribua-a no construtor, torne-a opcional (prop?: T) ou, se um framework ou um método de inicialização realmente a define antes do uso, escreva prop!: T. O ! é a última opção porque nada verifica a promessa.

O operador non-null assertion é uma má prática?

Não é errado, mas cada ! é uma afirmação não verificada. Configurações de lint como @typescript-eslint/no-non-null-assertion o apontam. Prefira uma verificação que faça narrowing (if (x), x ?? fallback, x?.y) ou um helper que lance um erro claro. Deixe o ! para lugares em que o valor é garantido por uma lógica que o compilador não consegue acompanhar.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR