How do I type a decorated property which type is changed by the decorator?





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ height:90px;width:728px;box-sizing:border-box;
}







1















Here's some code that works perfectly in 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 is an alternative to promises that allows cancelling and managing them similar to Observable from RxJS. Since JS promises don't allow cancelling, Ember Concurrency uses yield instead of async/await.



The task decorator used above converts a generator function into a TaskProperty instance that has a .perform() method.



Please note, that, though weird, this pattern has proven its handiness and reliability in non-typed JS apps.



But typing it presents a challenge.





Here are



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


These types aren't official and can be customized.





I struggle with properly typing the topmost code sample.



The error I'm getting is:




Property perform does not exist on type () => IterableIterator<any>.




It is understandable, since fetchRecord is defined as a generator.



Moreover, TypeScript officially does not support decorators that change the type of decorated property.



So the question is: how to work around the limitation and type such a decorator without reverting to @ts-ignore?



In addition to typing the fetchRecord property, I would like to properly type the arguments that I pass into this.fetchRecord.perform() and which are received by the generator.



Thank you. ^__^










share|improve this question

























  • Please don't put tags in question titles "You should not force a tag into your title."

    – Liam
    Jan 3 at 10:57













  • Shouldn't the return generatorFn return an iteration of promises ?

    – Titian Cernicova-Dragomir
    Jan 3 at 11:04


















1















Here's some code that works perfectly in 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 is an alternative to promises that allows cancelling and managing them similar to Observable from RxJS. Since JS promises don't allow cancelling, Ember Concurrency uses yield instead of async/await.



The task decorator used above converts a generator function into a TaskProperty instance that has a .perform() method.



Please note, that, though weird, this pattern has proven its handiness and reliability in non-typed JS apps.



But typing it presents a challenge.





Here are



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


These types aren't official and can be customized.





I struggle with properly typing the topmost code sample.



The error I'm getting is:




Property perform does not exist on type () => IterableIterator<any>.




It is understandable, since fetchRecord is defined as a generator.



Moreover, TypeScript officially does not support decorators that change the type of decorated property.



So the question is: how to work around the limitation and type such a decorator without reverting to @ts-ignore?



In addition to typing the fetchRecord property, I would like to properly type the arguments that I pass into this.fetchRecord.perform() and which are received by the generator.



Thank you. ^__^










share|improve this question

























  • Please don't put tags in question titles "You should not force a tag into your title."

    – Liam
    Jan 3 at 10:57













  • Shouldn't the return generatorFn return an iteration of promises ?

    – Titian Cernicova-Dragomir
    Jan 3 at 11:04














1












1








1








Here's some code that works perfectly in 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 is an alternative to promises that allows cancelling and managing them similar to Observable from RxJS. Since JS promises don't allow cancelling, Ember Concurrency uses yield instead of async/await.



The task decorator used above converts a generator function into a TaskProperty instance that has a .perform() method.



Please note, that, though weird, this pattern has proven its handiness and reliability in non-typed JS apps.



But typing it presents a challenge.





Here are



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


These types aren't official and can be customized.





I struggle with properly typing the topmost code sample.



The error I'm getting is:




Property perform does not exist on type () => IterableIterator<any>.




It is understandable, since fetchRecord is defined as a generator.



Moreover, TypeScript officially does not support decorators that change the type of decorated property.



So the question is: how to work around the limitation and type such a decorator without reverting to @ts-ignore?



In addition to typing the fetchRecord property, I would like to properly type the arguments that I pass into this.fetchRecord.perform() and which are received by the generator.



Thank you. ^__^










share|improve this question
















Here's some code that works perfectly in 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 is an alternative to promises that allows cancelling and managing them similar to Observable from RxJS. Since JS promises don't allow cancelling, Ember Concurrency uses yield instead of async/await.



The task decorator used above converts a generator function into a TaskProperty instance that has a .perform() method.



Please note, that, though weird, this pattern has proven its handiness and reliability in non-typed JS apps.



But typing it presents a challenge.





Here are



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


These types aren't official and can be customized.





I struggle with properly typing the topmost code sample.



The error I'm getting is:




Property perform does not exist on type () => IterableIterator<any>.




It is understandable, since fetchRecord is defined as a generator.



Moreover, TypeScript officially does not support decorators that change the type of decorated property.



So the question is: how to work around the limitation and type such a decorator without reverting to @ts-ignore?



In addition to typing the fetchRecord property, I would like to properly type the arguments that I pass into this.fetchRecord.perform() and which are received by the generator.



Thank you. ^__^







typescript ember.js decorator typescript-decorator ember-concurrency






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Jan 3 at 10:57









Liam

16.5k1678131




16.5k1678131










asked Jan 3 at 10:47









Andrey Mikhaylov - lolmausAndrey Mikhaylov - lolmaus

18k456104




18k456104













  • Please don't put tags in question titles "You should not force a tag into your title."

    – Liam
    Jan 3 at 10:57













  • Shouldn't the return generatorFn return an iteration of promises ?

    – Titian Cernicova-Dragomir
    Jan 3 at 11:04



















  • Please don't put tags in question titles "You should not force a tag into your title."

    – Liam
    Jan 3 at 10:57













  • Shouldn't the return generatorFn return an iteration of promises ?

    – Titian Cernicova-Dragomir
    Jan 3 at 11:04

















Please don't put tags in question titles "You should not force a tag into your title."

– Liam
Jan 3 at 10:57







Please don't put tags in question titles "You should not force a tag into your title."

– Liam
Jan 3 at 10:57















Shouldn't the return generatorFn return an iteration of promises ?

– Titian Cernicova-Dragomir
Jan 3 at 11:04





Shouldn't the return generatorFn return an iteration of promises ?

– Titian Cernicova-Dragomir
Jan 3 at 11:04












0






active

oldest

votes












Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54020783%2fhow-do-i-type-a-decorated-property-which-type-is-changed-by-the-decorator%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























0






active

oldest

votes








0






active

oldest

votes









active

oldest

votes






active

oldest

votes
















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54020783%2fhow-do-i-type-a-decorated-property-which-type-is-changed-by-the-decorator%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

MongoDB - Not Authorized To Execute Command

How to fix TextFormField cause rebuild widget in Flutter

in spring boot 2.1 many test slices are not allowed anymore due to multiple @BootstrapWith