74

Coming from a C# background, I want to create a datatype that defines a function signature. In C#, this is a delegate declared like this:

delegate void Greeter (string message);

public class Foo
{
    public void SayHi (Greeter g) {
        g("Hi!");
    }
}

Now, I want to achieve similar in Typescript. I know Typescript has no delegate types, but only lambdas. I came up with something like this:

class Foo {
    SayHi (greeter: (msg: String) => void) {
        greeter('Hi!');
    }
}

While this works, I want to reuse the method signature (msg:String) => void couple of times and think it would be cleaner to create a custom type - like the delegate in C#.

Any ideas how this can be done?

Dynalon
  • 6,577
  • 10
  • 54
  • 84
  • possible duplicate of [Are strongly-typed functions as parameters possible in TypeScript?](http://stackoverflow.com/questions/14638990/are-strongly-typed-functions-as-parameters-possible-in-typescript) – WiredPrairie Dec 01 '13 at 12:51

5 Answers5

87

In TypeScript, interfaces can have call signatures. In your example, you could declare it like this:

interface Greeter {
    (message: string): void;
}

function sayHi(greeter: Greeter) {
    greeter('Hello!');
}

sayHi((msg) => console.log(msg)); // msg is inferred as string
Ryan Cavanaugh
  • 209,514
  • 56
  • 272
  • 235
  • 1
    Not quite the same, but the best we've got. I just hate having to declare interfaces all over the place when an anon strongly typed function should do. – Alwyn May 17 '14 at 19:34
  • 1
    Not sure what you mean -- you can have anonymous function types as well. – Ryan Cavanaugh May 17 '14 at 22:30
  • I want to declare a function that takes in a call back delegate eg. Func in c#, how can this be declared in TypeScript? Using the lambda operator is used to declare an instance of a delegate not the type. The type is the anon delegate Func. What's the equivalent syntax? – Alwyn May 19 '14 at 17:28
  • 11
    e.g. `function foo(x: (n: number) => string) { console.log(x(3)); }`, or equivalently `function foo(x: { (n: number): string; }) { ... }` – Ryan Cavanaugh May 19 '14 at 17:37
  • How do you declare and export a function that is an instance of `Greeter` in a way that you get proper intellisense for the greeter function when it is imported in a different file? – Steven Liekens Feb 26 '18 at 08:23
51

You can create something like a delegate by using a type alias:

type MyDelegate = (input: string) => void;

which defines a type name for a function pointer, just as delegates do in C#. The following example uses it in combination with generic type parameters:

type Predicate<T> = (item: T) => boolean;

export class List<T> extends Array<T> {
    constructor(...items: T[]){
        super();
        for(let i of items || []){
            this.push(i);
        }
    }
    public hasAny(predicate?: Predicate<T>): boolean {
        predicate = predicate || (i => true)
        for(let item of this) {
            if(predicate(item)) return true;
        }
        return false;
    }
}
Ian Kemp
  • 28,293
  • 19
  • 112
  • 138
Orphid
  • 2,722
  • 2
  • 27
  • 41
13

Five years and many, many TS versions later I find myself using a simpler type definition for declaring function types:

type Greeter = (msg: string) => void;
const someGreeter: Greeter = (msg: string) => `Hi there with ${msg}`;
Dynalon
  • 6,577
  • 10
  • 54
  • 84
1

Type definition for a callable expression (this is a draft ok, for humans... not a BNF or anything formal):

callableType: (paramsDef) => returnType

paramsDef:    MULTIPLE paramDef SEPARATED BY ,

paramDef:     EITHER   paramName: paramType
                  OR   optionalParamName?: paramTypeWhenDefined
                  OR   ...manyParamName: eachParamType[]

Example:

var func = something as ((...x: any[]) => any);

Then you can:

var result = func("a", "b", 2);
Miguel Angelo
  • 23,796
  • 16
  • 59
  • 82
0

I now publish and use @steelbreeze/delegate; it has a few limitations compared to the C# delegate, in that it's immutable, but otherwise works well (and when called returns results from all the functions called).

It lets you write code such as:

import { create as delegate } from "@steelbreeze/delegate";

function world(s: string) {
    console.log(s + " world");
}

const one = delegate(s => console.log(s + " Hello world"));
const two = delegate(s => console.log(s + " Hello"), world);

one("A");
two("B");

delegate(one, two)("C");
Mesmo
  • 1,890
  • 2
  • 15
  • 13