Diferença entre '!' e '?' em TypeScript
Class Employee {
firstName: string;
lastName!: string;
middleName?: string;
}
Qual é a diferença nesses 3 diferentes campos de Employeeaula?
Exemplo ao Vivo
Respostas
O ?nessa posição marca a propriedade opcional .
O !nessa posição é a afirmação de atribuição definitiva . É uma espécie de versão em nível de declaração do operador de asserção não nulo , mas usado em uma propriedade (também pode ser usado em variáveis) em vez de em uma expressão.
Existem dois - ou possivelmente três - erros nesse exemplo:
Classdeve serclass; JavaScript e TypeScript diferenciam maiúsculas de minúsculas.Você precisa de um inicializador
firstName(ou um construtor que atribua a ele incondicionalmente).O
!onlastNamediz ao TypeScript quelastNamedefinitivamente será atribuído, suprimindo o tipo de erro que você está obtendofirstName, mas nada (no exemplo) realmente faz a atribuição que usar!lá promete TypeScript que você tem certeza de que está fazendo.
Editar: O código que você vinculou posteriormente trata dos itens 1 e 2 acima, mas não 3. O TypeScript não avisará que lastNamenunca é atribuído e assume que seu valor é uma string, quando na verdade ela não está lá e, portanto, a leitura de seu valor resultará em undefined.
Eles estão bem escondidos na documentação do TypeScript .
?é descrito em interfaces , ele marca uma propriedade opcional .
!é o operador de asserção definitivo . Diz ao compilador que a propriedade está definida (não nullou undefined) mesmo se as análises do TypeScript não puderem detectar isso.
Btw, Classnão é uma palavra-chave TypeScript ou JavaScript e produz um erro nessa posição. A palavra-chave para declarar uma classe é class. Identificadores e palavras-chave TypeScript e JavaScript diferenciam maiúsculas de minúsculas ( Classe classsão coisas diferentes).
Ao ter a opção de compilador strictNullChecks: false
Se você tiver strictNullChecks: falseem seu tsconfig.json, eles são exatamente os mesmos, pois ter strictNullChecksdesabilitado significa que todos os campos são nulos ou indefinidos como valores válidos.
Ao ter a opção de compilador strictNullChecks: true
class Employee {
firstName: string;
lastName!: string;
middleName?: string;
}
firstName: stringsignifica que firstNamedeve ser um string. nullou undefinednão são valores válidos para firstName.
Todos os campos terão um valor padrão, undefinedo que resultará em um erroProperty 'firstName' has no initializer and is not definitely assigned in constructor
Para silenciar o erro, você precisa alterar a declaração firstName: string = 'Some default value'ou adicionar um construtor e atribuir um valor para ele no construtor.
constructor() {
this.firstName = 'some default value';
}
Agora, para o! sintaxe. A lastName!: stringsintaxe é semelhante a porque lastName: stringbasicamente diz que only stringé o único tipo permitido. nulle undefinednão são permitidos. Mas isso silenciará o compilador sobre o erro de atribuição definitivo. Digamos que você tenha o seguinte código.
class Employee {firstName: string; lastName !: string; middleName ?: string;
constructor() {
// This will silence the compiler about first name not initialized
this.firstName = 'some default value';
// The compiler cannot tell that lastName is assigned in init() function
this.init();
}
private init(): void {
this.lastName = 'some default value';
}
}
No código anterior, lastNameé definitivamente atribuído na constructorvia da this.init()chamada. No entanto, o compilador não pode saber disso. Então, adicionando o! basicamente diz ao compilador "cale a boca, eu sei o que estou fazendo" . Cabe a você garantir a correção do seu código.
Sobre a middleName?: stringsintaxe. Isso é exatamente o mesmo que middleName: string | undefined; Como todos os valores têm o valor padrão de undefined, o compilador não reclamará que middleNamenão foi atribuído.