
Baru-baru ini, mempelajari penyebab tidak berfungsinya proyek rumah saya, saya sekali lagi melihat kesalahan yang sering diulang karena kelelahan. Inti dari kesalahan adalah bahwa, memiliki beberapa pengidentifikasi dalam satu blok kode, ketika saya memanggil suatu fungsi, saya melewati pengidentifikasi objek dari tipe lain. Pada artikel ini saya akan berbicara tentang cara mengatasi masalah ini menggunakan TypeScript.
Sedikit teori
TypeScript didasarkan pada pengetikan struktural, yang sangat cocok dengan ideologi bebek JavaScript. Sejumlah artikel telah ditulis tentang hal ini. Saya tidak akan mengulanginya, saya hanya akan menguraikan perbedaan utama dari pengetikan nominatif, yang lebih umum di bahasa lain. Mari kita lihat contoh kecil.
class Car { id: number; numberOfWheels: number; move (x: number, y: number) {
Mengapa TypeScript berperilaku seperti ini? Ini hanya manifestasi dari pengetikan struktural. Tidak seperti nominatif, yang memantau nama jenis, pengetikan struktural membuat keputusan tentang kompatibilitas jenis berdasarkan kontennya. Kelas Mobil berisi semua properti dan metode kelas Perahu, sehingga Mobil dapat digunakan sebagai Perahu. Kebalikannya tidak benar karena Boat tidak memiliki properti numberOfWheels.
Pengidentifikasi pengetikan
Pertama-tama, kami akan menetapkan tipe untuk pengidentifikasi
type CarId: number; type BoatId: number;
dan menulis ulang kelas menggunakan tipe ini.
class Car { id: CarId; numberOfWheels: number; move (x: number, y: number) {
Anda akan melihat bahwa situasinya tidak banyak berubah, karena kami masih tidak memiliki kendali atas dari mana kami mendapatkan pengenal, dan Anda akan benar. Tetapi contoh ini sudah memberikan beberapa keuntungan.
Selama pengembangan program, tipe pengenal tiba-tiba dapat berubah. Jadi, misalnya, nomor mobil tertentu, unik untuk proyek ini, dapat diganti dengan nomor string VIN. Tanpa menentukan jenis pengenal, Anda harus mengganti angka dengan string di semua tempat itu terjadi. Dengan tugas tipe, perubahan hanya perlu dilakukan di satu tempat di mana tipe itu sendiri ditentukan.
Saat memanggil fungsi, kami mendapatkan petunjuk dari editor kode kami, pengidentifikasi tipe apa yang seharusnya. Misalkan kita memiliki fungsi-fungsi berikut yang dideklarasikan:
function getCarById(id: CarId): Car {
Kemudian kita akan mendapatkan petunjuk dari editor bahwa kita harus mengirimkan tidak hanya nomor, tetapi CarId atau BoatId.
Meniru pengetikan yang paling ketat
Tidak ada pengetikan nominal pada TypeScript, tetapi kita dapat meniru perilakunya, membuat jenis apa pun menjadi unik. Untuk melakukan ini, tambahkan properti unik ke jenisnya. Trik ini dirujuk dalam artikel berbahasa Inggris di bawah istilah Branding, dan inilah tampilannya:
type BoatId = number & { _type: 'BoatId'}; type CarId = number & { _type: 'CarId'};
Setelah menunjukkan bahwa tipe kami harus berupa angka dan objek dengan properti dengan nilai unik, kami membuat tipe kami tidak kompatibel dalam memahami pengetikan struktural. Mari kita lihat cara kerjanya.
let carId: CarId; let boatId: BoatId; let car: Car; let boat: Boat; car = getCarById(carId);
Semuanya terlihat bagus kecuali untuk empat baris terakhir. Untuk membuat pengidentifikasi, Anda memerlukan fungsi pembantu:
function makeCarIdFromVin(id: number): CarId { return vin as any; }
Kerugian dari metode ini adalah bahwa fungsi ini akan tetap di runtime.
Membuat pengetikan yang kuat sedikit kurang ketat
Dalam contoh terakhir, saya harus menggunakan fungsi tambahan untuk membuat pengenal. Anda dapat menyingkirkannya menggunakan definisi antarmuka Flavour:
interface Flavoring<FlavorT> { _type?: FlavorT; } export type Flavor<T, FlavorT> = T & Flavoring<FlavorT>;
Sekarang Anda dapat menetapkan tipe untuk pengidentifikasi sebagai berikut:
type CarId = Flavor<number, βCarIdβ> type BoatId = Flavor<number, βBoatIdβ>
Karena properti _type adalah opsional, Anda dapat menggunakan konversi implisit:
let boatId: BoatId = 5; // OK let carId: CarId = 3; // OK
Dan kami masih tidak dapat mencampur pengidentifikasi:
let carId: CarId = boatId; // ERROR
Opsi mana yang harus dipilih
Kedua opsi memiliki hak untuk hidup. Branding memiliki keuntungan melindungi variabel dari penugasan langsung. Ini berguna jika variabel menyimpan string dalam beberapa format, seperti path file absolut, tanggal atau alamat IP. Fungsi helper yang berhubungan dengan konversi tipe dalam kasus ini juga dapat memeriksa dan memproses input data. Dalam kasus lain, Flavor lebih nyaman digunakan.
Sumber
- Titik awal stackoverflow.com
- Penafsiran artikel gratis