Definizione del tipo di callback TypeScript


173

Ho la seguente classe in TypeScript:

class CallbackTest
{
    public myCallback;

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

Sto usando la classe in questo modo:

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

Il codice funziona, quindi visualizza una finestra di messaggio come previsto.

La mia domanda è: esiste un tipo che posso fornire per il mio campo di classe myCallback? In questo momento, il campo pubblico myCallbackè di tipo anycome mostrato sopra. Come posso definire la firma del metodo del callback? O posso semplicemente impostare il tipo su un tipo di callback-type? O posso fare il nether di questi? Devo usare any(implicito / esplicito)?

Ho provato qualcosa del genere, ma non ha funzionato (errore di compilazione):

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

Non sono riuscito a trovare alcuna spiegazione online, quindi spero che tu mi possa aiutare.

Risposte:


212

Ho appena trovato qualcosa nelle specifiche del linguaggio TypeScript, è abbastanza semplice. Ero abbastanza vicino.

la sintassi è la seguente:

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

Nel mio esempio, lo sarebbe

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

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

8
Non capisco perché il nome del parametro sia necessario per definire la firma di callback ...
2grit

4
Immagino che potrebbe essere un patrimonio culturale del team C # , penso che mi piaccia dopo tutto ...
2grit

@nikeee puoi fornire un link a quella pagina della documentazione?
jcairney,

Questo può essere un buon collegamento fettblog.eu/typescript-substitutability
nilakantha singh deo

148

Per fare un ulteriore passo, è possibile dichiarare un puntatore tipo a una firma di funzione come:

interface myCallbackType { (myArgument: string): void }

e usalo così:

public myCallback : myCallbackType;

9
Questa (IMO) è una soluzione molto migliore della risposta accettata, perché ti consente di definire un tipo e quindi, diciamo, passare un parametro di quel tipo (il callback) che puoi quindi usare come vuoi, incluso chiamarlo. La risposta accettata utilizza una variabile membro e devi impostare la variabile membro sulla tua funzione, quindi chiamare un metodo - brutto e soggetto a errori, perché impostare prima la variabile fa parte del contratto di chiamata del metodo.
David,

Inoltre, consente di impostare facilmente la richiamata su nullable, ad eslet callback: myCallbackType|null = null;
Doches

1
Nota che TSLint si lamenterebbe "TSLint: Interface ha solo una firma di chiamata - usa type MyHandler = (myArgument: string) => voidinvece. (Callable-types)" ; vedi la risposta di TSV
Arjan,

La precedente bozza di questa risposta ha risolto il problema che mi ha portato a questa domanda. Avevo cercato di definire una firma di funzione abbastanza permissiva all'interno di un'interfaccia che potesse accettare un numero qualsiasi di parametri senza produrre un errore del compilatore. La risposta nel mio caso era usare ...args: any[]. Esempio: export interface MyInterface {/ ** Una funzione di callback. / callback: (... args: any []) => any, / * Parametri per la funzione di callback. * / callbackParams: any []}
Ken Lyon,

62

Puoi dichiarare un nuovo tipo:

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

var handler: MyHandler;

Aggiornare.

La declareparola chiave non è necessaria. Dovrebbe essere usato nei file .d.ts o in casi simili.


Dove trovo la documentazione per questo?
E. Sundin,

@ E.Sundin - Sezione "Type Aliases" del typescriptlang.org/docs/handbook/advanced-types.html
TSV

1
Sebbene sia vera e piacevole da sapere, la stessa pagina (al giorno d'oggi) afferma anche "Poiché una proprietà ideale del software è aperta alle estensioni, è necessario utilizzare sempre un'interfaccia su un alias di tipo, se possibile."
Arjan,

@Arjan - Sono assolutamente d'accordo con questo per gli oggetti. Potresti specificare: come si desidera estendere una funzione?
TSV,

Si noti che la dichiarazione del tipo è facoltativa: var handler: (myArgument: string) => voidè sintatticamente valida (se un po 'disordinata).
Hutch,

36

Ecco un esempio: non accettare parametri e non restituire nulla.

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

Se si desidera accettare un parametro, è possibile aggiungere anche quello:

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

E se si desidera restituire un valore, è possibile aggiungere anche quello:

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

Funzionalmente sono identici: definiscono la stessa cosa e ti danno il controllo del tipo sulla firma della funzione. Puoi usare quello che preferisci. Le specifiche dicono che lo sono exactly equivalent.
Fenton,

6
@nikeee: La domanda è piuttosto cosa c'è di diverso nella tua risposta? Steve ha pubblicato la sua risposta prima della tua.
Jgauffin,

@jgauffin In effetti, il risultato è lo stesso. IMO la soluzione che ho pubblicato è più naturale quando si parla di callback, poiché la versione di Steve consente di definire intere interfacce. Dipende dalle tue preferenze.
nikeee,

@Fenton potresti fornire un link a tale documentazione per favore?
jcairney,

18

Se si desidera una funzione generica, è possibile utilizzare quanto segue. Anche se non sembra essere documentato da nessuna parte.

class CallbackTest {
  myCallback: Function;
}   

4

È possibile utilizzare quanto segue:

  1. Digitare alias (usando la typeparola chiave, alias di una funzione letterale)
  2. Interfaccia
  3. Funzione letterale

Ecco un esempio di come usarli:

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

Mi sono imbattuto nello stesso errore durante il tentativo di aggiungere il callback a un listener di eventi. Stranamente, risolvendo il tipo di callback su EventListener. Sembra più elegante della definizione di una firma di intera funzione come tipo, ma non sono sicuro che questo sia il modo corretto per farlo.

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

mi piace. Ma dov'è in azione l'altra classe?
Yaro

2

Sono un po 'in ritardo, ma da qualche tempo in TypeScript è possibile definire il tipo di callback con

type MyCallback = (KeyboardEvent) => void;

Esempio di utilizzo:

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

addEvent(element, eventName, callback: MyCallback) {
    element.addEventListener(eventName, callback, false);
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.