Fungsi overScript TypeScript

244

Bagian 6.3 dari spesifikasi bahasa TypeScript berbicara tentang fungsi yang berlebihan dan memberikan contoh nyata tentang bagaimana mengimplementasikan ini. Namun jika saya mencoba sesuatu seperti ini:

export class LayerFactory { 

    constructor (public styleFactory: Symbology.StyleFactory) { }

    createFeatureLayer (userContext : Model.UserContext, mapWrapperObj : MapWrapperBase) : any {           
         throw "not implemented";
    }                 

    createFeatureLayer(layerName : string, style : any) : any {
        throw "not implemented";
     }        

}

Saya mendapatkan kesalahan kompiler yang menunjukkan pengidentifikasi duplikat meskipun parameter fungsi dari tipe yang berbeda. Bahkan jika saya menambahkan parameter tambahan ke fungsi createFeatureLayer kedua, saya masih mendapatkan kesalahan kompiler. Gagasan, silakan.

Klaus Nji
sumber
Kemungkinan duplikat Metode kelebihan beban?
BuZZ-dEE

Jawaban:

189

Ini mungkin karena, ketika kedua fungsi dikompilasi dengan JavaScript, tanda tangannya sama sekali identik. Karena JavaScript tidak memiliki jenis, kami akhirnya membuat dua fungsi dengan jumlah argumen yang sama. Jadi, TypeScript membatasi kita dari membuat fungsi-fungsi seperti itu.

TypeScript mendukung overloading berdasarkan jumlah parameter, tetapi langkah-langkah yang harus diikuti sedikit berbeda jika kita dibandingkan dengan bahasa OO. Dalam menjawab pertanyaan SO lainnya, seseorang menjelaskannya dengan contoh yang bagus: Metode overloading? .

Pada dasarnya, apa yang kita lakukan adalah, kita hanya menciptakan satu fungsi dan sejumlah deklarasi sehingga TypeScript tidak memberikan kesalahan kompilasi. Ketika kode ini dikompilasi ke JavaScript, fungsi konkret saja akan terlihat. Sebagai fungsi JavaScript dapat dipanggil dengan melewati beberapa argumen, itu hanya berfungsi.

S. Ravi Kiran
sumber
50
Bahasa dapat diubah untuk mendukung ini. Secara teori, seseorang dapat menghasilkan implementasi fungsi yang dinamai terpisah dan dipanggil dengan dikompilasi TypeScript (misalnya createFeatureLayer_1 dan createFeatureLayer_2) dan createFeatureLayer kemudian dapat menentukan mana yang akan dipanggil berdasarkan isi argumen untuk dioperasikan dengan vanilla JavaScript.
Thomas S. Trias
8
Anda mengatakannya seolah-olah kelebihan dalam TypeScript hanya mungkin berdasarkan jumlah parameter, sementara kelebihan berdasarkan jenis juga mungkin seperti yang ditunjukkan dalam jawaban Steve Fenton.
Matthijs Wessels
9
Ini agak timpang; TypeScript harus benar-benar menghasilkan "fungsi meta" yang memilih implementasi yang dinamai secara unik berdasarkan apa yang telah diteruskan. Bagaimana sekarang ada celah di mana Anda bisa melewati kompiler tetapi implementasi Anda dari jenis sniffing bisa salah.
Yehezkiel Victor
5
@EzekielVictor TypeScript akan melakukannya jika ada cara yang dapat diandalkan untuk memeriksa jenis pada waktu berjalan.
durï
3
Itu bahkan lebih rumit, itu bisa dilakukan dengan tipe JavaScript, tetapi pengertian TS-spesifik seperti antarmuka, types, enum, generik, dll, hilang saat runtime. Itu juga sebabnya Anda tidak bisa melakukannya someObject instanceof ISomeInterfaceDefinedInTypeScript.
Morgan Touverey Quilling
209

Ketika Anda kelebihan dalam TypeScript, Anda hanya memiliki satu implementasi dengan beberapa tanda tangan.

class Foo {
    myMethod(a: string);
    myMethod(a: number);
    myMethod(a: number, b: string);
    myMethod(a: any, b?: string) {
        alert(a.toString());
    }
}

Hanya tiga kelebihan yang dikenali oleh TypeScript sebagai tanda tangan yang mungkin untuk pemanggilan metode, bukan implementasi yang sebenarnya.

