Definindo o tipo de retorno de chamada TypeScript


172

Eu tenho a seguinte classe no TypeScript:

class CallbackTest
{
    public myCallback;

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

Estou usando a classe assim:

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

O código funciona e, portanto, exibe uma caixa de mensagens conforme o esperado.

Minha pergunta é: Existe algum tipo que eu possa fornecer para o meu campo de classe myCallback? No momento, o campo público myCallbacké do tipo anyconforme mostrado acima. Como posso definir a assinatura do método do retorno de chamada? Ou posso apenas definir o tipo para algum tipo de retorno de chamada? Ou posso fazer nada disso? Eu tenho que usar any(implícito / explícito)?

Eu tentei algo assim, mas não funcionou (erro em tempo de compilação):

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

Não encontrei nenhuma explicação para isso on-line, então espero que você possa me ajudar.

Respostas:


211

Acabei de encontrar algo na especificação da linguagem TypeScript, é bastante fácil. Eu estava bem perto.

a sintaxe é a seguinte:

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

No meu exemplo, seria

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

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

8
Eu não entendo por que o nome do parâmetro é necessário para definir a assinatura de retorno ...
2grit

4
Eu acho que pode ser alguma herança cultural da equipe de C # , acho que gosto de tudo ...
2grit

@nikeee, você pode fornecer um link para essa página da documentação?
jcairney


147

Para ir um passo adiante, você pode declarar um ponteiro de tipo para uma assinatura de função como:

interface myCallbackType { (myArgument: string): void }

e use-o assim:

public myCallback : myCallbackType;

9
Essa é (IMO) uma solução muito melhor do que a resposta aceita, porque permite definir um tipo e, por exemplo, passar um parâmetro desse tipo (o retorno de chamada) que você pode usar da maneira que quiser, incluindo chamá-lo. A resposta aceita usa uma variável de membro e você deve definir a variável de membro para sua função e chamar um método - feio e propenso a erros, porque definir a variável primeiro faz parte do contrato de chamar o método.
David

Também permite definir facilmente o retorno de chamada como anulável, por exemplo:let callback: myCallbackType|null = null;
Doches

1
Observe que o TSLint reclamaria "TSLint: Interface possui apenas uma assinatura de chamada - use em type MyHandler = (myArgument: string) => voidvez disso. (Tipos de chamada)" ; veja a resposta da TSV
Arjan

O rascunho anterior desta resposta realmente resolveu o problema que me levou a essa pergunta. Eu estava tentando definir uma assinatura de função permissiva o suficiente dentro de uma interface que pudesse aceitar qualquer número de parâmetros sem produzir um erro do compilador. A resposta no meu caso foi usar ...args: any[]. Exemplo: interface de exportação MyInterface {/ ** Uma função de retorno de chamada. / callback: (... args: any []) => any, / * Parâmetros para a função de retorno de chamada. * / callbackParams: any []}
Ken Lyon

61

Você pode declarar um novo tipo:

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

var handler: MyHandler;

Atualizar.

A declarepalavra-chave não é necessária. Ele deve ser usado nos arquivos .d.ts ou em casos semelhantes.


Onde encontro a documentação para isso?
E. Sundin

@ E.Sundin - Seção "Aliases de tipo" do typescriptlang.org/docs/handbook/advanced-types.html
TSV

1
Embora seja verdade e seja bom saber, a mesma página (atualmente) também declara "Como uma propriedade ideal do software está sendo aberta para extensão, você sempre deve usar uma interface sobre um alias de tipo, se possível".
Arjan #

@ Arjan - eu concordo totalmente com isso para objetos. Você poderia especificar - como deseja estender uma função?
TSV

Observe que a declaração de tipo é opcional: var handler: (myArgument: string) => voidé sintaticamente válida (se um pouco bagunçada).
Hutch

35

Aqui está um exemplo - não aceitando parâmetros e retornando 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();

Se você deseja aceitar um parâmetro, também pode adicioná-lo:

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

E se você deseja retornar um valor, você pode adicionar também:

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

Funcionalmente, eles são idênticos - eles definem a mesma coisa e permitem a verificação de tipo na assinatura da função. Você pode usar o que preferir. A especificação diz que eles são exactly equivalent.
Fenton

6
@nikeee: A questão é o que há de diferente na sua resposta? Steve postou sua resposta antes da sua.
jgauffin

@jgauffin De fato, o resultado é o mesmo. Na IMO, a solução que publiquei é mais natural quando se trata de retornos de chamada, pois a versão de Steve permite definições de interface inteiras. Depende da sua preferência.
Nikeee

@Fenton, você poderia fornecer um link para essa documentação, por favor?
jcairney

17

Se você deseja uma função genérica, pode usar o seguinte. Embora não pareça estar documentado em nenhum lugar.

class CallbackTest {
  myCallback: Function;
}   

3

Você pode usar o seguinte:

  1. Digite Alias ​​(usando a typepalavra-chave, alias uma literal de função)
  2. Interface
  3. Função Literal

Aqui está um exemplo de como usá-los:

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;

    // ...

}

1

Me deparei com o mesmo erro ao tentar adicionar o retorno de chamada a um ouvinte de evento. Estranhamente, a configuração do tipo de retorno de chamada como EventListener o resolveu. Parece mais elegante do que definir uma assinatura de função inteira como um tipo, mas não tenho certeza se essa é a maneira correta de fazer isso.

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

gosto disso. Mas onde está a outra classe em ação?
Yaro

1

Estou um pouco atrasado, mas, já há algum tempo, no TypeScript, você pode definir o tipo de retorno de chamada com

type MyCallback = (KeyboardEvent) => void;

Exemplo de uso:

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

addEvent(element, eventName, callback: MyCallback) {
    element.addEventListener(eventName, callback, false);
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.