Saat strictNullChecks aktif (opsi ini bagian dari strict), null dan undefined punya tipe sendiri. Sebuah string tidak akan pernah bisa bernilai null; nilai yang mungkin kosong harus menyatakannya di tipenya, misalnya string | null, dan TypeScript memaksamu menangani kasus itu sebelum memakai nilainya.
Setelah pengecekan itu, compiler tahu bahwa name adalah string, jadi .split boleh dipanggil. Bagian selanjutnya membahas berbagai cara melakukan pengecekan itu dan operator yang membuatnya singkat.
strictNullChecks dan Error "Possibly Null"
Kalau sebuah tipe memuat null atau undefined, TypeScript menolak memakai nilainya seolah nilai itu selalu ada:
index.ts(3,22): error TS18047: 'name' is possibly 'null'.
Versi untuk undefined adalah TS18048, 'x' is possibly 'undefined'. Tanpa strictNullChecks, null dan undefined diterima di semua tipe, sehingga kode ini lolos kompilasi, lalu crash saat runtime dengan TypeError begitu name bernilai null. Jenis bug inilah alasan utama untuk tetap mengaktifkan strict.
Dari mana tipe-tipe ini muncul dalam kode sehari-hari:
| Sumber | Tipe |
|---|---|
arr.find(...) | T | undefined |
map.get(key) | V | undefined |
property opsional p?: T | T | undefined saat dibaca |
parameter opsional x?: T | T | undefined di dalam fungsi |
str.match(re) | RegExpMatchArray | null |
JSON.parse(text) | any, jadi tidak ada yang dicek |
Mengecek null dan undefined
Setiap pengecekan di bawah ini mempersempit tipe di dalam bloknya. Pilih yang sesuai dengan apa yang ingin kamu singkirkan.
| Pengecekan | Yang dihapus dari tipe |
|---|---|
x !== undefined | undefined |
x !== null | null |
x != null | null dan undefined |
typeof x !== "undefined" | undefined |
if (x) | null dan undefined, sekaligus melewatkan nilai 0, "", false, NaN |
== null adalah satu-satunya tempat kesamaan longgar dianggap wajar: hasilnya true tepat untuk null dan undefined, tidak ada yang lain. Pengecekan truthy akan menganggap string kosong sebagai nilai yang hilang, dan itu sering menjadi bug.
Optional Chaining: ?.
a?.b membaca b kalau a bukan null atau undefined, dan kalau tidak, berhenti lalu mengembalikan undefined. Operator yang sama bisa dipakai untuk index, a?.[i], dan untuk pemanggilan fungsi, fn?.().
Tipe bob.address?.city adalah string | undefined: optional chaining menambahkan undefined ke hasilnya, jadi biasanya kamu memasangkannya dengan ??. Perilakunya saat runtime adalah JavaScript biasa; lihat optional chaining untuk detail short-circuiting.
Tanda Tanya Ganda: ??
a ?? b mengembalikan a, kecuali kalau nilainya null atau undefined, dan dalam hal itu mengembalikan b. Operator ini menggantikan pola lama a || b, yang juga membuang 0, "", false dan NaN:
| Nilai kiri | left || "d" | left ?? "d" |
|---|---|---|
null | "d" | "d" |
undefined | "d" | "d" |
0 | "d" | 0 |
"" | "d" | "" |
false | "d" | false |
NaN | "d" | NaN |
Pada level tipe, ?? menghapus null dan undefined dari sisi kiri lalu menggabungkan sisanya dengan sisi kanan dalam sebuah union, itulah sebabnya scores.get("Linus") ?? 0 bisa di-assign ke number.
Nullish Assignment: ??=
a ??= b meng-assign b ke a hanya kalau a bernilai null atau undefined. Saudaranya, ||= dan &&=, meng-assign saat sisi kiri bernilai falsy atau truthy.
retries: 0 tetap bertahan setelah ??=, sedangkan label yang kosong diganti oleh ||=. Setelah opts.retries ??= 3, TypeScript mempersempit opts.retries menjadi number untuk sisa fungsi itu.
Property Opsional vs | undefined
nickname?: string dan nickname: string | undefined terlihat mirip, tetapi berbeda dalam hal apakah key-nya wajib ada:
Pakai ? kalau pemanggil boleh tidak menyertakan property itu, dan | undefined kalau kamu ingin setiap pemanggil mengisinya secara eksplisit, walaupun nilainya undefined. Opsi exactOptionalPropertyTypes (bukan bagian dari strict) membuat ? lebih ketat lagi: nickname?: string lalu menolak { nickname: undefined } dan hanya menerima key yang tidak ada atau sebuah string. Parameter fungsi opsional (x?: number) berperilaku seperti property opsional: di dalam fungsi, x bertipe number | undefined.
null atau undefined: Mana yang Dipakai
TypeScript tidak memaksamu memilih, tetapi mencampur keduanya dalam satu codebase berarti setiap pengecekan harus menangani dua kasus. Konvensi yang umum:
- Pakai
undefined(dan property opsional) untuk "belum diisi" di tipe buatanmu sendiri. Itulah yang dihasilkan JavaScript secara default: property yang tidak ada, argumen yang dilewati,finddanMap.getyang tidak menemukan apa pun. - Terima
nulldi tempat sebuah API memberikannya:JSONtidak punyaundefined, sedangkanString.prototype.matchdan banyak method DOM mengembalikannull. - Cek dengan
== nullkalau sebuah nilai bisa salah satu dari keduanya.
Pengaksesan array lewat index adalah satu-satunya celah: users[5] diberi tipe elemen walaupun index-nya di luar jangkauan. Opsi noUncheckedIndexedAccess (bukan bagian dari strict) menambahkan | undefined ke setiap akses index supaya compiler juga menangkap kasus itu.
Pertanyaan yang sering diajukan
Apa arti tanda tanya ganda di TypeScript?
a ?? b adalah operator nullish coalescing dari JavaScript. Operator ini mengembalikan a, kecuali kalau a bernilai null atau undefined, dan dalam hal itu mengembalikan b. Berbeda dengan ||, operator ini tetap mempertahankan nilai falsy lain seperti 0, "" dan false. TypeScript menghapus null dan undefined dari tipe sisi kiri, jadi kalau a bertipe string | undefined, a ?? "x" bertipe string.
Bagaimana cara mengecek apakah sebuah nilai undefined di TypeScript?
Bandingkan saja: if (value !== undefined) { ... }. TypeScript mempersempit tipenya di dalam blok itu. Untuk menyingkirkan null dan undefined sekaligus dalam satu pengecekan, pakai value != null (kesamaan longgar), satu-satunya tempat ==/!= dianggap wajar. Pengecekan truthy (if (value)) juga mempersempit tipe, tetapi ikut melewatkan 0, "" dan false.
Apa arti "Object is possibly undefined"?
Error TS18048 ('x' is possibly 'undefined') dan TS18047 ('x' is possibly 'null'), atau TS2532 (Object is possibly 'undefined') kalau nilainya tidak punya nama sederhana, seperti pada getUser().address.city, berasal dari strictNullChecks: tipenya memuat undefined atau null, tetapi kodenya memakai nilai itu seolah tidak mungkin kosong. Cek dulu nilainya, pakai optional chaining (x?.name), beri nilai default dengan ??, atau ubah tipenya kalau nilai itu memang tidak mungkin hilang.
Apa perbedaan null dan undefined di TypeScript?
Keduanya adalah dua tipe terpisah yang masing-masing hanya punya satu nilai. JavaScript memakai undefined untuk sesuatu yang belum pernah diisi (property yang tidak ada, argumen yang dilewati, Map.get pada key yang tidak ada), sedangkan API memakai null untuk "tidak ada nilai" yang disengaja (JSON, banyak method DOM). TypeScript melacak keduanya secara terpisah, jadi string | null tidak menerima undefined. Banyak codebase memilih undefined untuk kodenya sendiri dan hanya menerima null di batas dengan kode luar.
Apakah property opsional sama dengan | undefined?
Tidak persis. name?: string berarti property itu boleh tidak ada sama sekali, dan membacanya menghasilkan string | undefined. name: string | undefined berarti property itu wajib ada, walaupun nilainya undefined. Kalau exactOptionalPropertyTypes aktif, name?: string juga tidak lagi menerima nilai undefined yang ditulis secara eksplisit.