260

I've got the following class in TypeScript:

class CallbackTest
{
    public myCallback;

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

I am using the class like this:

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

The code works, so it displays a messagebox as expected.

My question is: Is there any type I can provide for my class field myCallback? Right now, the public field myCallback is of type any as shown above. How can I define the method signature of the callback? Or can I just set the type to some kind of callback-type? Or can I do nether of these? Do I have to use any (implicit/explicit)?

I tried something like this, but it did not work (compile-time error):

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

I couldn't find any explanation to this online, so I hope you can help me.

nikeee
  • 10,248
  • 7
  • 40
  • 66

11 Answers11

306

I just found something in the TypeScript language specification, it's fairly easy. I was pretty close.

the syntax is the following:

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

In my example, it would be

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

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

As a type alias:

type MyCallback = (name: type) => returntype;
nikeee
  • 10,248
  • 7
  • 40
  • 66
186

To go one step further, you could declare a type pointer to a function signature like:

interface myCallbackType { (myArgument: string): void }

and use it like this:

public myCallback : myCallbackType;
Berik
  • 7,816
  • 2
  • 32
  • 40
Leng
  • 1,979
  • 1
  • 13
  • 7
  • 10
    This is (IMO) a much better solution than the accepted answer, because it lets you define a type and then, say, pass a parameter of that type (the callback) which you can then use any way you want, including calling it. The accepted answer uses a member variable and you have to set the member variable to your function, then call a method - ugly and prone to errors, because setting the variable first is part of the contract of calling the method. – David Apr 17 '15 at 13:05
  • It also lets you easily set the callback as nullable, e.g. `let callback: myCallbackType|null = null;` – Doches Oct 21 '16 at 13:25
  • 3
    Note that TSLint would complain _"TSLint: Interface has only a call signature — use `type MyHandler = (myArgument: string) => void` instead. (callable-types)"_; see [TSV's answer](https://stackoverflow.com/questions/13137350/defining-typescript-callback-type/33173494#33173494) – Arjan Aug 01 '17 at 09:40
  • 1
    The earlier draft of this answer actually solved the problem that led me to this question. I had been trying to define a permissive enough function signature within an interface that could accept any number of parameters without producing a compiler error. The answer in my case was to use `...args: any[]`. Example: export interface MyInterface { /** A callback function. */ callback: (...args: any[]) => any, /** Parameters for the callback function. */ callbackParams: any[] } – Ken Lyon Sep 19 '17 at 17:53
73

You can declare a new type:

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

var handler: MyHandler;

Update.

The declare keyword is not necessary. It should be used in the .d.ts files or in similar cases.

TSV
  • 7,538
  • 1
  • 29
  • 37
  • Where do I find the documentation for this? – E. Sundin Oct 12 '16 at 16:47
  • @E.Sundin - Section "Type Aliases" of the http://www.typescriptlang.org/docs/handbook/advanced-types.html – TSV Oct 13 '16 at 04:20
  • 1
    While true and nice to know, the same page (nowadays) also states _"Because an ideal property of software is being open to extension, you should always use an interface over a type alias if possible."_ – Arjan Oct 17 '16 at 13:03
  • @Arjan - I'm totally agree with this for objects. Could you please specify - how do you want to extend a function? – TSV Oct 18 '16 at 06:41
  • Note that the type declaration is optional: `var handler: (myArgument: string) => void` is syntactically valid (if a bit messy). – Hutch May 26 '17 at 20:32
  • ...a bit late, @TSV: using an interface like stated in my earlier comment actually also makes TSLint complain _"TSLint: Interface has only a call signature — use `type MyHandler = (myArgument: string) => void` instead. (callable-types)"_ So, indeed, my earlier comment is moot and your `declare type` is preferred over using an interface. – Arjan Aug 01 '17 at 09:38
  • Is "declare" keyword required here? Isn't "declare" used for type definition to describe existing classes or variables that are defined externally? – sudip Aug 02 '17 at 19:09
  • for my context (declaring a type inside a function as a local delegate) the declare keyword was an error. `type MyHandler = (myArgument: string) => void;` worked – Maslow Aug 15 '17 at 14:46
  • So is there a difference between using a type declaration and an interface definition? – Bruno Zell Apr 20 '18 at 11:42
  • Not really, interface gives better code completion. But you don't have to declare anything, since the type is actually `EventListener`. – Kokodoko Jun 13 '18 at 20:58
45

Here is an example - accepting no parameters and returning nothing.

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

If you want to accept a parameter, you can add that too:

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

And if you want to return a value, you can add that also:

public myCallback: {(msg: string): number;};
Fenton
  • 241,084
  • 71
  • 387
  • 401
  • Functionally they are identical - they define the same thing and give you type checking on the function signature. You can use whichever you prefer. The spec says they are `exactly equivalent`. – Fenton Oct 30 '12 at 10:59
  • 6
    @nikeee: The question is rather what's different with your answer? Steve posted his answer before yours. – jgauffin Jun 23 '14 at 18:37
  • @jgauffin Indeed, the result is the same. IMO the solution I posted is more natural when talking about callbacks, since Steve's version allows whole interface definitions. It depends on your preference. – nikeee Jun 23 '14 at 21:45
  • @Fenton could you provide a link to that documentation please? – jcairney Dec 27 '17 at 16:37
25

If you want a generic function you can use the following. Although it doesn't seem to be documented anywhere.

class CallbackTest {
  myCallback: Function;
}   
Ash Blue
  • 5,344
  • 5
  • 30
  • 36
8

You can use the following:

  1. Type Alias (using type keyword, aliasing a function literal)
  2. Interface
  3. Function Literal

Here is an example of how to use them:

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;

    // ...

}
nikeee
  • 10,248
  • 7
  • 40
  • 66
Willem van der Veen
  • 33,665
  • 16
  • 190
  • 155
7

I'm a little late, but, since some time ago in TypeScript you can define the type of callback with

type MyCallback = (KeyboardEvent) => void;

Example of use:

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

addEvent(element, eventName, callback: MyCallback) {
    element.addEventListener(eventName, callback, false);
}
Daniel
  • 951
  • 7
  • 20
  • That's indeed what the docs recommend, but if you do that, you can't pass a function that actually returns something. Typescript will complain. This makes using arrow function oneliners less convenient. Thoughts? – w00t Mar 03 '23 at 17:46
3

Here is a simple example of how I define interfaces that include a callback.

// interface containing the callback

interface AmazingInput {
    name: string
    callback: (string) => void  //defining the callback
}

// method being called

public saySomethingAmazing(data:AmazingInput) {
   setTimeout (() => {
     data.callback(data.name + ' this is Amazing!');
   }, 1000)

}

// create a parameter, based on the interface

let input:AmazingInput = {
    name: 'Joe Soap'
    callback: (message) => {
        console.log ('amazing message is:' + message);
    }
}

// call the method, pass in the parameter

saySomethingAmazing(input);

nikeee
  • 10,248
  • 7
  • 40
  • 66
sparkyspider
  • 13,195
  • 10
  • 89
  • 133
  • 2
    In TypeScript you cannot define the type of a function parameter without its name. You cannot do `(string) => void`. It would have to be something like `(param: string) => void` or `(_:string) => void`. The syntax you used is valid in other languages like Dart though. – Javi Marzán Jun 24 '21 at 09:43
2

I came across the same error when trying to add the callback to an event listener. Strangely, setting the callback type to EventListener solved it. It looks more elegant than defining a whole function signature as a type, but I'm not sure if this is the correct way to do this.

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);
    }
}
Kokodoko
  • 26,167
  • 33
  • 120
  • 197
0

This question has answers where everyone have defined types or interfaces. I have defined function with typescript without defining a type.

You can define a function with callback type parameter like below. You can define multiple return values in the type here itself and return multiple data in completion separating with comma and use the same from where you are calling the function.

functionWithCallback = (completion: (returnData: any, someMoreData: any) => void) => {
    completion(["A", "B"], 5);
}

And you can call it like below.

functionWithCallback((returnData: any, someMoreData: any) => {
    //you can use all the data returned from the function here
}

Hope this helps some people.

Mahesh Agrawal
  • 3,348
  • 20
  • 34
-2

This is an example of optional callback function for angular component and service

    maincomponent(){
        const param = "xyz";
       this.service.mainServie(param, (response)=>{
        if(response){console.log("true");}
        else{console.log("false");}
      })
    }

//Service Component
    mainService(param: string, callback?){
      if(string === "xyz"){
        //call restApi 
        callback(true);
      }
    else{
        callback(false);
      }
    }
  • Your example focuses on 99.99% JavaScript, not TypeScript. There is not type attached to your `callback` argument besides declaring it as possibly `undefined` (by suffixing ?: `callback?`). So your `callback`'s type is `any | undefined`. – Paul-Sebastian Manole Jun 25 '21 at 17:49