Quá tải chức năng TypeScript


242

Phần 6.3 của đặc tả ngôn ngữ TypeScript nói về quá tải chức năng và đưa ra các ví dụ cụ thể về cách thực hiện điều này. Tuy nhiên nếu tôi thử một cái gì đó như thế này:

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

}

Tôi nhận được một lỗi trình biên dịch cho biết định danh trùng lặp mặc dù các tham số chức năng là các loại khác nhau. Ngay cả khi tôi thêm một tham số bổ sung cho hàm createdFeatureLayer thứ hai, tôi vẫn gặp lỗi trình biên dịch. Ý tưởng, xin vui lòng.


Có thể trùng lặp Phương thức quá tải?
BuZZ-dEE

Câu trả lời:


189

Điều này có thể là do, khi cả hai hàm được biên dịch thành JavaScript, chữ ký của chúng hoàn toàn giống nhau. Vì JavaScript không có kiểu, nên cuối cùng chúng ta sẽ tạo hai hàm có cùng số lượng đối số. Vì vậy, TypeScript hạn chế chúng ta tạo các hàm như vậy.

TypeScript hỗ trợ nạp chồng dựa trên số lượng tham số, nhưng các bước cần tuân theo hơi khác một chút nếu chúng ta so sánh với các ngôn ngữ OO. Để trả lời cho một câu hỏi SO khác, ai đó đã giải thích nó bằng một ví dụ hay: Phương thức quá tải? .

Về cơ bản, những gì chúng tôi đang làm là, chúng tôi đang tạo chỉ một hàm và một số khai báo để TypeScript không đưa ra lỗi biên dịch. Khi mã này được biên dịch thành JavaScript, chỉ có chức năng cụ thể sẽ hiển thị. Là một hàm JavaScript có thể được gọi bằng cách chuyển nhiều đối số, nó chỉ hoạt động.


50
Ngôn ngữ có thể được sửa đổi để hỗ trợ điều này. Về lý thuyết, người ta có thể tạo ra các triển khai chức năng được đặt tên riêng và được gọi bằng TypeScript đã biên dịch (ví dụ: createdFeatureLayer_1 và createdFeatureLayer_2) và createdFeatureLayer sau đó có thể xác định cái nào sẽ gọi dựa trên nội dung của các đối số để tương tác với JavaScript vanilla.
Thomas S. Trias

8
Bạn nói nó như thể quá tải trong TypeScript chỉ có thể dựa trên số lượng tham số, trong khi quá tải dựa trên loại cũng có thể như trong câu trả lời của Steve Fenton.
Matthijs Wessels

9
Đây là loại khập khiễng; TypeScript thực sự nên tạo ra "hàm meta" chọn cách triển khai được đặt tên duy nhất một cách thích hợp dựa trên những gì nó được thông qua. Làm thế nào bây giờ có một rạn nứt nơi bạn có thể vượt qua trình biên dịch nhưng việc thực hiện kiểu đánh hơi của bạn có thể không chính xác.
Ezekiel Victor

5
@EzekielVictor TypeScript sẽ làm điều đó nếu có một cách đáng tin cậy để kiểm tra các loại trong thời gian chạy.
gai

3
Điều đó thậm chí còn phức tạp hơn, nó có thể thực hiện được với các loại JavaScript, nhưng các khái niệm dành riêng cho TS như giao diện, types, enum, generic, v.v., bị mất khi chạy. Đó cũng là lý do tại sao bạn không thể làm someObject instanceof ISomeInterfaceDefinedInTypeScript.
Morgan Todarey Quilling

209

Khi bạn quá tải trong TypeScript, bạn chỉ có một triển khai với nhiều chữ ký.

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

Chỉ có ba tình trạng quá tải được TypeScript nhận ra là chữ ký có thể có cho một cuộc gọi phương thức, chứ không phải việc triển khai thực tế.

Trong trường hợp của bạn, cá nhân tôi sẽ sử dụng hai phương thức với các tên khác nhau vì không có đủ điểm chung trong các tham số, điều đó có khả năng cơ thể phương thức sẽ cần phải có nhiều "ifs" để quyết định nên làm gì.

TypeScript 1.4

Kể từ TypeScript 1.4, bạn thường có thể loại bỏ nhu cầu quá tải bằng cách sử dụng loại kết hợp. Ví dụ trên có thể được thể hiện tốt hơn bằng cách sử dụng:

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

Loại alà "hoặc stringhoặc number".


Câu trả lời chính xác. Tôi chỉ muốn nhấn mạnh rằng, điều này có thể không hữu ích khi một người đang cố gắng quá tải vì những lý do như: Tôi muốn có một ví dụ, khi sử dụng cùng một hàm tạo, tôi có thể chuyển một đối tượng xác định tất cả các thuộc tính dự kiến ​​và trong một trường hợp, vượt qua các thông số cá nhân: class Foo { constructor(obj) { } constructor (a: number, b: string, c: boolean) {} }
Hlawuleka MAS

Nói chung, tôi muốn sử dụng một phương pháp nhà máy để tạo ra cho tôi một đối tượng mỗi chiều - không cần đến chi nhánh nếu bạn gọi Foo.fromObject(obj)Foo.fromJson(str)vân vân.
Fenton