Dalam kasus Anda, saya secara pribadi akan menggunakan dua metode dengan nama yang berbeda karena tidak ada cukup kesamaan dalam parameter, yang memungkinkan badan metode perlu memiliki banyak "seandainya" untuk memutuskan apa yang harus dilakukan.

TypeScript 1.4

Pada TypeScript 1.4, Anda biasanya dapat menghapus kebutuhan untuk kelebihan menggunakan tipe serikat. Contoh di atas dapat diekspresikan lebih baik menggunakan:

myMethod(a: string | number, b?: string) {
    alert(a.toString());
}

Jenisnya aadalah "salah stringatau number".

Fenton
sumber
Jawaban yang bagus Saya hanya ingin menyoroti itu, ini mungkin tidak membantu ketika seseorang mencoba membebani karena alasan-alasan seperti: Saya ingin memiliki sebuah contoh, di mana menggunakan konstruktor yang sama, saya dapat melewatkan objek mendefinisikan semua properti yang diharapkan dan di satu contoh, lulus params individu: class Foo { constructor(obj) { } constructor (a: number, b: string, c: boolean) {} }
Hlawuleka MAS
Secara umum, saya lebih suka menggunakan metode pabrik untuk membuat saya sebuah objek setiap jalan - tidak perlu ke cabang jika Anda menelepon Foo.fromObject(obj)dan Foo.fromJson(str)dan sebagainya.
Fenton
Tapi itu mendalilkan bahwa seseorang akan selalu melewati parameter mereka baik sebagai objek atau string tunggal, bagaimana jika saya ingin mereka melewati secara terpisah, seperti yang disorot dari komentar saya sebelumnya? Foo.methos(1, 2, 3) Foo.method(1) Foo.method(Obj) Saya juga memperhatikan Anda memiliki metode yang berbeda di FooKelas, fromObject dan fromJson?
Hlawuleka MAS
1
Jika Anda mengikuti perbedaan itu kembali ke sumbernya, biasanya Anda akan menemukan bahwa tidak perlu untuk itu. Sebagai contoh, Anda harus mengetik myNumatau myObjtetap, jadi mengapa tidak memiliki metode terpisah dan membuat semuanya jelas / hindari logika percabangan yang tidak perlu.
Fenton
2
Perhatikan bahwa menggunakan tipe gabungan dapat menjadi masalah jika Anda ingin memiliki tipe pengembalian yang berbeda berdasarkan parameter. Itu bisa diselesaikan dengan obat generik jika tipe kembali selalu cocok dengan salah satu tipe parameter, tetapi untuk kasus lain kelebihan adalah solusi terbaik.
John Montgomery
45

Anda dapat mendeklarasikan fungsi kelebihan beban dengan mendeklarasikan fungsi tersebut memiliki tipe yang memiliki banyak tanda tangan permohonan:

interface IFoo
{
    bar: {
        (s: string): number;
        (n: number): string;
    }
}

Maka berikut ini:

var foo1: IFoo = ...;

var n: number = foo1.bar('baz');     // OK
var s: string = foo1.bar(123);       // OK
var a: number[] = foo1.bar([1,2,3]); // ERROR

Definisi aktual dari fungsi harus tunggal dan melakukan pengiriman yang tepat secara internal pada argumennya.

Misalnya, menggunakan kelas (yang bisa mengimplementasikan IFoo, tetapi tidak harus):

class Foo
{
    public bar(s: string): number;
    public bar(n: number): string;
    public bar(arg: any): any 
    {
        if (typeof(arg) === 'number')
            return arg.toString();
        if (typeof(arg) === 'string')
            return arg.length;
    }
}

Yang menarik di sini adalah bahwa anyformulir disembunyikan oleh penimpaan diketik lebih spesifik.

var foo2: new Foo();

var n: number = foo2.bar('baz');     // OK
var s: string = foo2.bar(123);       // OK
var a: number[] = foo2.bar([1,2,3]); // ERROR
Drew Noakes
sumber
1

Apakah fungsi kelebihan beban secara umum?

Fungsi overloading atau metode overloading adalah kemampuan untuk membuat beberapa fungsi dari nama yang sama dengan implementasi yang berbeda ( Wikipedia )


Apakah fungsi kelebihan beban di JS?

Fitur ini tidak dimungkinkan dalam JS - fungsi yang didefinisikan terakhir diambil jika ada beberapa deklarasi:

function foo(a1, a2) { return `${a1}, ${a2}` }
function foo(a1) { return `${a1}` } // replaces above `foo` declaration
foo(42, "foo") // "42"

... dan di TS?

