Dalam naskah, apa itu! operator (tanda seru / letusan) ketika mendereferensi anggota?

453

Ketika melihat kode sumber untuk aturan tslint, saya menemukan pernyataan berikut:

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

Perhatikan !operator setelahnya node.parent. Menarik!

Saya pertama kali mencoba mengkompilasi file secara lokal dengan versi TS saya yang terinstal (1.5.3). Kesalahan yang dihasilkan menunjuk ke lokasi tepat bang:

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

Selanjutnya saya upgrade ke TS terbaru (2.1.6), yang mengkompilasinya tanpa masalah. Jadi sepertinya fitur TS 2.x. Tetapi transpilasi mengabaikan bang sepenuhnya, menghasilkan JS berikut:

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

Google fu saya sejauh ini telah mengecewakan saya.

Apa yang dimaksud dengan tanda seru TS, dan bagaimana cara kerjanya?

Mike Chamberlain
sumber

Jawaban:

688

Itu operator penegasan yang tidak nol. Ini adalah cara untuk memberitahu kompiler "ungkapan ini tidak bisa nullatau di undefinedsini, jadi jangan mengeluh tentang kemungkinan itu nullatau undefined". Terkadang pemeriksa tipe tidak dapat membuat penentuan itu sendiri.

Dijelaskan di sini :

Sebuah baru !Operator ekspresi pasca-fix dapat digunakan untuk menegaskan bahwa operan adalah non-nol dan non-terdefinisi dalam konteks di mana jenis checker tidak dapat menyimpulkan fakta bahwa. Secara khusus, operasi x!menghasilkan nilai tipe xdengan nulldan undefineddikecualikan. Mirip dengan jenis pernyataan bentuk <T>xdan x as T, !operator pernyataan non-nol hanya dihapus dalam kode JavaScript yang dipancarkan.

Saya menemukan penggunaan istilah "menegaskan" agak menyesatkan dalam penjelasan itu. Ini "menegaskan" dalam arti bahwa pengembang menegaskannya , bukan dalam arti bahwa tes akan dilakukan. Baris terakhir memang menunjukkan bahwa itu tidak menghasilkan kode JavaScript yang dipancarkan.

Louis
sumber
102
Panggilan bagus pada ambiguitas 'tegas'.
Estus Flask
8
Penjelasan yang bagus. Saya menemukan itu praktik yang baik untuk melakukan console.assert()pada variabel yang dimaksud sebelum menambahkan !setelahnya. Karena add !memberitahu kompiler untuk mengabaikan cek nol, ia mengkompilasi ke noop di javascript. Jadi, jika Anda tidak yakin bahwa variabel tersebut bukan nol, maka lebih baik lakukan pemeriksaan tegas eksplisit.
Jayesh
12
Sebagai contoh yang memotivasi: menggunakan tipe ES Map baru dengan kode seperti dict.has(key) ? dict.get(key) : 'default';kompiler TS tidak dapat menyimpulkan bahwa getpanggilan tidak pernah mengembalikan null / undefined. dict.has(key) ? dict.get(key)! : 'default';mempersempit jenis dengan benar.
kitsu.eb
1
Apakah ada gaul untuk operator ini, seperti bagaimana operator Elvis merujuk ke operator biner?
ebakunin
@ Jayesh dapatkah Anda memperluas pada console.assert () praktik yang baik, dapatkah Anda memposting contoh?
Christopher Francisco
168

Jawaban Louis sangat bagus, tetapi saya pikir saya akan mencoba merangkumnya dengan ringkas:

Operator bang memberitahu kompiler untuk sementara waktu mengendurkan batasan "bukan nol" yang mungkin diminta. Ia mengatakan kepada kompiler: "Sebagai pengembang, saya tahu lebih baik daripada Anda bahwa variabel ini tidak boleh nol sekarang".

Mike Chamberlain
sumber
85
Kemudian, sebagai pengembang, Anda telah mengacaukannya.
Mike Chamberlain
9
Atau, sebagai kompiler, itu telah kacau. Jika konstruktor tidak menginisialisasi properti tetapi kait siklus hidup melakukannya dan kompilator tidak mengenali ini.
Mukus
26
Ini bukan tanggung jawab kompiler TS. Tidak seperti beberapa bahasa lain (mis. C #), JS (dan karenanya TS) tidak menuntut variabel diinisialisasi sebelum digunakan. Atau, untuk melihatnya dengan cara lain, di JS semua variabel dideklarasikan dengan varatau letdiinisialisasi secara implisit undefined. Selanjutnya, properti instance kelas dapat dideklarasikan seperti itu, sehingga class C { constructor() { this.myVar = undefined; } }sangat legal. Akhirnya, kait siklus hidup bergantung pada kerangka; misalnya Angular dan React menerapkannya secara berbeda. Jadi kompiler TS tidak dapat diharapkan untuk alasan tentang mereka.
Mike Chamberlain
1
Apakah ada use case yang valid untuk operator bang mempertimbangkan tingkat keawetan analisis tipe berbasis aliran kontrol di TS?
Eugene Karataev
1
@ EugeneKarataev Ya ada, kerangka kerja sering menginisialisasi variabel dalam diri mereka sendiri dan analisis aliran kontrol ts tidak dapat menangkapnya. Penggunaannya tentu berkurang, tetapi Anda akan menemukan contoh di mana Anda membutuhkannya.
arg20