Xác định kiểu gọi lại TypeScript


172

Tôi đã có lớp sau trong TypeScript:

class CallbackTest
{
    public myCallback;

    public doWork(): void
    {
        //doing some work...
        this.myCallback(); //calling callback
    }
}

Tôi đang sử dụng lớp học như thế này:

var test = new CallbackTest();
test.myCallback = () => alert("done");
test.doWork();

Mã này hoạt động, vì vậy nó sẽ hiển thị một hộp thông báo như mong đợi.

Câu hỏi của tôi là: Có loại nào tôi có thể cung cấp cho trường lớp của mình myCallbackkhông? Ngay bây giờ, lĩnh vực công cộng myCallbacklà loại anynhư hiển thị ở trên. Làm cách nào để xác định chữ ký phương thức của cuộc gọi lại? Hoặc tôi chỉ có thể đặt loại thành một số loại gọi lại? Hoặc tôi có thể làm liên kết những điều này? Tôi có phải sử dụng khôngany (ẩn / tường minh) không?

Tôi đã thử một cái gì đó như thế này, nhưng nó không hoạt động (lỗi thời gian biên dịch):

public myCallback: ();
// or:
public myCallback: function;

Tôi không thể tìm thấy bất kỳ lời giải thích nào cho việc này trực tuyến, vì vậy tôi hy vọng bạn có thể giúp tôi.

Câu trả lời:


211

Tôi vừa tìm thấy thứ gì đó trong đặc tả ngôn ngữ TypeScript, nó khá dễ. Tôi đã khá thân thiết.

cú pháp như sau:

public myCallback: (name: type) => returntype;

Trong ví dụ của tôi, nó sẽ là

class CallbackTest
{
    public myCallback: () => void;

    public doWork(): void
    {
        //doing some work...
        this.myCallback(); //calling callback
    }
}

8
Tôi không hiểu tại sao tên tham số được yêu cầu để xác định chữ ký gọi lại ...
2grit

4
Tôi đoán đó có thể là một số di sản văn hóa từ đội C # , tôi nghĩ rằng tôi thích nó sau tất cả ...
2grit

@nikeee bạn có thể cung cấp một liên kết đến trang tài liệu đó không?
jcairney

Đây có thể là một liên kết tốt fettblog.eu/typescript-substitutability
nilakantha singh deo

147

Để tiến thêm một bước, bạn có thể khai báo một con trỏ kiểu tới chữ ký hàm như:

interface myCallbackType { (myArgument: string): void }

và sử dụng nó như thế này:

public myCallback : myCallbackType;

9
Đây là (IMO) một giải pháp tốt hơn nhiều so với câu trả lời được chấp nhận, bởi vì nó cho phép bạn xác định một loại và sau đó, chuyển một tham số của loại đó (gọi lại) mà sau đó bạn có thể sử dụng bất kỳ cách nào bạn muốn, kể cả gọi nó. Câu trả lời được chấp nhận sử dụng biến thành viên và bạn phải đặt biến thành viên cho hàm của mình, sau đó gọi một phương thức - xấu và dễ bị lỗi, bởi vì đặt biến trước là một phần của hợp đồng gọi phương thức.
David

Nó cũng cho phép bạn dễ dàng thiết lập các callback như nullable, ví dụ:let callback: myCallbackType|null = null;
Doches

1
Lưu ý rằng TSLint sẽ khiếu nại "TSLint: Giao diện chỉ có chữ ký cuộc gọi - type MyHandler = (myArgument: string) => voidthay vào đó hãy sử dụng (loại có thể gọi được)" ; xem câu trả lời của TSV
Arjan

Dự thảo trước đó của câu trả lời này thực sự đã giải quyết vấn đề dẫn tôi đến câu hỏi này. Tôi đã cố gắng xác định một chữ ký hàm đủ cho phép trong một giao diện có thể chấp nhận bất kỳ số lượng tham số nào mà không tạo ra lỗi biên dịch. Câu trả lời trong trường hợp của tôi là sử dụng ...args: any[]. Ví dụ: giao diện xuất MyInterface {/ ** Hàm gọi lại. / gọi lại: (... args: any []) => any, / * Tham số cho chức năng gọi lại. * / callbackParams: bất kỳ []}
Ken Lyon

61

Bạn có thể khai báo một loại mới:

declare type MyHandler = (myArgument: string) => void;

var handler: MyHandler;

Cập nhật.

Các declaretừ khóa là không cần thiết. Nó nên được sử dụng trong các tệp .d.ts hoặc trong các trường hợp tương tự.


