Ketik inferensi dengan TypeScript menggunakan konstruk sebagai const dan kata kunci inferior

TypeScript memungkinkan Anda untuk mengotomatisasi banyak tugas yang, tanpa menggunakan bahasa ini, pengembang harus menyelesaikannya secara mandiri. Tetapi ketika bekerja dengan TypeScript, tidak perlu selalu menggunakan anotasi jenis. Faktanya adalah bahwa kompiler melakukan pekerjaan yang baik dari jenis inferensi berdasarkan konteks eksekusi kode. Artikel, terjemahan yang kami terbitkan hari ini, dikhususkan untuk kasus-kasus inferensi jenis yang agak rumit di mana kata kunci inferdan konstruksi digunakan as const.



Ketik Dasar-dasar Inferensi


Pertama, lihat contoh inferensi tipe paling sederhana.

let variable;

Variabel yang dideklarasikan dengan cara ini adalah tipe any. Kami tidak memberikan kompiler petunjuk apa pun tentang bagaimana kami akan menggunakannya.

let variable = 'Hello!';

Di sini kami mendeklarasikan variabel dan segera menulis nilai ke dalamnya. TypeScript sekarang dapat menebak bahwa variabel ini bertipe string, jadi sekarang kita memiliki variabel yang diketik yang dapat diterima.

Pendekatan serupa berlaku untuk fungsi:

function getRandomInteger(max: number) {
  return Math.floor(Math.random() * max);
}

Dalam kode ini, kami tidak menunjukkan bahwa fungsi getRandomIntegermengembalikan angka. Tetapi kompiler TypeScript tahu ini dengan sangat baik.

Ketik inferensi dalam obat generik


Konsep di atas terkait dengan tipe universal (generik). Jika Anda ingin tahu lebih banyak tentang obat generik, lihat ini dan bahan ini .

Saat membuat tipe generik, Anda dapat melakukan banyak hal berguna. Ketik inferensi membuat bekerja dengan tipe universal lebih mudah dan menyederhanakannya.

function getProperty<ObjectType, KeyType extends keyof ObjectType>(
  object: ObjectType, key: KeyType
) {
  return object[key];
}

Saat menggunakan fungsi generik di atas, kita tidak perlu menentukan jenisnya secara eksplisit.

const dog = {
  name: 'Fluffy'
};
getProperty(dog, 'name');

Teknik ini, antara lain, sangat berguna dalam menciptakan komponen React universal. Berikut ini materi tentangnya.

Menggunakan kata kunci yang disimpulkan


Salah satu fitur TypeScript paling maju yang muncul dalam pikiran ketika berbicara tentang inferensi jenis adalah kata kunci infer.

Pertimbangkan sebuah contoh. Buat fungsi berikut:

function call<ReturnType>(
  functionToCall: (...args: any[]) => ReturnType, ...args: any[]
): ReturnType {
  return functionToCall(...args);
}

Panggil, dengan bantuan fungsi ini, fungsi lain, dan tulis apa yang dikembalikan ke konstanta:

const randomNumber = call(getRandomInteger, 100);

Ekspresi sebelumnya memungkinkan kita untuk mendapatkan fungsi apa getRandomIntegeryang menerima input dikembalikan sebagai batas atas dari integer acak yang dikembalikan padanya, 100. Benar, ada satu masalah kecil. Itu terletak pada kenyataan bahwa tidak ada yang mencegah kita untuk mengabaikan jenis argumen fungsi getRandomInteger.

const randomNumber = call(getRandomInteger, '100'); //   

Karena TypeScript mendukung parameter spread dan rest dalam fungsi urutan yang lebih tinggi, kita dapat menyelesaikan masalah ini seperti ini:

function call<ArgumentsType extends any[], ReturnType>(
  functionToCall: (...args: ArgumentsType) => ReturnType, ...args: ArgumentsType
): ReturnType {
  return functionToCall(...args);
}

Sekarang kami telah menunjukkan bahwa fungsi tersebut calldapat memproses berbagai argumen dalam bentuk apa pun, dan juga bahwa argumen tersebut harus sesuai dengan harapan fungsi yang diteruskan kepadanya.

Sekarang mari kita coba lagi untuk membuat panggilan fungsi yang salah:

const randomNumber = call(getRandomInteger, '100');

Ini menghasilkan pesan kesalahan:

Argument of type β€˜β€100β€³β€˜ is not assignable to parameter of type β€˜number’.

Sebenarnya, mengikuti langkah-langkah di atas, kami cukup membuat tuple. Tuple dalam TypeScript adalah array dengan panjang tetap yang nilainya diketahui tetapi tidak harus sama.

type Option = [string, boolean];
const option: Option = ['lowercase', true];

Fitur Kata Kunci disimpulkan


Sekarang mari kita bayangkan bahwa tujuan kita bukan untuk mendapatkan apa yang dikembalikan fungsi, tetapi hanya untuk mendapatkan informasi tentang jenis data yang dikembalikan padanya.

type FunctionReturnType<FunctionType extends (...args: any) => ?> = ?;