Nhưng điều đó quy định rằng người ta sẽ luôn truyền các tham số của họ dưới dạng một đối tượng hoặc một chuỗi, nếu tôi muốn chúng được truyền riêng rẽ, như được tô sáng từ nhận xét trước đó của tôi thì sao? Foo.methos(1, 2, 3) Foo.method(1) Foo.method(Obj) Tôi cũng nhận thấy bạn có các phương thức khác nhau trong FooClass, fromObject và fromJson?
Hlawuleka MAS

1
Nếu bạn theo sự khác biệt đó trở lại nguồn, bạn thường sẽ thấy không cần nó. Ví dụ, bạn phải gõ myNumhoặc bằng myObjmọi cách, vậy tại sao không có các phương thức riêng biệt và làm cho mọi thứ rõ ràng / tránh logic phân nhánh không cần thiết.
Fenton

2
Lưu ý rằng việc sử dụng loại kết hợp có thể có vấn đề nếu bạn muốn có các loại trả lại khác nhau dựa trên các tham số. Điều đó có thể được giải quyết bằng thuốc generic nếu loại trả về luôn khớp với một trong các loại tham số, nhưng đối với các trường hợp khác, quá tải là giải pháp tốt nhất.
John Montgomery

45

Bạn có thể khai báo một hàm quá tải bằng cách khai báo hàm là có một loại có nhiều chữ ký gọi:

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

Sau đó, như sau:

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

Định nghĩa thực tế của hàm phải là số ít và thực hiện việc gửi đi thích hợp bên trong các đối số của nó.

Ví dụ: sử dụng một lớp (có thể thực hiện IFoo, nhưng không phải):

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

Điều thú vị ở đây là anyhình thức được ẩn bởi các phần ghi đè được gõ cụ thể hơn.

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

Chức năng quá tải nói chung là gì?

Tái định nghĩa hàm hoặc phương pháp quá tải là khả năng tạo ra nhiều chức năng của cùng tên với hiện thực khác nhau ( Wikipedia )


Chức năng nạp chồng trong JS là gì?

Tính năng này không khả dụng trong JS - hàm được xác định cuối cùng được thực hiện trong trường hợp có nhiều khai báo:

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

... và trong TS?

Quá tải là một cấu trúc thời gian biên dịch mà không ảnh hưởng đến thời gian chạy 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

Một lỗi thực thi trùng lặp được kích hoạt, nếu bạn sử dụng mã ở trên (an toàn hơn so với JS). TS chọn quá tải phù hợp đầu tiên theo thứ tự từ trên xuống, do đó, quá tải được sắp xếp từ cụ thể nhất đến rộng nhất.


Phương thức nạp chồng trong TS: một ví dụ phức tạp hơn

Các kiểu phương thức lớp quá tải có thể được sử dụng theo cách tương tự như nạp chồng hàm:

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

Quá tải rất khác nhau là có thể, vì việc thực hiện chức năng tương thích với tất cả các chữ ký quá tải - được thực thi bởi trình biên dịch.

Thêm thông tin:


0

Với tư cách là người đứng đầu, tôi đã khẳng định rằng ít nhất như được thể hiện bởi TypeScript được biên soạn bởi WebPack cho Angular 2, bạn lặng lẽ nhận được các phương thức OverRREN thay vì các phương thức TẢI XUỐNG.

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

Gọi điện thoại:

myComponent.method()

dường như thực thi phương thức với các đối số, âm thầm bỏ qua phiên bản no-arg, với đầu ra:

with arg

2
Bạn không thể khai báo các cơ quan riêng biệt cho tình trạng quá tải của mình, chỉ có các chữ ký khác nhau.
adharris

5
Tôi không chắc chắn phiên bản nào của trình biên dịch TypeScript mà bạn đang sử dụng, nhưng phiên bản hiện tại phát ra Duplicate function implementationcảnh báo cho mã như thế này.
Royston Shufflebotham

0

Chức năng quá tải trong bản thảo:

Theo Wikipedia, (và nhiều sách lập trình) định nghĩa về nạp chồng phương thức / hàm là như sau:

Trong một số ngôn ngữ lập trình, nạp chồng hàm hoặc nạp chồng phương thức là khả năng tạo nhiều hàm cùng tên với các cách triển khai khác nhau . Các cuộc gọi đến một chức năng bị quá tải sẽ chạy một triển khai cụ thể của chức năng đó phù hợp với ngữ cảnh của cuộc gọi, cho phép một cuộc gọi chức năng thực hiện các tác vụ khác nhau tùy theo ngữ cảnh.

Trong bản thảo, chúng ta không thể có các cách triển khai khác nhau của cùng một hàm được gọi theo số lượng và loại đối số. Điều này là do khi TS được biên dịch thành JS, các hàm trong JS có các đặc điểm sau:

  • Các định nghĩa hàm JavaScript không chỉ định các kiểu dữ liệu cho các tham số của chúng
  • Các hàm JavaScript không kiểm tra số lượng đối số khi được gọi

Do đó, theo một nghĩa nghiêm ngặt, người ta có thể lập luận rằng quá tải chức năng TS không tồn tại. Tuy nhiên, có những điều bạn có thể làm trong mã TS hoàn toàn có thể bắt chước quá tải chức năng.

Đây là một ví dụ:

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

Các tài liệu TS gọi phương thức này là quá tải và về cơ bản những gì chúng tôi đã làm là cung cấp nhiều chữ ký phương thức (mô tả các tham số và loại có thể) cho trình biên dịch TS. Bây giờ TS có thể tìm ra nếu chúng ta gọi hàm của chúng ta một cách chính xác trong thời gian biên dịch và báo lỗi nếu chúng ta gọi hàm không đúng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.