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.

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

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


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;

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.


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

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

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;

    // ...

}

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

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);
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.