Apakah ada cara untuk melakukan overloading metode di TypeScript?


109

Apakah ada cara untuk melakukan overloading metode dalam bahasa TypeScript?

Saya ingin mencapai sesuatu seperti ini:

class TestClass {
    someMethod(stringParameter: string): void {
        alert("Variant #1: stringParameter = " + stringParameter);
    }

    someMethod(numberParameter: number, stringParameter: string): void {
        alert("Variant #2: numberParameter = " + numberParameter + ", stringParameter = " + stringParameter);
    }
}

var testClass = new TestClass();
testClass.someMethod("string for v#1");
testClass.someMethod(12345, "string for v#2");

Berikut adalah contoh dari apa yang tidak ingin saya lakukan (saya benar-benar benci bagian dari peretasan yang berlebihan di JS):

class TestClass {
    private someMethod_Overload_string(stringParameter: string): void {
        // A lot of code could be here... I don't want to mix it with switch or if statement in general function
        alert("Variant #1: stringParameter = " + stringParameter);
    }

    private someMethod_Overload_number_string(numberParameter: number, stringParameter: string): void {
        alert("Variant #2: numberParameter = " + numberParameter + ", stringParameter = " + stringParameter);
    }

    private someMethod_Overload_string_number(stringParameter: string, numberParameter: number): void {
        alert("Variant #3: stringParameter = " + stringParameter + ", numberParameter = " + numberParameter);
    }

    public someMethod(stringParameter: string): void;
    public someMethod(numberParameter: number, stringParameter: string): void;
    public someMethod(stringParameter: string, numberParameter: number): void;

    public someMethod(): void {
        switch (arguments.length) {
        case 1:
            if(typeof arguments[0] == "string") {
                this.someMethod_Overload_string(arguments[0]);
                return;
            }
            return; // Unreachable area for this case, unnecessary return statement
        case 2:
            if ((typeof arguments[0] == "number") &&
                (typeof arguments[1] == "string")) {
                this.someMethod_Overload_number_string(arguments[0], arguments[1]);
            }
            else if ((typeof arguments[0] == "string") &&
                     (typeof arguments[1] == "number")) {
                this.someMethod_Overload_string_number(arguments[0], arguments[1]);
            }
            return; // Unreachable area for this case, unnecessary return statement
        }
    }
}


var testClass = new TestClass();
testClass.someMethod("string for v#1");
testClass.someMethod(12345, "string for v#2");
testClass.someMethod("string for v#3", 54321);

1
Ya, ada kemungkinan bahasanya belum mati. Ada pertanyaan lagi?
hakre

6
@hakre Itu hal yang aneh untuk dikatakan, mengingat TypeScript sudah mendukung metode overloading.
svick

@svick: baik, apakah Anda menyebut metode itu overloading? Dalam jawaban Anda, metode itu sendiri tidak kelebihan beban, satu tubuh ada di sekitar.
hakre

2
@hakre Spesifikasi menyebutnya sebagai metode overloading. Anda pasti dapat membantah bahwa ini bukan versi yang sangat bagus, tetapi saya pikir Anda tidak dapat mengatakan bahwa itu tidak ada sama sekali.
svick

@svick: Saya juga tidak mengatakannya. Tetapi menurut saya peluang yang ditanyakan OP adalah spesifik tentang model mental dari metode overloading. Untuk memecah rambut kita bisa mengatakan itu metode signature overloading;)
hakre

Jawaban:


165

Menurut spesifikasinya, TypeScript memang mendukung metode overloading, tetapi ini cukup canggung dan menyertakan banyak jenis parameter pemeriksaan kerja manual. Saya pikir itu terutama karena yang paling dekat Anda bisa mendapatkan metode overloading dalam JavaScript biasa termasuk pemeriksaan itu juga dan TypeScript mencoba untuk tidak mengubah badan metode sebenarnya untuk menghindari biaya kinerja runtime yang tidak perlu.

Jika saya memahaminya dengan benar, Anda harus terlebih dahulu menulis deklarasi metode untuk masing-masing kelebihan beban dan kemudian satu implementasi metode yang memeriksa argumennya untuk memutuskan kelebihan beban mana yang dipanggil. Tanda tangan implementasi harus kompatibel dengan semua kelebihan beban.

class TestClass {
    someMethod(stringParameter: string): void;
    someMethod(numberParameter: number, stringParameter: string): void;

    someMethod(stringOrNumberParameter: any, stringParameter?: string): void {
        if (stringOrNumberParameter && typeof stringOrNumberParameter == "number")
            alert("Variant #2: numberParameter = " + stringOrNumberParameter + ", stringParameter = " + stringParameter);
        else
            alert("Variant #1: stringParameter = " + stringOrNumberParameter);
    }
}

