Ketika saya pertama kali melihat kata tidak pernah, saya berpikir betapa tidak berguna tipe muncul di TypeScript. Seiring waktu, tenggelam lebih dalam ke dalam, aku mulai mengerti betapa kuatnya kata ini. Dan kekuatan ini lahir dari kasing dunia nyata yang ingin saya bagikan dengan pembaca. Siapa peduli, selamat datang ke kucing.
Apa yang tidak pernah
Jika kita terjun ke dalam sejarah, kita akan melihat bahwa tipe itu tidak pernah muncul pada awal TypeScript versi 2.0, dengan
deskripsi yang agak
sederhana tentang tujuannya. Jika Anda secara singkat dan bebas menceritakan kembali versi pengembang ts, maka tipe tersebut tidak pernah merupakan tipe primitif yang mewakili tanda untuk nilai yang tidak akan pernah terjadi. Atau, tanda untuk fungsi yang tidak akan pernah mengembalikan nilai, baik karena loop-nya, misalnya, loop tak terbatas, atau karena gangguannya. Dan untuk menunjukkan dengan jelas esensi dari apa yang dikatakan, saya mengusulkan untuk melihat contoh di bawah ini:
function error(message: string): never { throw new Error(message); } function infiniteLoop(): never { while (true) { } } function infiniteRec(): never { return infiniteRec(); }
Mungkin karena contoh-contoh seperti itu, saya mendapat kesan pertama bahwa jenis ini diperlukan untuk kejelasan.
Jenis sistem
Sekarang saya bisa mengatakan bahwa fauna sistem tipe kaya di TypeScript juga tidak pernah jatuh tempo. Dan untuk mendukung kata-kata saya, saya akan memberikan beberapa jenis perpustakaan dari lib.es5.d.ts
type Exclude<T, U> = T extends U ? never : T; type Extract<T, U> = T extends U ? T : never; type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>; type NonNullable<T> = T extends null | undefined ? never : T; type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
Dari tipe saya dengan tidak pernah, saya akan memberikan favorit saya - GetNames, analog yang ditingkatkan dari keyof:
type GetNames<FromType, KeepType = any, Include = true> = { [K in keyof FromType]: FromType[K] extends KeepType ? Include extends true ? K : never : Include extends true ? never : K }[keyof FromType];
Memantau perubahan di masa depan
Ketika kehidupan proyek tidak cepat berlalu, pengembangannya mengambil karakter khusus. Saya ingin memiliki kendali atas poin-poin penting dalam kode, untuk memiliki jaminan bahwa Anda atau anggota tim Anda tidak akan lupa untuk memperbaiki bagian penting dari kode ketika dibutuhkan waktu. Bagian kode semacam itu sangat nyata ketika ada keadaan atau penghitungan sesuatu. Misalnya, daftar serangkaian tindakan pada suatu entitas. Saya sarankan melihat contoh untuk memahami konteks apa yang terjadi.
Kode di atas disederhanakan sebanyak mungkin hanya untuk fokus pada poin penting - jenis AdminAction didefinisikan dalam proyek lain dan bahkan mungkin tidak disertai oleh tim Anda. Karena proyek akan hidup untuk waktu yang lama, maka perlu untuk melindungi ActionEngine Anda dari perubahan dalam tipe AdminAction tanpa sepengetahuan Anda. TypeScript menawarkan beberapa resep untuk mengatasi masalah ini, salah satunya adalah dengan menggunakan tipe never. Untuk melakukan ini, kita perlu mendefinisikan NeverError dan menggunakannya dalam metode doAction.
class NeverError extends Error {
Sekarang tambahkan nilai "BLOCK" baru ke AdminAction dan dapatkan kesalahan pada waktu kompilasi: Argumen tipe '"BLOCK"' tidak dapat ditetapkan ke parameter tipe 'never'.ts (2345).
Pada prinsipnya, kami mencapai ini. Perlu disebutkan poin menarik bahwa konstruksi sakelar melindungi kita dari mengubah elemen AdminAction atau menghapusnya dari set. Dari latihan, saya bisa mengatakan itu benar-benar berfungsi seperti yang diharapkan.
Jika Anda tidak ingin memperkenalkan kelas NeverError, Anda dapat mengontrol kode dengan mendeklarasikan variabel tipe tidak pernah. Seperti ini:
type AdminAction = "CREATE" | "ACTIVATE" | "BLOCK"; class ActionEngine { doAction(action: AdminAction) { switch (action) { case "CREATE":
Batas Konteks: ini + tidak pernah
Trik berikut ini sering menyelamatkan saya dari kesalahan konyol di tengah kelelahan atau kecerobohan. Dalam contoh di bawah ini, saya tidak akan memberikan penilaian terhadap kualitas pendekatan yang dipilih. Bersama kami, de facto, ini terjadi. Misalkan Anda menggunakan metode di kelas yang tidak memiliki akses ke bidang kelas. Ya, kedengarannya menakutkan - semua ini adalah gov ... kode.
@SomeDecorator({...}) class SomeUiPanel { @Inject private someService: SomeService; public beforeAccessHook() {
Dalam kasus yang lebih luas, bisa berupa fungsi callback atau panah, yang memiliki konteks eksekusi sendiri. Dan tugasnya adalah: Bagaimana melindungi diri Anda dari kesalahan runtime? Untuk ini, TypeScript memiliki kemampuan untuk menentukan konteks
ini .
@SomeDecorator({...}) class SomeUiPanel { @Inject private someService: SomeService; public beforeAccessHook(this: never) {
Dalam keadilan, saya akan mengatakan bahwa itu tidak pernah pantas. Sebagai gantinya, Anda dapat menggunakan void dan {}. Tetapi ini adalah tipe yang tidak pernah menarik perhatian ketika Anda membaca kode.
Harapan
Invarian
Memiliki gagasan pasti tentang tidak pernah, saya pikir kode berikut ini akan berfungsi:
type Maybe<T> = T | void; function invariant<Cond extends boolean>(condition: Cond, message: string): Cond extends true ? void : never { if (condition) { return; } throw new Error(message); } function f(x: Maybe<number>, c: number) { if (c > 0) { invariant(typeof x === "number", "When c is positive, x should be number"); (x + 1);
Tapi sayang sekali. Ekspresi (x + 1) melempar kesalahan: Operator '+' tidak dapat diterapkan ke jenis 'Mungkin' dan '1'. Contohnya sendiri saya melihat dalam artikel
Mentransfer 30.000 baris kode dari Flow ke TypeScript.Ikatan fleksibel
Saya berpikir bahwa dengan bantuan tidak pernah saya dapat mengontrol parameter fungsi wajib dan, dalam kondisi tertentu, menonaktifkan yang tidak perlu. Tapi tidak, itu tidak akan berhasil:
function variants<Type extends number | string>(x: Type, c: Type extends number ? number : never): number { if (typeof x === "number") { return x + c; } return +x; } const three = variants(1, 2);
Masalah di atas diselesaikan dengan
cara yang berbeda .
Verifikasi lebih ketat
Saya ingin kompiler ts untuk tidak melewatkan sesuatu seperti itu bertentangan dengan akal sehat.
variants(<never> {}, <never> {});
Kesimpulan
Pada akhirnya, saya ingin menawarkan tugas kecil, dari serangkaian keanehan aneh. Baris mana yang salah?
class never<never> { never: never; } const whats = new never<string>(); whats.never = "";
OpsiDalam yang terakhir: Ketik '""' tidak dapat ditugaskan untuk mengetik 'tidak pernah' .ts (2322)
Itu saja yang saya ingin katakan tentang tidak pernah. Terima kasih atas perhatian Anda dan sampai jumpa lagi.