Menu

Function Overloading TypeScript: Overload Signature

Function overload di TypeScript membuat satu fungsi punya beberapa call signature, masing-masing dengan tipe kembaliannya sendiri. Pelajari pola overload signature ditambah implementasi, aturan yang diperiksa compiler, kapan parameter union lebih baik, dan overload di class.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

Function overloading di TypeScript berarti menulis beberapa call signature untuk satu fungsi, lalu diikuti satu implementasi. Setiap signature bisa memasangkan tipe parameter yang berbeda dengan tipe kembalian yang berbeda, dan pemanggil mendapat tipe yang tepat.

Tanpa overload, parse akan mengembalikan number | number[] di setiap pemanggilan, dan one + 1 akan menjadi error sampai kamu mempersempit hasilnya sendiri.

Overload Signature dan Implementasinya

Fungsi yang di-overload punya dua bagian:

  1. Overload signature: deklarasi tanpa badan, satu untuk setiap bentuk pemanggilan yang didukung. Hanya signature inilah yang bisa dipakai pemanggil.
  2. Implementation signature: deklarasi terakhir, yang punya badan. Parameternya harus menerima semua yang diterima overload, dan tipe kembaliannya harus mencakup tipe kembalian setiap overload. Signature ini tidak terlihat dari luar.

Tipe hanya ada saat kompilasi, jadi saat runtime hanya ada satu fungsi JavaScript. Implementasinya harus memeriksa argumennya (typeof, Array.isArray, arguments.length...) untuk memutuskan apa yang dilakukan. Compiler memeriksa bahwa overload dan implementasinya sesuai:

function format(value: string): string;
function format(value: number): number {
  return value;
}
// error TS2394: This overload signature is not compatible with its implementation signature.

Perbaikannya adalah memperlebar implementasinya: function format(value: string | number): string | number.

Implementation Signature Tidak Bisa Dipanggil

Inilah aturan yang paling sering mengejutkan. Sebuah pemanggilan harus cocok dengan salah satu overload signature secara mandiri; TypeScript tidak menggabungkannya.

Compiler mencetak:

index.ts(12,19): error TS2769: No overload matches this call.
  The last overload gave the following error.
    Argument of type 'string | string[]' is not assignable to parameter of type 'string[]'.
      Type 'string' is not assignable to type 'string[]'.

Implementasinya menerima string | string[], tetapi pemanggil tidak bisa melihatnya. Tambahkan overload ketiga yang menerima union dan mengembalikan union, maka pemanggilan itu bisa dikompilasi dan mencetak [ 1, 2 ]:

function parse(input: string): number;
function parse(input: string[]): number[];
function parse(input: string | string[]): number | number[];
function parse(input: string | string[]): number | number[] {
  return Array.isArray(input) ? input.map(Number) : Number(input);
}

Jumlah Parameter yang Berbeda

Overload juga bisa mendeskripsikan pemanggilan dengan jumlah argumen yang berbeda. Di sini, sebuah tanggal bisa dibuat dari timestamp atau dari tahun, bulan, dan hari, tetapi tidak dari dua angka:

Satu signature dengan dua parameter opsional akan menerima makeDate(2024, 3) dan diam-diam membuat tanggal yang salah. Overload mengubahnya menjadi error kompilasi (TS2575).

Urutan Itu Penting

TypeScript mencoba overload dari atas ke bawah dan memilih yang pertama cocok. Taruh signature yang paling spesifik lebih dulu. Overload yang luas di awal daftar akan menelan pemanggilan yang seharusnya untuk overload setelahnya:

function describe(value: unknown): string;   // matches everything
function describe(value: string): "text";    // never chosen
function describe(value: unknown): string {
  return typeof value === "string" ? "text" : "other";
}

const d = describe("hi"); // d: string, not "text"

Tukar dua signature pertama, dan describe("hi") akan bertipe "text".

Overload atau Parameter Union?

Overload sepadan dengan baris tambahannya ketika tipe kembalian bergantung pada tipe argumen. Kalau tidak, satu signature dengan parameter union lebih pendek, lebih mudah dibaca, dan menerima argumen union yang akan ditolak oleh overload.

PakaiKapan
Parameter unionTipe kembalian sama untuk setiap input
Parameter opsionalBentuk pemanggilan hanya berbeda pada argumen di bagian akhir yang bebas dihilangkan
OverloadTipe kembalian berubah sesuai argumen, atau ada kombinasi argumen yang harus ditolak
GenericTipe kembalian dibangun dari tipe argumen, seperti identity<T>(x: T): T

Generic dengan conditional type bisa mengekspresikan sebagian kumpulan overload sebagai satu signature, tetapi untuk dua atau tiga kasus, overload biasanya lebih mudah dibaca.

Method dan Constructor yang Di-overload

Method memakai pola yang sama di dalam class: overload signature, lalu method dengan badannya. Constructor juga bisa di-overload dengan cara yang sama.

Interface dan object type juga bisa mendeklarasikan overload, sebagai beberapa call signature atau beberapa method signature dengan nama yang sama. Banyak fungsi bawaan dideklarasikan dengan cara ini: arahkan kursor ke reduce pada sebuah array di editor dan akan muncul "+2 overloads".

Pertanyaan yang sering diajukan

Apakah TypeScript mendukung function overloading?

Ya, di level tipe. Kamu menulis beberapa overload signature (deklarasi tanpa badan) lalu diikuti satu implementasi. Pemanggil hanya melihat overload signature-nya. Saat runtime tetap hanya ada satu fungsi JavaScript, jadi implementasinya memeriksa argumen sendiri dan menangani setiap kasus.

Apa arti "No overload matches this call"?

Error TS2769: argumennya tidak cocok dengan overload signature mana pun. Implementation signature tidak dihitung, jadi pemanggilan dengan argumen union seperti string | string[] gagal meskipun implementasinya menerimanya. Tambahkan overload yang menerima union itu, atau ganti overload-nya dengan satu signature.

Kapan sebaiknya aku memakai overload daripada union type?

Pakai overload ketika tipe kembalian bergantung pada tipe argumen yang dikirim, misalnya input string menghasilkan number, tetapi input string[] menghasilkan number[]. Kalau tipe kembaliannya sama untuk setiap input, satu signature dengan parameter union lebih sederhana dan juga menerima argumen union.

Apakah arrow function bisa di-overload di TypeScript?

Tidak dengan sintaks deklarasi overload, yang hanya berlaku untuk deklarasi function dan method. Kamu bisa memberi variabel tipe overload dengan beberapa call signature, type Parse = { (s: string): number; (s: string[]): number[] }, tetapi memberikan arrow function ke variabel itu biasanya butuh type assertion, jadi deklarasi function adalah pilihan yang lebih rapi.

Kenapa overload signature-ku tidak kompatibel dengan implementation signature-nya?

Error TS2394 berarti salah satu overload menerima atau mengembalikan sesuatu yang tidak ditangani implementasinya. Parameter implementasi harus menerima parameter setiap overload, dan tipe kembaliannya harus kompatibel dengan tipe kembalian setiap overload. Memperlebar implementasinya (sering kali menjadi union) akan memperbaikinya.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai