Definición del tipo de devolución de llamada de TypeScript

173

Tengo la siguiente clase en TypeScript:

class CallbackTest
{
    public myCallback;

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

Estoy usando la clase así:

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

El código funciona, por lo que muestra un cuadro de mensaje como se esperaba.

Mi pregunta es: ¿Hay algún tipo que pueda proporcionar para mi campo de clase myCallback? En este momento, el campo público myCallbackes del tipo anyque se muestra arriba. ¿Cómo puedo definir la firma del método de la devolución de llamada? ¿O puedo establecer el tipo en algún tipo de devolución de llamada? ¿O puedo hacer menos de estos? ¿Tengo que usar any(implícito / explícito)?

Intenté algo como esto, pero no funcionó (error en tiempo de compilación):

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

No pude encontrar ninguna explicación a esto en línea, así que espero que me puedan ayudar.

nikeee
fuente

Respuestas:

212

Acabo de encontrar algo en la especificación del lenguaje TypeScript, es bastante fácil. Estaba bastante cerca.

La sintaxis es la siguiente:

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

En mi ejemplo, sería

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

    public doWork(): void
    {
        //doing some work...
        this.myCallback(); //calling callback
    }
}
nikeee
fuente
8
No entiendo por qué se requiere el nombre del parámetro para definir la firma de devolución de llamada ...
2grit
44
Supongo que podría ser una herencia cultural del equipo de C # , creo que me gusta después de todo ...
2grit
@nikeee, ¿puede proporcionar un enlace a esa página de la documentación?
jcairney
Este puede ser un buen enlace fettblog.eu/typescript-substitutability
nilakantha singh deo
148

Para ir un paso más allá, puede declarar un puntero de tipo a una firma de función como:

interface myCallbackType { (myArgument: string): void }

y úsalo así:

public myCallback : myCallbackType;
Leng
fuente
9
Esta es (IMO) una solución mucho mejor que la respuesta aceptada, ya que le permite definir un tipo y luego, por ejemplo, pasar un parámetro de ese tipo (la devolución de llamada) que luego puede usar de la forma que desee, incluso llamarlo. La respuesta aceptada usa una variable miembro y debe establecer la variable miembro en su función, luego llamar a un método, feo y propenso a errores, porque establecer la variable primero es parte del contrato de llamar al método.
David
También le permite configurar fácilmente la devolución de llamada como anulable, por ejemplolet callback: myCallbackType|null = null;
Doches
1
Tenga en cuenta que TSLint se quejaría "TSLint: la interfaz solo tiene una firma de llamada; use type MyHandler = (myArgument: string) => voiden su lugar. (Tipos invocables)" ; ver la respuesta de TSV
Arjan
El borrador anterior de esta respuesta en realidad resolvió el problema que me llevó a esta pregunta. Había estado tratando de definir una firma de función suficientemente permisiva dentro de una interfaz que pudiera aceptar cualquier número de parámetros sin producir un error del compilador. La respuesta en mi caso fue usar ...args: any[]. Ejemplo: interfaz de exportación MyInterface {/ ** Una función de devolución de llamada. / callback: (... args: any []) => any, / * Parámetros para la función de devolución de llamada. * / callbackParams: any []}
Ken Lyon
62

Puedes declarar un nuevo tipo:

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

var handler: MyHandler;

Actualizar.

La declarepalabra clave no es necesaria. Debe usarse en los archivos .d.ts o en casos similares.

TSV
fuente
¿Dónde encuentro la documentación para esto?
E. Sundin
@ E.Sundin - Sección "Alias ​​de tipo" del typescriptlang.org/docs/handbook/advanced-types.html
TSV
1
Si bien es cierto y es bueno saberlo, la misma página (hoy en día) también dice "Debido a que una propiedad ideal del software está abierta a la extensión, siempre debe usar una interfaz sobre un alias de tipo si es posible".
Arjan
@Arjan: estoy totalmente de acuerdo con esto para los objetos. ¿Podría especificar, por favor, cómo desea ampliar una función?
TSV
Tenga en cuenta que la declaración de tipo es opcional: var handler: (myArgument: string) => voides sintácticamente válida (si está un poco desordenada).
Hutch
36

Aquí hay un ejemplo: no aceptar parámetros y no devolver nada.

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

Si desea aceptar un parámetro, también puede agregarlo:

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

Y si desea devolver un valor, también puede agregarlo:

public myCallback: {(msg: string): number;};
Fenton
fuente
Funcionalmente son idénticos: definen lo mismo y le permiten verificar el tipo en la firma de la función. Puedes usar lo que prefieras. La especificación dice que sí exactly equivalent.
Fenton
66
@nikeee: La pregunta es, ¿qué es diferente con tu respuesta? Steve publicó su respuesta antes que la tuya.
jgauffin
@jgauffin De hecho, el resultado es el mismo. En mi opinión, la solución que publiqué es más natural cuando hablamos de devoluciones de llamada, ya que la versión de Steve permite definiciones de interfaz completas. Eso depende de tu preferencia.
nikeee
@Fenton, ¿podría proporcionar un enlace a esa documentación, por favor?
jcairney
18

Si desea una función genérica, puede usar lo siguiente. Aunque no parece estar documentado en ningún lado.

class CallbackTest {
  myCallback: Function;
}   
Ceniza azul
fuente
4

Puedes usar lo siguiente:

  1. Escriba Alias ​​(usando la typepalabra clave, aliasing una función literal)
  2. Interfaz
  3. Función literal

Aquí hay un ejemplo de cómo usarlos:

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;

    // ...

}
Willem van der Veen
fuente
2

Encontré el mismo error al intentar agregar la devolución de llamada a un detector de eventos. Curiosamente, establecer el tipo de devolución de llamada en EventListener lo resolvió. Parece más elegante que definir una firma de función completa como un tipo, pero no estoy seguro de si esta es la forma correcta de hacerlo.

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
fuente
gusta. ¿Pero dónde está la otra clase en acción?
Yaro
2

Llego un poco tarde, pero desde hace algún tiempo en TypeScript puede definir el tipo de devolución de llamada con

type MyCallback = (KeyboardEvent) => void;

Ejemplo de uso:

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

addEvent(element, eventName, callback: MyCallback) {
    element.addEventListener(eventName, callback, false);
}
Daniel
fuente