Вот некоторый код, который отлично работает в 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()
и которые получены генератором.
Спасибо.^ __ ^