ฟังก์ชัน TypeScript โอเวอร์โหลด


243

ส่วนที่ 6.3 ของสเป็คภาษา TypeScript พูดถึงเกี่ยวกับฟังก์ชั่นการโอเวอร์โหลดและให้ตัวอย่างที่เป็นรูปธรรมเกี่ยวกับวิธีการใช้งาน อย่างไรก็ตามถ้าฉันลองทำสิ่งนี้:

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";
     }        

}

ฉันได้รับข้อผิดพลาดของคอมไพเลอร์ที่ระบุตัวระบุที่ซ้ำกันถึงแม้ว่าพารามิเตอร์ฟังก์ชั่นจะเป็นประเภทที่แตกต่างกัน แม้ว่าฉันจะเพิ่มพารามิเตอร์เพิ่มเติมให้กับฟังก์ชั่น createFeatureLayer ที่สองฉันยังคงได้รับข้อผิดพลาดของคอมไพเลอร์ ความคิดโปรด


คำตอบ:


189

อาจเป็นเพราะเมื่อทั้งสองฟังก์ชั่นถูกคอมไพล์ไปยัง JavaScript ลายเซ็นของมันจะเหมือนกันโดยสิ้นเชิง เนื่องจาก JavaScript ไม่มีประเภทเราจึงสิ้นสุดการสร้างสองฟังก์ชันที่มีจำนวนอาร์กิวเมนต์เท่ากัน TypeScript จำกัด เราจากการสร้างฟังก์ชั่นดังกล่าว

TypeScript รองรับการโอเวอร์โหลดตามจำนวนพารามิเตอร์ แต่ขั้นตอนที่ต้องปฏิบัติตามนั้นแตกต่างกันเล็กน้อยหากเราเปรียบเทียบกับภาษา OO ในการตอบคำถาม SO อื่นมีคนอธิบายด้วยตัวอย่างที่ดี: วิธีการมากไป? .

โดยทั่วไปสิ่งที่เรากำลังทำคือเรากำลังสร้างเพียงหนึ่งฟังก์ชั่นและจำนวนการประกาศเพื่อให้ TypeScript ไม่ให้ข้อผิดพลาดในการรวบรวม เมื่อโค้ดนี้ถูกคอมไพล์ไปยัง JavaScript ฟังก์ชันที่เป็นรูปธรรมเพียงอย่างเดียวจะมองเห็นได้ ในฐานะที่เป็นฟังก์ชั่นจาวาสคริปต์ที่สามารถเรียกได้โดยผ่านข้อโต้แย้งหลายมันก็ใช้งานได้


50
ภาษาสามารถแก้ไขได้เพื่อสนับสนุนสิ่งนี้ ในทางทฤษฎีเราสามารถสร้างฟังก์ชั่นการใช้งานที่ตั้งชื่อแยกและเรียกโดยรวบรวม TypeScript (เช่น createFeatureLayer_1 และ createFeatureLayer_2) และ createFeatureLayer นั้นสามารถกำหนดที่หนึ่งที่จะเรียกตามเนื้อหาของข้อโต้แย้งสำหรับการทำงานร่วมกันกับวานิลลา JavaScript
โทมัสเอส. Trias

8
คุณใช้คำว่าราวกับว่าการโหลดมากเกินไปใน TypeScript เป็นไปได้เฉพาะตามจำนวนของพารามิเตอร์ในขณะที่การบรรทุกมากเกินไปตามประเภทยังเป็นไปได้ดังที่แสดงในคำตอบของ Steve Fenton
Matthijs Wessels

9
นี่เป็นง่อยเลย TypeScript ควรจะสร้าง "ฟังก์ชั่นเมตา" ที่เลือกการใช้งานที่มีชื่อไม่ซ้ำกันอย่างเหมาะสมขึ้นอยู่กับสิ่งที่มันถูกส่งผ่าน ตอนนี้มีความแตกแยกที่คุณสามารถส่งคอมไพเลอร์ได้อย่างไร แต่การใช้การดมกลิ่นประเภทของคุณอาจไม่ถูกต้อง
Ezekiel Victor

5
@EzekielVictor TypeScript จะทำเช่นนั้นหากมีวิธีการตรวจสอบประเภทที่เชื่อถือได้ในขณะใช้งาน
หนาม̈

3
มันมีความซับซ้อนมากขึ้นมันสามารถทำได้กับประเภทของ JavaScript แต่แนวคิดเฉพาะ TS เช่นอินเทอร์เฟซtypes, enums, generics และอื่น ๆ หายไปตอนรันไทม์ someObject instanceof ISomeInterfaceDefinedInTypeScriptนั่นเป็นเหตุผลที่คุณไม่สามารถทำได้
Morgan Touverey Quilling

209

เมื่อคุณโอเวอร์โหลดใน TypeScript คุณจะมีการนำไปใช้งานที่มีลายเซ็นหลายรายการเท่านั้น

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

TypeScript สามตัวเท่านั้นที่ได้รับการยอมรับโดย TypeScript ว่าเป็นลายเซ็นที่เป็นไปได้สำหรับการเรียกใช้เมธอดไม่ใช่การติดตั้งจริง