3
@NicoVanBelle JavaScript sama sekali tidak mendukung kelebihan metode, bukan? Jadi bagaimana kembali ke JS akan membantu?
svick

Bagaimana dengan memeriksa antarmuka? Apakah Anda memiliki solusi yang lebih baik dari itu: stackoverflow.com/questions/14425568/… ?
DiPix

hmm .. saya tidak terlalu suka ini, hanya memiliki parameter opsional saja lebih baik untuk dibaca.
LuckyLikey

3
Saya pikir yang penting di sini adalah membuat kode Anda dapat dibaca tanpa banyak kesalahan ketik memeriksa pernyataan if. Siapa yang peduli apa yang terjadi.
Brain2000

34

Perbarui untuk kejelasan. Metode overloading di TypeScript adalah fitur yang berguna sejauh memungkinkan Anda membuat definisi tipe untuk pustaka yang ada dengan API yang perlu direpresentasikan.

Namun, saat menulis kode Anda sendiri, Anda mungkin dapat menghindari overhead kognitif dari kelebihan beban menggunakan parameter opsional atau default. Ini adalah alternatif yang lebih mudah dibaca untuk kelebihan metode dan juga menjaga API Anda tetap jujur ​​karena Anda akan menghindari membuat kelebihan beban dengan pengurutan yang tidak intuitif.

Hukum umum kelebihan TypeScript adalah:

Jika Anda dapat menghapus tanda tangan kelebihan beban dan semua pengujian Anda berhasil, Anda tidak memerlukan kelebihan TypeScript

Anda biasanya dapat mencapai hal yang sama dengan opsional, atau parameter default - atau dengan tipe gabungan, atau dengan sedikit orientasi objek.

Pertanyaan Sebenarnya

Pertanyaan sebenarnya meminta kelebihan beban:

