Визначення типу зворотного виклику TypeScript


172

У TypeScript у мене такий клас:

class CallbackTest
{
    public myCallback;

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

Я використовую такий клас:

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

Код працює, тому він відображає поле повідомлень, як очікувалося.

Моє запитання: чи є тип, який я можу надати для свого класу myCallback? Зараз публічне поле myCallbackмає тип, anyяк показано вище. Як я можу визначити підпис методу зворотного дзвінка? Або я можу просто встановити тип на якийсь тип зворотного дзвінка? Або я можу зробити це з них? Чи потрібно використовувати any(неявне / явне)?

Я спробував щось подібне, але не вийшло (помилка під час компіляції):

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

Я не зміг знайти жодного пояснення цьому в Інтернеті, тому сподіваюся, ви можете мені допомогти.

Відповіді:


211

Я щойно знайшов щось у специфікації мови TypeScript, це досить просто. Я був досить близько.

синтаксис такий:

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

У моєму прикладі це було б

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

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

8
Я не розумію, чому ім'я параметра потрібне для визначення підпису зворотного дзвінка ...
2grit

4
Я думаю, це може бути якась культурна спадщина команди C # , я думаю, що це мені подобається все-таки ...
2grit

@nikeee Ви можете надати посилання на цю сторінку документації?
jcairney

Це може бути хорошим посиланням fettblog.eu/typescript-substitutability
nilakantha singh

147

Щоб піти на крок далі, ви можете оголосити покажчик типу на підпис функції, наприклад:

interface myCallbackType { (myArgument: string): void }

і використовувати його так:

public myCallback : myCallbackType;

9
Це (IMO) набагато краще рішення, ніж прийнята відповідь, оскільки воно дозволяє визначити тип, а потім, скажімо, передати параметр того типу (зворотний виклик), який ви потім можете використовувати будь-яким способом, включаючи його виклик. Прийнята відповідь використовує змінну члена, і ви повинні встановити змінну члена на свою функцію, а потім викликати метод - некрасивий і схильний до помилок, оскільки встановлення змінної спочатку є частиною договору виклику методу.
Девід

Це також дозволяє вам легко встановити зворотний дзвінок як нульовий, наприкладlet callback: myCallbackType|null = null;
Doches

1
Зауважте, що TSLint скаржиться на "TSLint: Інтерфейс має лише підпис дзвінка - type MyHandler = (myArgument: string) => voidзамість цього використовувати (типи дзвінка )" ; дивіться відповідь TSV
Арджан

Попередній проект цієї відповіді фактично вирішив проблему, яка спричинила мене до цього питання. Я намагався визначити достатньо дозвільну функцію підпису в інтерфейсі, який міг би приймати будь-яку кількість параметрів, не створюючи помилки компілятора. Відповідь у моєму випадку полягала у використанні ...args: any[]. Приклад: експортний інтерфейс MyInterface {/ ** Функція зворотного виклику. / callback: (... args: any []) => any, / * Параметри для функції зворотного виклику. * / callbackParams: any []}
Кен Ліон

61

Ви можете оголосити новий тип:

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

var handler: MyHandler;

Оновлення.

declareКлючове слово не потрібно. Його слід використовувати у файлах .d.ts або в подібних випадках.


Де я можу знайти документацію на це?
Е. Сундін

@ E.Sundin - розділ «Синоніми типу» на typescriptlang.org/docs/handbook/advanced-types.html
TSV

1
Хоча правда і приємно знати, на тій же сторінці (нині) також зазначено "Оскільки ідеальна властивість програмного забезпечення відкрита для розширення, ви завжди повинні використовувати інтерфейс через псевдонім типу, якщо це можливо".
Ар'ян

@Arjan - Я повністю погоджуюся з цим щодо об’єктів. Скажіть, будь ласка, - як ви хочете розширити функцію?
TSV

Зверніть увагу, що декларація типу необов'язкова: var handler: (myArgument: string) => voidсинтаксично дійсна (якщо трохи брудна).
Хатч

35

Ось приклад - не приймаючи жодних параметрів і нічого не повертаючи.

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

Якщо ви бажаєте прийняти параметр, ви також можете додати його:

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

І якщо ви хочете повернути значення, ви можете також додати це:

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

Функціонально вони ідентичні - вони визначають одне і те ж і дають вам перевірити тип підпису функції. Ви можете використовувати те, що вам зручніше. Специфікація каже, що вони є exactly equivalent.
Фентон

6
@nikeee: Питання полягає в тому, чим відрізняється ваша відповідь? Стів виклав свою відповідь перед вашою.
jgauffin

@jgauffin Дійсно, результат той самий. ІМО рішення, яке я розмістив, більш природно, коли йдеться про зворотні виклики, оскільки версія Стіва дозволяє цілі визначення інтерфейсу. Це залежить від ваших уподобань.
nikeee

@Fenton, чи можете ви надати посилання на цю документацію?
jcairney

17

Якщо ви хочете загальну функцію, ви можете скористатися наступною. Хоча це, здається, ніде не задокументовано.

class CallbackTest {
  myCallback: Function;
}   

3

Ви можете використовувати наступне:

  1. Введіть псевдонім (використовуючи typeключове слово, псевдонім функції буквал)
  2. Інтерфейс
  3. Функція літеральна

Ось приклад того, як ними користуватися:

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

Я зіткнувся з тією ж помилкою, намагаючись додати зворотній дзвінок слухачеві події. Як не дивно, встановлення типу зворотного дзвінка на EventListener вирішило це. Це виглядає більш елегантно, ніж визначення цілої підпису функції як типу, але я не впевнений, чи це правильний спосіб зробити це.

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

люблю це. Але де інший клас в дії?
Яро

1

Я трохи запізнююся, але, оскільки деякий час тому в TypeScript можна визначити тип зворотного виклику

type MyCallback = (KeyboardEvent) => void;

Приклад використання:

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

addEvent(element, eventName, callback: MyCallback) {
    element.addEventListener(eventName, callback, false);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.