In Typescript, qual è il! (punto esclamativo / botto) operatore quando si dereferenzia un membro?


453

Quando ho esaminato il codice sorgente per una regola tslint, mi sono imbattuto nella seguente dichiarazione:

if (node.parent!.kind === ts.SyntaxKind.ObjectLiteralExpression) {
    return;
}

Si noti l' !operatore dopo node.parent. Interessante!

Prima ho provato a compilare il file localmente con la mia versione attualmente installata di TS (1.5.3). L'errore risultante ha indicato la posizione esatta del botto:

$ tsc --noImplicitAny memberAccessRule.ts 
noPublicModifierRule.ts(57,24): error TS1005: ')' expected.

Successivamente ho eseguito l'aggiornamento all'ultimo TS (2.1.6), che lo ha compilato senza problemi. Quindi sembra essere una caratteristica di TS 2.x. Ma la transpilation ha ignorato completamente il botto, risultando nel seguente JS:

if (node.parent.kind === ts.SyntaxKind.ObjectLiteralExpression) {
    return;
}

Finora il mio Google Fu non mi ha deluso.

Cos'è l'operatore del punto esclamativo di TS e come funziona?

Risposte:


688

Questo è l'operatore di asserzione non nullo. È un modo per dire al compilatore "questa espressione non può essere nullo undefinedqui, quindi non lamentarti della possibilità che sia nullo undefined". A volte il controllo del tipo non è in grado di effettuare tale determinazione da solo.

È spiegato qui :

Un nuovo !operatore di espressione post-correzione può essere usato per affermare che il suo operando è non nullo e non indefinito in contesti in cui il controllo del tipo non è in grado di concludere tale fatto. In particolare, l'operazione x!produce un valore del tipo di xcon nulled undefinedescluso. Simile alle asserzioni di tipo dei moduli <T>xe x as T, l' !operatore di asserzione non null viene semplicemente rimosso nel codice JavaScript emesso.

Trovo l'uso del termine "asserire" un po 'fuorviante in quella spiegazione. È "asserire" nel senso che lo sviluppatore lo sta affermando , non nel senso che verrà eseguito un test. L'ultima riga indica infatti che non viene emesso alcun codice JavaScript.


102
Buona chiamata all'ambiguità 'asserire'.
Estus Flask

8
Buona spiegazione Trovo una buona pratica fare un console.assert()sulla variabile in questione prima di aggiungere un !dopo. Poiché add !sta dicendo al compilatore di ignorare il controllo null, compila noop in javascript. Pertanto, se non si è certi che la variabile sia diversa da null, è consigliabile eseguire un controllo di asserzione esplicito.
Jayesh,

12
Come esempio motivante: l'utilizzo del nuovo tipo di ES Map con codice come dict.has(key) ? dict.get(key) : 'default';il compilatore TS non può dedurre che la getchiamata non restituisce mai null / indefinito. dict.has(key) ? dict.get(key)! : 'default';restringe il tipo correttamente.
kitsu.eb,

1
Esiste un gergo per questo operatore, come il modo in cui l'operatore Elvis si riferisce all'operatore binario?
ebakunin

@Jayesh potresti espandere la buona pratica console.assert (), potresti pubblicare un esempio?
Christopher Francisco,

168

La risposta di Louis è fantastica, ma ho pensato di provare a riassumere in breve:

L'operatore bang dice al compilatore di allentare temporaneamente il vincolo "non nullo" che altrimenti potrebbe richiedere. Dice al compilatore: "Come sviluppatore, so meglio di te che questa variabile non può essere nulla al momento".


85
Quindi, come sviluppatore, hai incasinato.
Mike Chamberlain,

9
Oppure, come compilatore, ha incasinato. Se il costruttore non inizializza una proprietà ma un hook del ciclo di vita lo fa e il compilatore non lo riconosce.
Mukus,

26
Questa non è responsabilità del compilatore TS. A differenza di altre lingue (es. C #), JS (e quindi TS) non richiede l'inizializzazione delle variabili prima dell'uso. Oppure, per guardarlo in un altro modo, in JS tutte le variabili dichiarate con varo letsono inizializzate implicitamente undefined. Inoltre, le proprietà dell'istanza di classe possono essere dichiarate come tali, quindi class C { constructor() { this.myVar = undefined; } }è perfettamente legale. Infine, gli hook del ciclo di vita dipendono dal framework; ad esempio Angular e React li implementano in modo diverso. Quindi non ci si può aspettare che il compilatore TS possa ragionare su di loro.
Mike Chamberlain,

1
Esiste un caso d'uso valido per l'operatore del botto considerando la meraviglia dell'analisi del tipo basata sul flusso di controllo in TS?
Eugene Karataev,

1
@EugeneKarataev Sì, i framework spesso inizializzano le variabili al loro interno e l'analisi del flusso di controllo ts non riesce a rilevarla. Il suo utilizzo è certamente ridotto, ma ti imbatterai in casi in cui ne hai bisogno.
arg20,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.