typeof punya dua arti di TypeScript. Di dalam ekspresi, ia adalah operator JavaScript: ia berjalan, mengembalikan string seperti "string" atau "number", dan TypeScript mempersempit variabelnya ketika kamu membandingkan string itu. Di posisi tipe, ia adalah type query: typeof config menghasilkan tipe statis dari variabel config.
Baris terakhir memperlihatkan bedanya: typeof custom di dalam console.log adalah operator runtime dan mencetak object, sedangkan typeof defaults setelah type Options = adalah query saat kompilasi dan tidak pernah sampai ke JavaScript.
Dua Arti Sekilas
typeof runtime (JavaScript) | typeof type query (TypeScript) | |
|---|---|---|
| Tempat munculnya | Ekspresi apa pun: if, return, console.log(...) | Posisi tipe: setelah :, di type X = ..., di dalam <...> |
| Contoh | typeof x === "string" | let y: typeof x; |
| Dievaluasi | Saat kode berjalan | Oleh compiler, lalu dihapus |
| Hasil | Salah satu dari delapan string | Sebuah tipe |
| Dipakai untuk | Memeriksa nilai dan mempersempit tipenya | Menurunkan tipe dari sebuah nilai |
TypeScript membedakan keduanya dari posisinya, jadi kata kunci yang sama tidak pernah berarti keduanya sekaligus.
Hasil typeof Saat Runtime
Operator runtime mengembalikan salah satu dari delapan string. Jalankan kode ini untuk melihat hasil dari nilai-nilai yang umum:
| Nilai | Hasil typeof | Catatan |
|---|---|---|
"hi", template string | "string" | |
42, 3.14, NaN, Infinity | "number" | NaN adalah number; periksa dengan Number.isNaN |
10n | "bigint" | |
true, false | "boolean" | |
undefined | "undefined" | |
Symbol("id") | "symbol" | |
| fungsi, arrow function, class | "function" | class adalah fungsi constructor |
null | "object" | bug historis di JavaScript |
{}, [], new Date(), new Map() | "object" | array adalah object: pakai Array.isArray |
new String("x"), new Number(1) | "object" | wrapper object, hindari |
Di JavaScript biasa, typeof pada variabel yang tidak pernah dideklarasikan mengembalikan "undefined", bukan melempar error. Di TypeScript, kode itu tidak lolos kompilasi: Cannot find name 'notDeclared'. (TS2304).
Narrowing dengan typeof
Perbandingan typeof di dalam if, switch, operator ternary, atau setelah && mempersempit variabelnya di setiap cabang. Ini adalah cara standar untuk menangani union dari tipe primitif, dan pemeriksaan pertama yang dipakai untuk unknown.
typeof x !== "string" mempersempit ke arah sebaliknya, menjadi semua tipe kecuali string. Compiler juga tahu kedelapan string yang mungkin, jadi salah ketik dalam perbandingan menjadi error kompilasi, bukan pemeriksaan yang diam-diam selalu bernilai false:
Compiler mencetak index.ts(3,7): error TS2367: This comparison appears to be unintentional because the types '"bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined"' and '"strnig"' have no overlap. Di JavaScript biasa, salah ketik itu diam-diam akan mengirim setiap string ke jalur number. Perbaiki ejaannya dan kode itu mencetak text.
Jebakan typeof null
Karena typeof null adalah "object", TypeScript mempersempit typeof x === "object" menjadi object | null, bukan object. Tambahkan pemeriksaan null, kalau tidak, membaca property akan menjadi error (TS18047 'x' is possibly 'null'.).
typeof tidak bisa membedakan array, date, map, dan object biasa: semuanya "object". Pakai Array.isArray(x) untuk array dan x instanceof Date untuk instance class. Baik typeof maupun cara lain tidak bisa memeriksa apakah sebuah nilai cocok dengan interface saat runtime, karena interface dihapus saat kompilasi; untuk itu, tulis fungsi type guard yang memeriksa property-nya.
typeof di Posisi Tipe
Di dalam tipe, typeof someVariable menyalin tipe yang disimpulkan TypeScript (atau yang kamu deklarasikan) untuk variabel itu. Cara ini menghemat penulisan tipe dengan tangan untuk nilai yang sudah ada, dan menjaga keduanya tetap sinkron.
const theme = {
primary: "#3178c6",
spacing: 8,
dark: false,
};
type Theme = typeof theme;
// { primary: string; spacing: number; dark: boolean }
function withSpacing(t: Theme, factor: number): Theme {
return { ...t, spacing: t.spacing * factor };
}
console.log(withSpacing(theme, 2)); // { primary: '#3178c6', spacing: 16, dark: false }
let userName = "Ada";
const fixed = "Ada";
type A = typeof userName; // string
type B = typeof fixed; // "Ada" (a const keeps its literal type)
Hasilnya tergantung pada cara variabel dideklarasikan: let atau property object dilebarkan (string, number), sedangkan primitif const mempertahankan tipe literalnya. Tambahkan as const pada object atau array, dan typeof menghasilkan tipe literal readonly sampai ke tingkat terdalam. typeof juga menerima akses property, typeof theme.spacing adalah number, tetapi tidak menerima sembarang ekspresi: type T = typeof getTheme(); adalah syntax error (';' expected., TS1005). Untuk tipe yang dikembalikan sebuah pemanggilan, pakai ReturnType<typeof getTheme>.
keyof typeof: Key Object sebagai Tipe
keyof typeof obj adalah kombinasi yang paling umum. typeof mengubah object menjadi tipe, keyof mengambil key-nya, dan kamu mendapat union dari nama-nama property yang tetap benar ketika object itu berubah.
Trik yang sama pada array konstan menghasilkan union dari nilai-nilainya: dengan const roles = ["admin", "editor"] as const, tipe (typeof roles)[number] adalah "admin" | "editor". Tanpa as const, tipenya hanya string. Halaman keyof membahas keyof secara terpisah.
ReturnType<typeof fn> dan typeof pada Class
Utility type seperti ReturnType dan Parameters menerima tipe fungsi. Nama fungsi adalah sebuah nilai, jadi lewatkan dulu melalui typeof:
Menulis ReturnType<createUser> gagal dengan 'createUser' refers to a value, but is being used as a type here. Did you mean 'typeof createUser'? (TS2749), dan itu adalah compiler yang meminta persis bentuk ini.
Class adalah satu-satunya kasus di mana sebuah nama sekaligus merupakan nilai dan tipe. User sebagai tipe berarti instance dari class itu; typeof User berarti class-nya sendiri, yaitu constructor, termasuk member static-nya:
class Point {
static origin = new Point(0, 0);
constructor(public x: number, public y: number) {}
}
const p: Point = new Point(1, 2); // an instance
const Ctor: typeof Point = Point; // the class (constructor + statics)
const q = new Ctor(3, 4); // q: Point
type Instance = InstanceType<typeof Point>; // Point
typeof vs instanceof vs Type Guard
| Pemeriksaan | Bekerja pada | Contoh |
|---|---|---|
typeof | tipe primitif, fungsi, "apakah ini object" | typeof x === "number" |
Array.isArray | array | Array.isArray(x) |
instanceof | instance class (Date, Error, class milikmu) | x instanceof Date |
in | union object, berdasarkan property | "email" in x |
| Fungsi type guard | interface, type alias, apa pun yang lain | isUser(x) dengan x is User |
Pilih alat paling sempit yang cocok. Untuk instance class, lihat instanceof; untuk bentuk yang dijelaskan oleh interface, fungsi type guard adalah satu-satunya pemeriksaan runtime yang tersedia.
Pertanyaan yang sering diajukan
Apa fungsi typeof di TypeScript?
Ada dua hal yang berbeda, tergantung di mana ia muncul. Di dalam ekspresi, typeof x adalah operator JavaScript: ia berjalan dan mengembalikan string seperti "string" atau "object", dan TypeScript mempersempit x ketika kamu membandingkan string itu. Di dalam anotasi tipe, typeof x adalah type query: ia dievaluasi oleh compiler, menghasilkan tipe statis dari variabel x, dan hilang dari JavaScript yang dihasilkan.
Apa itu keyof typeof di TypeScript?
keyof typeof obj menghasilkan union dari nama-nama property sebuah object sebagai tipe string literal. typeof obj mengubah nilai object menjadi tipenya, lalu keyof mengambil key dari tipe itu. Untuk const colors = { red: "#f00", blue: "#00f" }, keyof typeof colors adalah "red" | "blue".
Kenapa typeof null mengembalikan "object"?
Ini adalah bug dari versi pertama JavaScript yang tidak bisa lagi diperbaiki tanpa merusak web. TypeScript mengikutinya: setelah typeof x === "object", x dipersempit menjadi object | null, jadi kamu juga harus memeriksa x !== null sebelum membaca property.
Bagaimana cara mengecek apakah sebuah nilai adalah array di TypeScript?
Pakai Array.isArray(value), yang mempersempit ke tipe array. typeof tidak bisa melakukannya: typeof [] adalah "object", sama seperti untuk object biasa dan null.
Apa perbedaan typeof dan instanceof di TypeScript?
typeof memeriksa kategori primitif sebuah nilai ("string", "number", "function", "object"...). instanceof memeriksa apakah sebuah object dibuat oleh class atau constructor tertentu, seperti Date atau class User milikmu sendiri. Pakai typeof untuk tipe primitif dan instanceof untuk instance class; keduanya tidak bisa memeriksa interface atau type alias, yang tidak ada saat runtime.