ในกรณีของคุณฉันจะใช้สองวิธีที่มีชื่อแตกต่างกันเป็นการส่วนตัวเนื่องจากมีพารามิเตอร์ทั่วไปไม่เพียงพอซึ่งทำให้มีความเป็นไปได้ที่ร่างกายของวิธีการจะต้องมี "ifs" จำนวนมากเพื่อตัดสินใจว่าจะทำอย่างไร

TypeScript 1.4

ในฐานะของ TypeScript 1.4 คุณสามารถลบความต้องการโอเวอร์โหลดโดยใช้ประเภทยูเนี่ยน ตัวอย่างด้านบนสามารถแสดงได้ดีขึ้นโดยใช้:

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

ประเภทของการaเป็น "อย่างใดอย่างหนึ่งstringหรือnumber"


คำตอบที่ดี ฉันต้องการเน้นว่าสิ่งนี้อาจไม่เป็นประโยชน์เมื่อมีคนพยายามโอเวอร์โหลดด้วยเหตุผลเช่น: ฉันต้องการมีอินสแตนซ์โดยใช้ Constructor เดียวกันฉันสามารถส่งวัตถุที่กำหนดคุณสมบัติทั้งหมดที่คาดหวังและใน ตัวอย่างหนึ่งส่งผ่าน params แต่ละรายการ: class Foo { constructor(obj) { } constructor (a: number, b: string, c: boolean) {} }
Hlawuleka MAS

โดยทั่วไปแล้วผมอยากจะใช้วิธีการที่โรงงานเพื่อสร้างฉันวัตถุแต่ละวิธี - มีความจำเป็นที่จะสาขาถ้าคุณโทรFoo.fromObject(obj)และFoo.fromJson(str)และอื่น ๆ
เฟนตัน

แต่นั่นเป็นข้ออ้างว่าจะส่งผ่านพารามิเตอร์ของพวกเขาเป็นวัตถุหรือสตริงเดียวจะเกิดอะไรขึ้นถ้าฉันต้องการให้พวกเขาผ่านแยกต่างหากดังที่ไฮไลต์จากความคิดเห็นก่อนหน้าของฉัน Foo.methos(1, 2, 3) Foo.method(1) Foo.method(Obj) ฉันยังสังเกตเห็นว่าคุณมีวิธีการที่แตกต่างกันในFooชั้นเรียนจาก Object และจาก Json?
Hlawuleka MAS

1
หากคุณติดตามความแตกต่างนั้นกลับไปยังแหล่งข้อมูลคุณมักจะพบว่าไม่จำเป็นต้องใช้มัน ตัวอย่างเช่นคุณต้องพิมพ์myNumหรือmyObjต่อไปดังนั้นทำไมไม่มีวิธีแยกต่างหากและทำให้ทุกอย่างชัดเจน / หลีกเลี่ยงตรรกะการแยกย่อยที่ไม่จำเป็น
เฟนตัน

2
โปรดทราบว่าการใช้ประเภทยูเนี่ยนอาจเป็นปัญหาได้หากคุณต้องการมีประเภทการส่งคืนที่แตกต่างกันตามพารามิเตอร์ ที่สามารถแก้ไขได้ด้วย generics หากประเภทการคืนสินค้าตรงกับหนึ่งในประเภทพารามิเตอร์เสมอ แต่สำหรับกรณีอื่น ๆ การโอเวอร์โหลดเป็นทางออกที่ดีที่สุด
John Montgomery

45

คุณสามารถประกาศฟังก์ชั่นที่โอเวอร์โหลดได้โดยการประกาศฟังก์ชั่นว่ามีประเภทที่มีลายเซ็นหลายรายการ:

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

จากนั้นต่อไปนี้:

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

คำจำกัดความที่แท้จริงของฟังก์ชันจะต้องเป็นเอกพจน์และดำเนินการจัดส่งที่เหมาะสมภายในข้อโต้แย้ง

ตัวอย่างเช่นการใช้คลาส (ซึ่งสามารถใช้งานIFooได้ แต่ไม่จำเป็นต้องทำ):

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;
    }
}

สิ่งที่น่าสนใจในที่นี้คือanyรูปแบบนั้นถูกซ่อนไว้โดยการแทนที่ที่มีการพิมพ์เฉพาะ

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

1

ฟังก์ชั่นการโหลดเกินพิกัดทั่วไปคืออะไร?

การโอเวอร์โหลดฟังก์ชั่นหรือวิธีการโอเวอร์โหลดเป็นความสามารถในการสร้างหลายฟังก์ชั่นในชื่อเดียวกันกับการใช้งานที่แตกต่างกัน ( Wikipedia )


ฟังก์ชันโอเวอร์โหลดใน JS คืออะไร

คุณลักษณะนี้เป็นไปไม่ได้ใน JS - ฟังก์ชั่นที่กำหนดไว้ล่าสุดถูกนำมาใช้ในกรณีที่มีการประกาศหลายรายการ:

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

... และใน TS?