Tôi tìm tài liệu cho việc này ở đâu?
E. Sundin

@ E.Sundin - Phần "Loại bí danh" của typeclang.org/docs/handbook/advified-types.html
TSV

1
Mặc dù đúng và rất tốt để biết, cùng một trang (ngày nay) cũng nêu rõ "Vì một thuộc tính lý tưởng của phần mềm đang được mở rộng, bạn nên luôn luôn sử dụng giao diện trên một bí danh loại nếu có thể."
Arjan

@Arjan - Tôi hoàn toàn đồng ý với điều này cho các đối tượng. Bạn có thể vui lòng chỉ định - bạn muốn mở rộng một chức năng như thế nào?
TSV

Lưu ý rằng khai báo kiểu là tùy chọn: var handler: (myArgument: string) => voidcó giá trị cú pháp (nếu hơi lộn xộn).
Hutch

35

Dưới đây là một ví dụ - chấp nhận không có tham số và không trả lại gì.

class CallbackTest
{
    public myCallback: {(): void;};

    public doWork(): void
    {
        //doing some work...
        this.myCallback(); //calling callback
    }
}

var test = new CallbackTest();
test.myCallback = () => alert("done");
test.doWork();

Nếu bạn muốn chấp nhận một tham số, bạn cũng có thể thêm tham số đó:

public myCallback: {(msg: string): void;};

Và nếu bạn muốn trả về một giá trị, bạn cũng có thể thêm vào đó:

public myCallback: {(msg: string): number;};

Về mặt chức năng, chúng giống hệt nhau - chúng xác định cùng một thứ và cung cấp cho bạn loại kiểm tra trên chữ ký hàm. Bạn có thể sử dụng bất cứ điều gì bạn thích. Các thông số kỹ thuật nói rằng họ là exactly equivalent.
Fenton

6
@nikeee: Câu hỏi khá khác với câu trả lời của bạn? Steve đăng câu trả lời của bạn trước bạn.
jgauffin

@jgauffin Thật vậy, kết quả là như nhau. IMO giải pháp tôi đã đăng là tự nhiên hơn khi nói về các cuộc gọi lại, vì phiên bản của Steve cho phép định nghĩa toàn bộ giao diện. Nó phụ thuộc vào sở thích của bạn.
nikeee

@Fenton bạn có thể cung cấp một liên kết đến tài liệu đó xin vui lòng?
jcairney

17

Nếu bạn muốn một chức năng chung, bạn có thể sử dụng như sau. Mặc dù nó dường như không được ghi nhận ở bất cứ đâu.

class CallbackTest {
  myCallback: Function;
}   

3

Bạn có thể sử dụng như sau:

  1. Nhập bí danh (sử dụng type từ khóa, đặt bí danh một hàm theo nghĩa đen)
  2. Giao diện
  3. Chức năng

Dưới đây là một ví dụ về cách sử dụng chúng:

type myCallbackType = (arg1: string, arg2: boolean) => number;

interface myCallbackInterface { (arg1: string, arg2: boolean): number };

class CallbackTest
{
    // ...

    public myCallback2: myCallbackType;
    public myCallback3: myCallbackInterface;
    public myCallback1: (arg1: string, arg2: boolean) => number;

    // ...

}

1

Tôi đã gặp lỗi tương tự khi cố gắng thêm cuộc gọi lại vào một người nghe sự kiện. Thật kỳ lạ, việc đặt loại gọi lại thành EventListener đã giải quyết nó. Nó trông thanh lịch hơn so với việc xác định toàn bộ chữ ký hàm là một loại, nhưng tôi không chắc liệu đây có phải là cách chính xác để làm điều này không.

class driving {
    // the answer from this post - this works
    // private callback: () => void; 

    // this also works!
    private callback:EventListener;

    constructor(){
        this.callback = () => this.startJump();
        window.addEventListener("keydown", this.callback);
    }

    startJump():void {
        console.log("jump!");
        window.removeEventListener("keydown", this.callback);
    }
}

thích nó Nhưng đâu là lớp khác trong hành động?
Yaro

1

Tôi hơi muộn, nhưng, từ trước đây trong TypeScript, bạn có thể xác định loại gọi lại bằng

type MyCallback = (KeyboardEvent) => void;

Ví dụ sử dụng:

this.addEvent(document, "keydown", (e) => {
    if (e.keyCode === 1) {
      e.preventDefault();
    }
});

addEvent(element, eventName, callback: MyCallback) {
    element.addEventListener(eventName, callback, false);
}
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.