Overload adalah konstruk waktu kompilasi tanpa dampak pada runtime JS:

function foo(s: string): string // overload #1 of foo
function foo(s: string, n: number): number // overload #2 of foo
function foo(s: string, n?: number): string | number {/* ... */} // foo implementation

Kesalahan implementasi duplikat dipicu, jika Anda menggunakan kode di atas (lebih aman daripada JS). TS memilih overload fitting pertama dalam urutan top-down, sehingga overload diurutkan dari yang paling spesifik ke yang paling luas.


Metode overloading di TS: contoh yang lebih kompleks

Tipe metode kelas kelebihan beban dapat digunakan dengan cara yang mirip dengan fungsi kelebihan beban:

class LayerFactory {
    createFeatureLayer(a1: string, a2: number): string
    createFeatureLayer(a1: number, a2: boolean, a3: string): number
    createFeatureLayer(a1: string | number, a2: number | boolean, a3?: string)
        : number | string { /*... your implementation*/ }
}

const fact = new LayerFactory()
fact.createFeatureLayer("foo", 42) // string
fact.createFeatureLayer(3, true, "bar") // number

Overload yang sangat berbeda dimungkinkan, karena implementasi fungsi kompatibel untuk semua tanda tangan overload - diberlakukan oleh kompiler.

Lebih banyak info:

ford04
sumber
0

Sebagai kepala ke orang lain, saya telah menyadari bahwa setidaknya sebagaimana dimanifestasikan oleh TypeScript dikompilasi oleh WebPack untuk Angular 2, Anda diam-diam mendapatkan overWRITTEN alih-alih metode overLOADED.

myComponent {
  method(): { console.info("no args"); },
  method(arg): { console.info("with arg"); }
}

Panggilan:

myComponent.method()

tampaknya mengeksekusi metode dengan argumen, dengan diam-diam mengabaikan versi no-arg, dengan output:

with arg
mtyson
sumber
2
Anda tidak dapat mendeklarasikan badan terpisah untuk kelebihan beban Anda, hanya tanda tangan yang berbeda.
adharris
5
Saya tidak yakin versi mana dari kompiler TypeScript yang Anda gunakan, tetapi versi saat ini memancarkan Duplicate function implementationperingatan untuk kode seperti ini.
Royston Shufflebotham
0

Fungsi overload dalam naskah:

Menurut Wikipedia, (dan banyak buku pemrograman) definisi overloading metode / fungsi adalah sebagai berikut:

Dalam beberapa bahasa pemrograman, kelebihan fungsi atau kelebihan metode adalah kemampuan untuk membuat beberapa fungsi dengan nama yang sama dengan implementasi yang berbeda . Panggilan ke fungsi yang kelebihan beban akan menjalankan implementasi spesifik dari fungsi yang sesuai dengan konteks panggilan, memungkinkan satu panggilan fungsi untuk melakukan tugas yang berbeda tergantung pada konteksnya.

Dalam naskah, kita tidak bisa memiliki implementasi yang berbeda dari fungsi yang sama yang dipanggil sesuai dengan jumlah dan jenis argumen. Ini karena ketika TS dikompilasi ke JS, fungsi-fungsi di JS memiliki karakteristik berikut:

  • Definisi fungsi JavaScript tidak menentukan tipe data untuk parameternya
  • Fungsi JavaScript tidak memeriksa jumlah argumen saat dipanggil

Oleh karena itu, dalam arti yang ketat, orang dapat berargumen bahwa kelebihan fungsi TS tidak ada. Namun, ada beberapa hal yang dapat Anda lakukan dalam kode TS Anda yang dapat dengan sempurna meniru fungsi yang berlebihan.

Berikut ini sebuah contoh:

function add(a: number, b: number, c: number): number;
function add(a: number, b: number): any;
function add(a: string, b: string): any;

function add(a: any, b: any, c?: any): any {
  if (c) {
    return a + c;
  }
  if (typeof a === 'string') {
    return `a is ${a}, b is ${b}`;
  } else {
    return a + b;
  }
}

TS docs menyebut metode ini kelebihan beban, dan yang pada dasarnya kami lakukan adalah memasok beberapa tanda tangan metode (deskripsi parameter dan tipe yang mungkin) ke kompiler TS. Sekarang TS dapat mengetahui apakah kita memanggil fungsi kita dengan benar selama waktu kompilasi dan memberi kita kesalahan jika kita menyebut fungsi tersebut dengan tidak benar.

Willem van der Veen
sumber