someMethod(stringParameter: string): void {

someMethod(numberParameter: number, stringParameter: string): void {

Sekarang, bahkan dalam bahasa yang mendukung kelebihan beban dengan implementasi terpisah (catatan: kelebihan TypeScript berbagi satu implementasi) - programmer disarankan untuk memberikan konsistensi dalam pengurutan. Ini akan membuat tanda tangan:

someMethod(stringParameter: string): void {

someMethod(stringParameter: string, numberParameter: number): void {

Itu stringParameterselalu dibutuhkan, jadi itu yang pertama. Anda bisa menulis ini sebagai kelebihan TypeScript yang berfungsi:

someMethod(stringParameter: string): void;
someMethod(stringParameter: string, numberParameter: number): void;
someMethod(stringParameter: string, numberParameter?: number): void {
    if (numberParameter != null) {
        // The number parameter is present...
    }
}

Tapi mengikuti hukum TypeScript overloads, kita bisa menghapus tanda tangan overload dan semua pengujian kita akan tetap lulus.

someMethod(stringParameter: string, numberParameter?: number): void {
    if (numberParameter != null) {
        // The number parameter is present...
    }
}

Pertanyaan Aktual, Dalam Urutan Sebenarnya

Jika Anda bertekad untuk tetap menggunakan pesanan asli, kelebihan bebannya adalah:

someMethod(stringParameter: string): void;
someMethod(numberParameter: number, stringParameter: string): void;
someMethod(a: string | number, b?: string | number): void {
  let stringParameter: string;
  let numberParameter: number;

  if (typeof a === 'string') {
    stringParameter = a;
  } else {
    numberParameter = a;

    if (typeof b === 'string') {
      stringParameter = b;
    }
  }
}

Sekarang banyak percabangan untuk mencari tahu di mana harus meletakkan parameter, tetapi Anda benar-benar ingin mempertahankan urutan ini jika Anda membaca sejauh ini ... tapi tunggu, apa yang terjadi jika kita menerapkan hukum kelebihan beban TypeScript?

someMethod(a: string | number, b?: string | number): void {
  let stringParameter: string;
  let numberParameter: number;

  if (typeof a === 'string') {
    stringParameter = a;
  } else {
    numberParameter = a;

    if (typeof b === 'string') {
      stringParameter = b;
    }
  }
}

Percabangan Sudah Cukup

Tentu saja, mengingat jumlah pemeriksaan tipe yang perlu kita lakukan ... mungkin jawaban terbaik adalah memiliki dua metode:

someMethod(stringParameter: string): void {
  this.someOtherMethod(0, stringParameter);
}

someOtherMethod(numberParameter: number, stringParameter: string): void {
  //...
}

yang umumnya tidak dikenal sebagai metode overloading. juga melihat pertanyaan, jenis perubahan parameter pertama.
hakre

3
Saya mengakui bahwa dalam jawaban saya - Anda akan meletakkan parameter opsional terakhir, jadi numberparameter akan menjadi argumen kedua dan akan menjadi opsional. TypeScript tidak mendukung kelebihan metode yang "tepat" - tetapi bahkan dunia C # beralih dari kelebihan beban ke parameter opsional karena dalam banyak kasus ini mengarah ke kode yang lebih mudah dibaca.
Fenton

Maksud Anda if (typeof numberParameter != 'undefined'), benar;)
Juan Mendes

Itu tergantung pada kasus penggunaan Anda, khususnya apakah Anda menerima nol. Jika Anda perlu melakukannya, ingatlah untuk menggunakannya !==untuk menghindari juggling.
Fenton

1
@Sebastian kedengarannya seperti kesempatan untuk injeksi ketergantungan. Mengingat metode overloading di TypeScript melibatkan satu metode dengan beberapa dekorasi, parameter default, dan jenis gabungan, memberikan pengalaman yang lebih baik. Jika metode berbeda dalam cara yang lebih substansial, Anda dapat menggunakan abstraksi, atau menerapkan beberapa metode.
Fenton

7

Saya harap. Saya ingin fitur ini juga, tetapi TypeScript harus dapat dioperasikan dengan JavaScript tanpa tipe yang tidak memiliki metode kelebihan beban. yaitu Jika metode kelebihan beban Anda dipanggil dari JavaScript, maka metode itu hanya bisa dikirim ke satu implementasi metode.

Ada beberapa diskusi yang relevan tentang codeplex. misalnya

https://typescript.codeplex.com/workitem/617

Saya masih berpikir TypeScript harus menghasilkan semua if'ing dan switching sehingga kita tidak perlu melakukannya.


2

Mengapa tidak menggunakan antarmuka yang ditentukan properti opsional sebagai argumen fungsi ..

Untuk kasus dalam pertanyaan ini, menggunakan antarmuka sebaris yang ditentukan dengan beberapa properti opsional hanya dapat langsung membuat kode seperti di bawah ini:

class TestClass {

    someMethod(arg: { stringParameter: string, numberParameter?: number }): void {
        let numberParameterMsg = "Variant #1:";
        if (arg.numberParameter) {
            numberParameterMsg = `Variant #2: numberParameter = ${arg.numberParameter},`;
        }
        alert(`${numberParameterMsg} stringParameter = ${arg.stringParameter}`);
    }
}

var testClass = new TestClass();
testClass.someMethod({ stringParameter: "string for v#1" });
testClass.someMethod({ numberParameter: 12345, stringParameter: "string for v#2" });

Karena kelebihan beban yang disediakan dalam TypeScript, seperti yang disebutkan dalam komentar orang lain, hanyalah daftar tanda tangan fungsi yang berbeda tanpa mendukung kode implementasi yang sesuai seperti bahasa statis lainnya. Sehingga implementasinya masih harus dilakukan hanya pada satu function body, yang membuat penggunaan function overloading pada typescript tidak senyaman bahasa-bahasa pendukung fitur overloading yang sebenarnya.

Namun, masih banyak barang baru dan nyaman yang disediakan dalam skrip ketikan yang tidak tersedia dalam bahasa pemrograman lama, di mana dukungan properti opsional dalam antarmuka anonim adalah pendekatan semacam itu untuk memenuhi zona nyaman dari kelebihan fungsi warisan, menurut saya.


0

Jika ada banyak variasi metode overload, cara lain adalah membuat kelas dengan semua argumen Anda di dalamnya. Jadi Anda hanya dapat mengirimkan parameter yang Anda inginkan dalam urutan apa pun.

class SomeMethodConfig {
  stringParameter: string;
  numberParameter: number;

 /**
 *
 */
 constructor(stringParameter: string = '012', numberParameter?: number) { // different ways to make a param optional
   this.numberParameter = 456; // you can put a default value here
   this.stringParameter = stringParameter; // to pass a value throw the constructor if necessary
 }
}

Anda juga dapat membuat konstruktor dengan nilai default dan / atau meneruskan beberapa argumen wajib. Kemudian gunakan saja seperti ini:

const config = new SomeMethodConfig('text');
config.numberParameter = 123; // initialize an optional parameter only if you want to do it
this.SomeMethod(config);

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.