Phantom Type di Typescript

Jihad Dzikri Waspada
codewey
Published in
3 min readSep 16, 2018

--

Phantom Type adalah istilah untuk sebuah type yang salah satu type parameter-nya tidak muncul di sebalah kanan definisinya (gak tau gimana jelasin ke Bahasa Indonesia yang enak 😞). Pake contoh aja ya biar lebih nangkep:

type Either<L, R> = Left<L> | Right<R>

Type di atas adalah salah satu contoh Phantom Type karena Left hanya menggunakan generics L dan Right hanya menggunakan generics R: L dan R tidak keluar secara berbarengan.

type Tuple<A, B> = [A, B]

Type ini bukan merupakan Phantom Type karena A dan B digunakan sepenuhnya.

Yak, terus?

“Terus, terus, terus.” — Kang Parkir

Temen-temen pasti pernah main sama measurement unit kayak Kilometer, Celcius, atau bahkan mata uang. 100 derajat Celcius !== 100 derajat Fahrenheit. Tapi kadang kita justru agak sloppy dalam perhitungan “relatif” seperti ini.

const isBoiling = (degree: number) => degree >= 100;const celcius100 = 100;
const fahrenheit100 = 100;
isBoiling(celcius100); // true
isBoiling(fahrenheit100); // true, tapi kenyataannya salah, karena air mendidih di 212F, 100C === 212F

Ada berbagai macam solusi. Misal, kita ubah nama variabel degree menjadi degreeInCelcius, atau kasih DocBlock di atas nama fungsinya yang menjelaskan bahwa fungsi ini hanya menerima satuan Celcius. Tapi rasanya akan lebih asik kalau kita bisa memanfaatkan compiler untuk menangani masalah ini. Lho emang bisa? Insyallah 😎

Proposed Solution

Sekilas, type parameter A nggak berguna, karena gak dipake dimana-mana :( Tapi dari deklarasi fungsi isBoiling, argument temp sudah sangat jelas menyatakan bahwa ia hanya menerima Celcius, bukan yang lain.

Namun sayangnya, bila dijalankan, Typescript belum bisa membedakan Temp<Celcius> dan Temp<Fahrenheit> adalah sesuatu yang berbeda 😞

const c100 = new Temp<Celcius>(100);
const f100 = new Temp<Fahrenheit>(100);
isBoiling(c100); // type-check
isBoiling(f100); // type-check

Ini dikarenakan nature Typescript yang meng-infer suatu type dari strukturnya, bukan dari nama-nya (bisa dibaca disini: https://basarat.gitbooks.io/typescript/docs/types/type-compatibility.html#structural).

Jadi, kita harus revisi!

Ketika kita supply f100 ke fungsi isBoiling, error-nya jelas:

Argument of type 'Temp<Fahrenheit>' is not assignable to parameter of type 'Temp<Celcius>'.
Type 'Fahrenheit' is not assignable to type 'Celcius'.
Property '__celcius__' is missing in type 'Fahrenheit'.

MASHHUKK PAK EKKOO!

Sebagai alternatif, kita juga bisa menggunakan fitur Enumeration (https://www.typescriptlang.org/docs/handbook/enums.html) untuk membuat “type” yang unique:

pesan errornya juga gak kalah jelas:

Argument of type 'Temp<Unit.Fahrenheit>' is not assignable to parameter of type 'Temp<Unit.Celcius>'.
Type 'Unit.Fahrenheit' is not assignable to type 'Unit.Celcius'.

Jadi terserah mau pakai yang mana :))

Contoh 2

Salah satu contoh yang paling terkenal dari Phantom Type adalah validasi data.

Harusnya sih sudah sangat jelas: data hanya bisa dikirim ke Backend setelah divalidasi terlebih dahulu. Types make everything even more clear 😙

Kesimpulan

Teknik ini sangat berguna ketika kita ingin membedakan suatu data dengan data lainnya, namun keduanya memiliki tipe yang sama.

const date: string = '2018-09-17';
const dateIsoXXX: string = '17 Sept 2018 UTC+2' // whatever

bisa disubstitusi dengan

interface MyDate extends String { __my_date__: never }
interface MyDateIsoXXX extends String { __xxx__: never }
const date: MyDate = '2018-09-17';
const dateIsoXXX: MyDateIsoXXX = '17 Sept 2018 UTC+2';

dan sebagainya. Zero overhead ketika run time karena interface di Typescript akan dihilangkan dari code. Semoga bermanfaat 🙂

Referensi

  1. https://medium.com/@gcanti/phantom-types-with-flow-828aff73232b
  2. https://medium.com/@dhruvrajvanshi/advanced-typescript-patterns-6cf8826c7944
  3. https://robhoward.id.au/blog/2017/02/effective-types-phantom-types-with-flow/

--

--

Jihad Dzikri Waspada
codewey

Software Developer @Chordify, Utrecht. NOTE: Please navigate to https://jihadwaspada.com. I no longer write on Medium