Type Assertions — TypeScript

Type assertion memberitahu TypeScript bahwa kamu tahu tipe lebih spesifik: as keyword: const input = document.getElementById("email") as HTMLInputElement…

Type assertion adalah cara bilang ke TypeScript: "percaya aku, nilai ini bertipe X". Ditulis dengan kata as. TypeScript berhenti mengecek dan langsung percaya, tapi nilainya sendiri gak berubah sedikit pun. Mirip menempel label "gula" di toples: semua orang percaya labelnya, tapi kalau isinya garam, kopimu tetap asin.

🧪 Tebakan cepat: const user = JSON.parse('{"umur":17,"aktif":true}') as User, lalu user.nama.toUpperCase(). TypeScript protes gak? Dan apa yang terjadi waktu kodenya jalan? Tebak dulu, jawabannya ada di bagian "Assertion gak mengubah datanya".

as: kamu yang menjamin tipenya

Kadang kamu tahu lebih banyak dari TypeScript. Contohnya elemen HTML. TypeScript gak membaca file HTML-mu, jadi document.getElementById("email") cuma dikenal sebagai HTMLElement | null: elemen apa saja, atau gak ada. Tipe HTMLElement gak punya value, jadi baris kedua di bawah ditolak:

// Di HTML-mu ada <input id="email">
const el = document.getElementById("email")
el.value = "[email protected]"
// error TS18047: 'el' is possibly 'null'.
// error TS2339: Property 'value' does not exist on type 'HTMLElement'.

const input = document.getElementById("email") as HTMLInputElement
input.value = "[email protected]" // OK

Contoh kedua: JSON.parse selalu mengembalikan any, karena TypeScript gak tahu isi teks JSON sebelum kodenya jalan. Orang sering menulis JSON.parse(teks) as User supaya hasilnya punya tipe.

Assertion gak mengubah datanya

Type cuma ada waktu TypeScript mengecek. Sebelum kode jalan, semua type dihapus, termasuk as User. Ini kodenya, dan JS yang tersisa:

type User = { nama: string }

const teks = '{"umur":17,"aktif":true}'
const data: unknown = JSON.parse(teks)

const user = data as User // TypeScript: gak ada error
console.log(user.nama.toUpperCase())
// JS setelah type dihapus
const teks = '{"umur":17,"aktif":true}'
const data = JSON.parse(teks)
const user = data
console.log(user.nama.toUpperCase())

unknown artinya "belum tahu isinya", jadi TypeScript menolak data.nama sebelum ada yang menjamin. as User adalah jaminan itu. Hasilnya untuk tiga isi teks:

Isi teks TypeScript Console waktu jalan
{"nama":"Budi"} gak ada error BUDI
{"umur":17,"aktif":true} gak ada error TypeError: Cannot read properties of undefined (reading 'toUpperCase')
null gak ada error TypeError: Cannot read properties of null (reading 'nama')

Itu jawaban tebakannya: TypeScript diam saja, lalu programnya crash waktu jalan. Teks kedua gak punya nama, jadi user.nama itu undefined. Pesan error di tabel adalah pesan Chrome dan Node; browser lain menulisnya sedikit beda.

Yang ditolak as, dan yang lolos

as cuma menolak yang jelas mustahil, yaitu dua tipe yang sama sekali gak nyambung:

const n = "17" as number
// error TS2352: Conversion of type 'string' to type 'number' may be a mistake because
// neither type sufficiently overlaps with the other. If this was intentional,
// convert the expression to 'unknown' first.

const m = "17" as unknown as number // gak ada error
m.toFixed(1) // waktu jalan: TypeError: m.toFixed is not a function

satisfies: dicek, tapi tipenya tetap

satisfies (sejak TypeScript 4.9) kebalikan dari as: dia mengecek nilainya cocok dengan sebuah type, tapi gak mengganti tipe yang ditebak TypeScript.

type Config = { port: number | string }

const a: Config = { port: 3000 }
a.port.toFixed(0)
// error TS2339: Property 'toFixed' does not exist on type 'string | number'.

const b = { port: 3000 } satisfies Config
b.port.toFixed(0) // OK: b.port tetap number

const c = { port: true } satisfies Config
// error TS2322: Type 'boolean' is not assignable to type 'string | number'.

Karena satisfies benar-benar mengecek, dia gak bisa dipakai buat data yang belum jelas. data satisfies User dengan data: unknown ditolak: error TS1360: Type 'unknown' does not satisfy the expected type 'User'.

Data dari luar: cek dengan type guard

Data dari server, localStorage atau input user gak bisa dijamin dengan as. Yang aman adalah type guard dari pelajaran type narrowing: fungsi yang benar-benar mengecek isinya waktu jalan.

function isUser(x: unknown): x is User {
  return typeof x === "object" && x !== null && "nama" in x && typeof x.nama === "string"
}

const data: unknown = JSON.parse(teks)
if (isUser(data)) {
  console.log(data.nama.toUpperCase()) // di sini data: User
} else {
  console.log("Bukan User")
}

Dengan {"nama":"Budi"} hasilnya BUDI. Dengan {"umur":17,"aktif":true} atau null hasilnya Bukan User, gak ada crash. Pengecekan typeof dan in tetap ada di JS, gak ikut dihapus.

⚠️ Jebakan yang sering ketemu

TL;DR: as Tipe menyuruh TypeScript percaya, tanpa mengecek dan tanpa mengubah nilainya, karena semua type dihapus sebelum kode jalan. as cuma menolak tipe yang mustahil (TS2352); as unknown as dan ! gak menolak apa pun, jadi kesalahannya muncul sebagai crash. satisfies mengecek nilai yang kamu tulis sendiri dan tetap menyimpan tipe aslinya. Untuk data dari luar, pakai type guard yang mengecek isinya waktu jalan. JSON.parse mengembalikan any: objek hasil uraian teksnya.

Yang akan kamu pelajari