การกำหนดประเภทการโทรกลับ TypeScript


172

ฉันมีคลาสต่อไปนี้ใน TypeScript:

class CallbackTest
{
    public myCallback;

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

ฉันใช้คลาสเช่นนี้:

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

รหัสทำงานได้ดังนั้นจึงแสดงกล่องข้อความตามที่คาดไว้

คำถามของฉันคือ: มีวิธีใดที่ฉันสามารถให้ข้อมูลชั้นเรียนของฉันmyCallback? ตอนนี้ฟิลด์สาธารณะmyCallbackเป็นประเภทanyที่แสดงด้านบน ฉันจะกำหนดลายเซ็นวิธีการโทรกลับได้อย่างไร หรือฉันจะตั้งค่าประเภทการโทรกลับบางประเภท? หรือฉันสามารถทำสิ่งเหล่านี้ได้หรือไม่ ฉันต้องใช้any(โดยนัย / ชัดเจน) หรือไม่

ฉันลองอะไรเช่นนี้ แต่มันไม่ทำงาน (ข้อผิดพลาดในการคอมไพล์เวลา):

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

ฉันไม่พบคำอธิบายเกี่ยวกับออนไลน์นี้ดังนั้นฉันหวังว่าคุณจะสามารถช่วยฉันได้

คำตอบ:


211

ฉันเพิ่งพบบางอย่างในข้อกำหนดภาษาของ TypeScript มันค่อนข้างง่าย ฉันสนิทกันมาก

ไวยากรณ์มีดังต่อไปนี้:

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

ในตัวอย่างของฉันมันจะเป็น

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

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

8
ฉันไม่ได้รับทำไมชื่อพารามิเตอร์จะต้องกำหนดลายเซ็นโทรกลับ ...
2grit

4
ฉันคิดว่ามันอาจเป็นมรดกทางวัฒนธรรมจากทีม C #ฉันคิดว่าฉันชอบมันหลังจากทั้งหมด ...
2grit

@nikeee คุณสามารถให้ลิงค์ไปยังหน้าเอกสารนั้นได้หรือไม่?
jcairney

นี่อาจเป็นลิงค์ที่ดีfettblog.eu/typescript-substitutability
nilakantha singh deo

147

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

interface myCallbackType { (myArgument: string): void }

และใช้มันเช่นนี้

public myCallback : myCallbackType;

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

นอกจากนี้ยังช่วยให้คุณตั้งค่าการโทรกลับได้อย่างง่ายดายเช่นlet callback: myCallbackType|null = null;
Doches

1
โปรดทราบว่า TSLint จะบ่น"TSLint: ส่วนต่อประสานมีเพียงลายเซ็นการโทร - ใช้type MyHandler = (myArgument: string) => voidแทน (callable-types)" ; ดูคำตอบของ TSV
Arjan

แบบร่างก่อนหน้าของคำตอบนี้แก้ปัญหาที่นำฉันมาที่คำถามนี้จริง ๆ ฉันพยายามกำหนดลายเซ็นฟังก์ชั่นที่ได้รับอนุญาตเพียงพอภายในอินเตอร์เฟสที่สามารถรับพารามิเตอร์จำนวนใด ๆ โดยไม่ทำให้เกิดข้อผิดพลาดของคอมไพเลอร์ ...args: any[]คำตอบในกรณีของฉันคือการใช้ ตัวอย่าง: อินเตอร์เฟสการส่งออก MyInterface {/ ** ฟังก์ชั่นการโทรกลับ / callback: (... args: any []) => any, / *พารามิเตอร์สำหรับฟังก์ชันการเรียกกลับ * / callbackParams: ใด ๆ []}
Ken Lyon

61

คุณสามารถประกาศประเภทใหม่:

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

var handler: MyHandler;

ปรับปรุง

declareคำหลักที่ไม่จำเป็น ควรใช้ในไฟล์. d.ts หรือในกรณีที่คล้ายกัน


ฉันจะหาเอกสารของสิ่งนี้ได้จากที่ไหน?
E. Sundin

@ E.Sundin - ส่วน "นามแฝงประเภท" ของ typescriptlang.org/docs/handbook/advanced-types.html
TSV

1
ในขณะที่เป็นเรื่องจริงและน่ารู้หน้าเดียวกัน (ทุกวันนี้) ก็กล่าวว่า"เนื่องจากคุณสมบัติในอุดมคติของซอฟต์แวร์กำลังเปิดให้มีการขยายคุณควรใช้ส่วนต่อประสานกับนามแฝงประเภทถ้าเป็นไปได้"
Arjan

@Arjan - ฉันเห็นด้วยกับสิ่งนี้ทั้งหมดสำหรับวัตถุ คุณกรุณาระบุ - คุณต้องการขยายฟังก์ชั่นอย่างไร?
TSV

โปรดทราบว่าการประกาศประเภทเป็นทางเลือก: var handler: (myArgument: string) => voidมีความถูกต้องทางไวยากรณ์ (ถ้าบิตยุ่งเหยิง)
ฮัทช์

35

นี่คือตัวอย่าง - การยอมรับพารามิเตอร์และไม่ส่งคืนอะไร

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();

หากคุณต้องการยอมรับพารามิเตอร์คุณสามารถเพิ่มได้ด้วย:

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

และถ้าคุณต้องการคืนค่าคุณสามารถเพิ่มได้ด้วย:

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

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

6
@nikeee: คำถามนี้มีความแตกต่างกับคำตอบของคุณอย่างไร Steve โพสต์คำตอบของคุณต่อหน้าคุณ
jgauffin

@jgauffin แท้จริงแล้วผลลัพธ์จะเหมือนกัน IMO โซลูชันที่ฉันโพสต์นั้นดูเป็นธรรมชาติมากขึ้นเมื่อพูดถึงการโทรกลับเนื่องจากรุ่นของ Steve ช่วยให้คำจำกัดความอินเตอร์เฟสทั้งหมด มันขึ้นอยู่กับความต้องการของคุณ
nikeee

@Fenton คุณสามารถให้ลิงค์ไปยังเอกสารนั้นได้ไหม?
jcairney

17

หากคุณต้องการฟังก์ชั่นทั่วไปคุณสามารถใช้ดังต่อไปนี้ แม้ว่ามันจะไม่ได้รับการบันทึกไว้ที่ใดก็ตาม

class CallbackTest {
  myCallback: Function;
}   

3

คุณสามารถใช้สิ่งต่อไปนี้:

  1. พิมพ์นามแฝง (ใช้ typeคำหลักนามแฝงของฟังก์ชันตามตัวอักษร)
  2. อินเตอร์เฟซ
  3. ตัวอักษรฟังก์ชั่น

นี่คือตัวอย่างของวิธีการใช้งาน:

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

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

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

ชอบมัน. แต่ชั้นเรียนอื่น ๆ อยู่ที่ไหน?
Yaro

1

ฉันช้าไปหน่อย แต่เมื่อไม่นานมานี้ใน TypeScript คุณสามารถกำหนดประเภทการโทรกลับได้

type MyCallback = (KeyboardEvent) => void;

ตัวอย่างการใช้งาน:

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

addEvent(element, eventName, callback: MyCallback) {
    element.addEventListener(eventName, callback, false);
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.