Menu

typeof di TypeScript: Type Guard dan Operator Tipe typeof

typeof punya dua tugas di TypeScript. Di dalam kode, ia adalah operator JavaScript yang mengembalikan "string", "number", "object" dan seterusnya saat runtime, dan TypeScript mempersempit tipe berdasarkan hasilnya. Di dalam tipe, ia adalah type query yang menyalin tipe sebuah variabel, seperti pada keyof typeof obj dan ReturnType<typeof fn>.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

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 munculnyaEkspresi apa pun: if, return, console.log(...)Posisi tipe: setelah :, di type X = ..., di dalam <...>
Contohtypeof x === "string"let y: typeof x;
DievaluasiSaat kode berjalanOleh compiler, lalu dihapus
HasilSalah satu dari delapan stringSebuah tipe
Dipakai untukMemeriksa nilai dan mempersempit tipenyaMenurunkan 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:

NilaiHasil typeofCatatan
"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

PemeriksaanBekerja padaContoh
typeoftipe primitif, fungsi, "apakah ini object"typeof x === "number"
Array.isArrayarrayArray.isArray(x)
instanceofinstance class (Date, Error, class milikmu)x instanceof Date
inunion object, berdasarkan property"email" in x
Fungsi type guardinterface, type alias, apa pun yang lainisUser(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.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai