Row Polymorphism di Typescript
Intro
Pada suatu hari ada sebuah object koordinat 3 dimensi yang memiliki attribute X, Y, dan Z
dan object kooridinat tersebut dapat di-flip secara horizontal seolah-olah memberikan efek bercermin. Dandan sekalian kalo bisa bro.
Function flipX
tersebut menerima object bertipe Coordinate3d
yang memiliki attribute x, y, dan z. Artinya compiler akan complain ketika data seperti Coordinate2d
dimasukkan ke dalam function tersebut.
Sedangkan kalau dipikir baik-baik, fungsi tersebut hanya melakukan perubahan pada row x saja. Kalau yang dibutuhkan hanyalah x, function flipX
harusnya bisa dibuat lebih “minimal” dengan mengubahnya menjadi
Dengan begini, flipX
sekarang hanya memiliki satu attribute sebagai constraint, yaitu x yang bertipe number. Seolah-olah ia menyeru, “Barangsiapa memiliki row x bertipe number, niscaya aku akan dapat melakukan manipulasi data terhadapnya. Sesungguhnya, aku adalah row polymorphism”.
🎉🎉🎉
NOTE: Perlu diperhatikan bahwa penggunaan keyword extends
di sini sangat penting untuk mengindikasikan bahwa type parameter A
bisa jadi memiliki row lain selain x. Jika kita tuliskan code di atas seperti ini
Maka flipX
tidak lagi sepenuhnya row-polymorphic. Akan terlihat ketika sebuah subset dari X dimasukkan
Compiler sekarang kehilangan informasi row y (dan row z!) karena notasi dari fungsi flipX
yang hanya menyatakan X sebagai return type-nya.
Inilah dasar dari definisi Row Polymorphism, yang memungkinkan kita untuk membuat program yang polymorphic terhadap rows tanpa kehilangan informasi row sama sekali.
Tapi jangan salah, kalau mau dibuat lebih constrained juga boleh kok. Misal kita ingin menulis sebuah function yang hanya menerima row x
saja, tidak lebih tidak kurang.
Plis jangan muntah liat syntax-nya ya!
Adding
Row Polymorphism nggak sebatas bisa baca row aja, tapi harusnya juga bisa melakukan manipulasi terhadap rows: seperti penambahan, pengurangan, dan penamaan ulang.
Anggap kita punya function yang polymorphic dengan row firstName
bertipe string, yang menambahkan row lastName
(jika belum ada) sebagai return value-nya.
Bagaimana informasi penambahan row ini dapat ditangkap oleh compiler?
Kebetulan Typescript memiliki operasi intersection yang dinotasikan menggunakan symbol &
sehingga operasi penambahan row ini relatif terlihat mudah.
Deleting
Sekarang kita balik: kita ingin membuat sebuah function yang polymorphic terhadap row firstName
dan lastName
(keduanya bertipe string), dan ingin menghilangkan lastName
dari object tersebut.
Typescript sudah menyediakan utility type untuk operasi ini dengan menggunakan Omit.
Renaming
Menurut saya renaming ini adalah operasi gabungan dari adding
dan deleting
. Anggap kita ingin mengubah label (key) row y menjadi z. Operasi ini dapat dilakukan dengan dua cara yang identik:
- Tambah row
z
kemudian hapus rowy
, atau - Hapus row
y
dulu baru tambah rowz
Kesimpulan
Sebuah fungsi yang row-polymorphic adalah fungsi yang reusable, dapat digunakan oleh berbagai macam record selama memenuhi constraint-nya. Row Polymorphism di Typescript lumrahnya ditandai dengan keyword extends
agar compiler tidak kehilangan informasi tipe rows yang sedang dimanipulasi. Terjaganya informasi ini sangat dibutuhkan ketika kita ingin mengembalikan object tersebut (return type).
Typescript sendiri sudah menyediakan sekumpulan utlity types (seperti Record, Omit, dsb) yang bisa digunakan untuk mendukung Row Polymorphism dengan cukup mudah. Yah, menurut saya sih lebih mudah dibanding RP nya Purescript yang… sudahlah 😄