Как мне ввести оформленное свойство, тип которого изменяется декоратором? - PullRequest
0 голосов
/ 03 января 2019

Вот некоторый код, который отлично работает в JS:

import Component from '@ember/component';
import {task} from 'ember-concurrency';

class Foo extends Component {
  currentRecordId!: string; // passed from template

  @task
  fetchRecord *(id) {
    return yield this.store.findRecord('foo', id);
  }

  async fetchCurrentRecord() {
    return this.fetchRecord.perform(this.currentRecordId);
  }
}

Ember Concurrency - альтернатива обещаниям, которая позволяет отменять и управлять ими, аналогично Observable из RxJS.Поскольку обещания JS не позволяют отмены, Ember Concurrency использует yield вместо async / await.

Декоратор task, использованный выше, преобразует функцию генератора в экземпляр TaskProperty, который имеет.perform() метод.

Обратите внимание, что, хотя и странно, этот шаблон доказал свою удобство и надежность в нетипизированных приложениях JS.

Но ввод его представляет проблему.


Вот

export declare function task<T, A>(generatorFn: () => Iterator<T>): Task<T, () => TaskInstance<T>>;

export declare function task<T, A>(
  generatorFn: (a: A) => Iterator<T>
): Task<T, (a: A) => TaskInstance<T>>;

export declare function task<T, A>(
  generatorFn: (a: A) => Iterator<PromiseLike<T>>
): Task<T, (a: A) => TaskInstance<T>>;

export declare function task<T, A1, A2>(
  generatorFn: (a1: A1, a2: A2) => Iterator<T>
): Task<T, (a1: A1, a2: A2) => TaskInstance<T>>;

// More variants of arguments skipped

export interface TaskInstance<T> extends PromiseLike<T> {
  readonly error?: any;
  readonly hasStarted: ComputedProperty<boolean>;
  readonly isCanceled: ComputedProperty<boolean>;
  readonly isDropped: ComputedProperty<boolean>;
  readonly isError: boolean;
  readonly isFinished: ComputedProperty<boolean>;
  readonly isRunning: ComputedProperty<boolean>;
  readonly isSuccessful: boolean;
  readonly state: ComputedProperty<TaskInstanceState>;
  readonly value?: T;
  cancel(): void;
  catch(): RSVP.Promise<any>;
  finally(): RSVP.Promise<any>;
  then<TResult1 = T, TResult2 = never>(
    onfulfilled?: ((value: T) => TResult1 | RSVP.Promise<TResult1>) | undefined | null,
    onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null
  ): RSVP.Promise<TResult1 | TResult2>;
}

interface Task<T> extends TaskProperty<T> {
    readonly isIdle: boolean;
    readonly isQueued: boolean;
    readonly isRunning: boolean;
    readonly last?: TaskInstance<T>;
    readonly lastCanceled?: TaskInstance<T>;
    readonly lastComplete?: TaskInstance<T>;
    readonly lastErrored?: TaskInstance<T>;
    readonly lastIncomplete?: TaskInstance<T>;
    readonly lastPerformed?: TaskInstance<T>;
    readonly lastRunning?: TaskInstance<T>;
    readonly lastSuccessful?: TaskInstance<T>;
    readonly performCount: number;
    readonly state: TaskState;
    perform(...args: any[]): TaskInstance<T>;
    cancelAll(): void;
}

export interface TaskProperty<T> extends ComputedProperty<T> {
    cancelOn(eventNames: string[]): this;
    debug(): this;
    drop(): this;
    enqueue(): this;
    group(groupPath: string): this;
    keepLatest(): this;
    maxConcurrency(n: number): this;
    on(eventNames: string[]): this;
    restartable(): this;
}

Эти типы не являются официальными и могут быть настроены.


Я борюсь с правильным набором самого верхнего примера кода.

Ошибка, которую я получаю:

Свойство perform не существует для типа () => IterableIterator<any>.

Это понятно, так как fetchRecord определяется как генератор.

Более того, TypeScript официально не поддерживает декораторы, которые изменяют тип оформленного свойства.

Так что вопрос: какобойти ограничение и набрать такой декоратор, не возвращаясь к @ts-ignore?

Помимо ввода свойства fetchRecord, я хотел бы правильно ввести аргументы, которые я передаю в this.fetchRecord.perform() и которые получены генератором.

Спасибо.^ __ ^

...