定义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;

我在网上找不到对此的任何解释,希望您能帮助我。

Answers:


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


147

要更进一步,您可以声明一个指向函数签名的类型指针,例如:

interface myCallbackType { (myArgument: string): void }

并像这样使用它:

public myCallback : myCallbackType;

9
这是(IMO)比接受的答案更好的解决方案,因为它使您可以定义一个类型,然后传递该类型的参数(回调),然后您可以使用任何想要的方式,包括调用它。可接受的答案使用成员变量,您必须将成员变量设置为函数,然后调用方法-丑陋且容易出错,因为首先设置变量是调用方法的约定的一部分。
戴维(David)

它还可以让您轻松地将回调设置为可为空,例如let callback: myCallbackType|null = null;
Doches

1
请注意,TSLint会抱怨“ TSLint:接口只有一个呼叫签名-请type MyHandler = (myArgument: string) => void改用。(callable-types)”;请参阅TSV的答案
-Arjan

这个答案的较早草案实际上解决了导致我提出这个问题的问题。我一直试图在一个接口内定义一个足够允许的函数签名,该函数签名可以接受任意数量的参数而不会产生编译器错误。就我而言,答案是使用...args: any[]。示例:export interface MyInterface {/ **回调函数。/ callback:(... args:any [])=> any,/ *回调函数的参数。* / callbackParams:any []}
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在语法上有效(如果有点混乱)。
Hutch

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
芬顿(Fenton)

6
@nikeee:问题在于您的答案有何不同?史蒂夫在您面前发布了他的答案。
jgauffin

@jgauffin确实,结果是一样的。讨论回调时,我发布的IMO解决方案更为自然,因为Steve的版本允许整个接口定义。这取决于您的喜好。
耐克

@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);
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.