โอเวอร์โหลดเป็นโครงสร้างเวลารวบรวมโดยไม่มีผลกระทบกับรันไทม์ 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

ข้อผิดพลาดในการใช้งานที่ซ้ำกันจะถูกเรียกใช้หากคุณใช้รหัสด้านบน (ปลอดภัยกว่า JS) TS เลือกโอเวอร์โหลดแรกที่เหมาะสมตามลำดับจากบนลงล่างดังนั้นโอเวอร์โหลดจึงถูกเรียงลำดับจากเฉพาะส่วนใหญ่ไปจนถึงกว้างที่สุด


วิธีการบรรทุกเกินพิกัดใน TS: ตัวอย่างที่ซับซ้อนมากขึ้น

ประเภทวิธีการเรียนมากเกินไปสามารถใช้ในลักษณะที่คล้ายกับการทำงานมากเกินไป:

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

โอเวอร์โหลดที่แตกต่างกันอย่างมากมายเป็นไปได้เนื่องจากการใช้งานฟังก์ชั่นเข้ากันได้กับลายเซ็นเกินพิกัด - บังคับใช้โดยคอมไพเลอร์

ข่าวสารเพิ่มเติม:


0

ในฐานะที่เป็นหัวถึงคนอื่น ๆ ฉันได้รับการยอมรับว่าอย่างน้อยที่สุดเท่าที่ประจักษ์โดย TypeScript รวบรวมโดย WebPack สำหรับ Angular 2 คุณจะได้รับ overWRITTEN แทนที่จะเป็นวิธีการดาวน์โหลดมากเกินไป

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

โทรศัพท์:

myComponent.method()

ดูเหมือนว่าจะดำเนินการวิธีการที่มีข้อโต้แย้งโดยไม่สนใจรุ่น no-arg ที่มีเอาต์พุต:

with arg

2
คุณไม่สามารถประกาศเนื้อหาที่แยกต่างหากสำหรับการโอเวอร์โหลดของคุณได้เฉพาะลายเซ็นที่แตกต่างกัน
adharris

5
ฉันไม่แน่ใจว่าคอมไพเลอร์ TypeScript เวอร์ชันใดที่คุณกำลังใช้อยู่ แต่เวอร์ชันปัจจุบันส่งDuplicate function implementationคำเตือนสำหรับรหัสเช่นนี้
Royston Shufflebotham

0

ฟังก์ชันโอเวอร์โหลดใน typescript:

ตามวิกิพีเดีย (และหนังสือการเขียนโปรแกรมจำนวนมาก) ความหมายของวิธีการ / ฟังก์ชั่นการโอเวอร์โหลดมีดังต่อไปนี้:

ในบางภาษาการเขียนโปรแกรมมากไปฟังก์ชั่นหรือวิธีการบรรทุกเกินพิกัดคือความสามารถในการสร้างฟังก์ชั่นหลาย ๆ ที่มีชื่อเดียวกันกับการใช้งานที่แตกต่างกัน การเรียกใช้ฟังก์ชันที่โอเวอร์โหลดจะเรียกใช้การใช้งานเฉพาะของฟังก์ชันนั้นที่เหมาะสมกับบริบทของการโทรทำให้การเรียกใช้ฟังก์ชันหนึ่งสามารถทำงานที่แตกต่างกันไปตามบริบท

ใน typescript เราไม่สามารถมีการใช้งานที่แตกต่างกันของฟังก์ชั่นเดียวกันที่เรียกว่าตามจำนวนและประเภทของการขัดแย้ง นี่คือเนื่องจากเมื่อ TS ถูกคอมไพล์กับ JS ฟังก์ชันใน JS มีลักษณะดังต่อไปนี้:

  • นิยามฟังก์ชัน JavaScript ไม่ได้ระบุชนิดข้อมูลสำหรับพารามิเตอร์
  • ฟังก์ชั่นจาวาสคริปต์ไม่ได้ตรวจสอบจำนวนข้อโต้แย้งเมื่อเรียก

ดังนั้นในความหมายที่เข้มงวดเราอาจโต้แย้งได้ว่าฟังก์ชั่นการโอเวอร์โหลดของ TS ไม่มีอยู่จริง อย่างไรก็ตามมีหลายสิ่งที่คุณสามารถทำได้ภายในรหัส TS ของคุณที่สามารถเลียนแบบการทำงานหนักเกินไปได้อย่างสมบูรณ์แบบ

นี่คือตัวอย่าง:

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 เรียกวิธีนี้มากไปและสิ่งที่เราทำโดยทั่วไปคือการจัดหาลายเซ็นของวิธีการต่างๆ (คำอธิบายของพารามิเตอร์และประเภทที่เป็นไปได้) ให้กับคอมไพเลอร์ TS ตอนนี้ TS สามารถเข้าใจได้ว่าเราเรียกใช้ฟังก์ชันของเราอย่างถูกต้องในช่วงเวลารวบรวมและให้ข้อผิดพลาดแก่เราหรือไม่ถ้าเราเรียกใช้ฟังก์ชันไม่ถูกต้อง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.