Jenis di atas belum siap digunakan. Kita perlu menyelesaikan masalah bagaimana menentukan nilai kembali. Di sini Anda dapat menggambarkan semuanya secara manual, tetapi ini bertentangan dengan tujuan kami.

type FunctionReturnType<ReturnType, FunctionType extends (...args: any) => ReturnType> = ReturnType;
FunctionReturnType<number, typeof getRandomInteger>;

Alih-alih melakukan ini sendiri, kita dapat meminta TypeScript untuk menampilkan jenis kembali. Kata kunci inferhanya dapat digunakan dalam tipe bersyarat. Itulah sebabnya kode kita terkadang tidak rapi.

type FunctionReturnType<FunctionType extends (args: any) => any> = FunctionType extends (...args: any) => infer ReturnType ? ReturnType : any;

Inilah yang terjadi dalam kode ini:

  • Dikatakan FunctionTypeberkembang di sini (args: any) => any.
  • Kami menunjukkan bahwa FunctionReturnTypeini adalah tipe bersyarat.
  • Kami memeriksa apakah itu mengembang FunctionType (...args: any) => infer ReturnType.

Setelah melakukan semua ini, kita dapat mengekstraksi tipe kembali dari fungsi apa pun.

FunctionReturnType<typeof getRandomInteger>; // number

Di atas adalah tugas yang umum sehingga TypeScript memiliki utilitas ReturnType bawaan , yang dirancang untuk menyelesaikan masalah ini.

Bangun sebagai const


Masalah lain yang terkait dengan jenis inferensi adalah perbedaan antara kata kunci constdan letyang digunakan ketika mendeklarasikan konstanta dan variabel.

let fruit = 'Banana';
const carrot = 'Carrot';

Variabel fruit- memiliki tipe string. Ini berarti bahwa nilai string apa pun dapat disimpan di dalamnya.

Dan konstanta carrotadalah string literal. Ini dapat dianggap sebagai contoh subtipe string. Deskripsi literal string berikut diberikan dalam PR ini : "Tipe literal string adalah tipe yang nilainya adalah string dengan konten teks yang setara dengan konten string literal yang sama."

Perilaku ini dapat diubah. TypeScript 3.4 memperkenalkan fitur baru yang menarik yang disebut const assertions yang menyediakan penggunaan konstruk as const. Berikut tampilannya seperti apa:

let fruit = 'Banana' as const;

Sekarang fruitini adalah string literal. Desainnya as constjuga nyaman ketika beberapa entitas perlu dibuat kekal. Pertimbangkan objek berikut:

const user = {
  name: 'John',
  role: 'admin'
};

Dalam JavaScript, kata kunci constberarti Anda tidak dapat menimpa apa yang disimpan dalam konstanta user. Tetapi, di sisi lain, Anda dapat mengubah struktur internal suatu objek yang direkam dalam konstanta ini.

Sekarang objek menyimpan tipe berikut:

const user: {
  name: string,
  role: string
};

Agar sistem menganggap objek ini tidak dapat diubah, Anda dapat menggunakan desain as const:

const user = {
  name: 'John',
  role: 'admin'
} as const;

Sekarang tipe telah berubah. String menjadi string literal, bukan string biasa. Tapi bukan hanya itu saja yang berubah. Sekarang propertinya hanya baca:

const user: {
  readonly name: 'John',
  readonly role: 'admin'
};

Dan ketika bekerja dengan array, kemungkinan yang lebih kuat terbuka di hadapan kita:

const list = ['one', 'two', 3, 4];

Jenis array ini adalah (string | number)[]. Menggunakan array ini, as constAnda bisa mengubahnya menjadi tuple:

const list = ['one', 'two', 3, 4] as const;

Sekarang tipe array ini terlihat seperti ini:

readonly ['one', 'two', 3, 4]

Semua ini berlaku untuk struktur yang lebih kompleks. Perhatikan contoh yang diberikan Anders Halesberg dalam pidatonya di TSConf 2019 :

const colors = [
  { color: 'red', code: { rgb: [255, 0, 0], hex: '#FF0000' } },
  { color: 'green', code: { rgb: [0, 255, 0], hex: '#00FF00' } },
  { color: 'blue', code: { rgb: [0, 0, 255], hex: '#0000FF' } },
] as const;

Array kami colorssekarang dilindungi dari perubahan, dan elemen-elemennya juga dilindungi dari perubahan:

const colors: readonly [
    {
        readonly color: 'red';
        readonly code: {
            readonly rgb: readonly [255, 0, 0];
            readonly hex: '#FF0000';
        };
    },
    /// ...
]

Ringkasan


Pada artikel ini, kami melihat beberapa contoh penggunaan mekanisme inferensi tipe tingkat lanjut dalam TypeScript. Kata kunci inferdan mekanisme digunakan di sini as const. Alat-alat ini dapat sangat berguna dalam beberapa situasi yang sulit. Misalnya, ketika Anda perlu bekerja dengan entitas yang tidak dapat diubah, atau ketika menulis program dengan gaya fungsional. Jika Anda ingin melanjutkan pengenalan topik ini - lihat materi ini .

Pembaca yang budiman! Apakah Anda menggunakan kata kunci inferdan konstruksi as constdi TypeScript?


All Articles