Which TypeScript AsyncGenerator type returns a Promise?

I am currently in the process of assigning a return type to the function displayed below:

async function *sleepyNumbers() {  // trying to determine TypeScript type
  let n = 0;
  while (true) {
    yield new Promise(resolve => resolve(n++));
    await new Promise(resolve => setTimeout(resolve, 500));
  }
}

(async () => {
  for await (const i of sleepyNumbers())
    console.log(i);
})();

The generator is returning a Promise that resolves to a number. When I attempt to set the type as Promise<number>, it results in an error message:

TS2739: Type 'AsyncGenerator' is missing the following properties from type 'Promise': then, catch, [Symbol.toStringTag], finally

A similar error occurs when attempting to use Iterable.

I have tried setting the type to AsyncGenerator, but that lacks specificity. What is the correct TypeScript syntax to define the return type of this function?

Answer №1

The type will be

AsynchronousGenerator<number, never, void>
:

number - value returned by the next method
never represents that the generator never returns a value
void - indicates that the next method does not receive any parameter

You also need to explicitly specify the type for promise resolution:

yield new Promise<number>(resolve => resolve(n++));

All of this together:

async function *sleepyNumbers(): AsynchronousGenerator<number, never, void> {
    let n = 0;
    while (true) {
        yield new Promise<number>(resolve => resolve(n++));
        await new Promise(resolve => setTimeout(resolve, 500));
    }
}

(async () => {
    for await (const i of sleepyNumbers())
        console.log(i);
})();

Answer №2

For those seeking an exit condition, I have modified the question and adapted Aleksey L.'s answer as follows:

  1. terminate after 4 iterations:
  2. solve the resulting
    error TS2534: A function returning 'never' cannot have a reachable end point.
    (tested using the lib "es2018.asyncgenerator").
async function* sleepyNumbers(count: number): AsyncGenerator<number, void, void> {
  let n = 0;
  while (n < count) {
    yield new Promise<number>(resolve => resolve(n++));
    await new Promise(resolve => setTimeout(resolve, 250));
  }
}

(async () => {
  for await (const i of sleepyNumbers(4))
    console.log(i);
})();

#2 involved changing the 2nd template argument to void in AsyncGenerator because the generator function (the function*) reaches the end without a return statement, causing the caller to receive:

{ value: undefined, done: true }

By adjusting the generator to pass a final value when finished, we utilize the 2nd template parameter:

async function* sleepyNumbers(count: number): AsyncGenerator<number, string, void> {
  let n = 0;
  while (n < count) {
    yield new Promise<number>(resolve => resolve(n++));
    await new Promise(resolve => setTimeout(resolve, 250));
  }
  return 'some string';
}

(async () => {
  const it = sleepyNumbers(4);
  let res;
  for (res = await it.next(); !res.done; res = await it.next())
    console.log(res);
  console.log('Finished with:', res);
  console.log('past end 1:', await it.next());
  console.log('past end 2:', await it.next());
})();

, which yields the following output:

{ value: 0, done: false }
{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: false }
Finished with: { value: 'some string', done: true }
past end 1: { value: undefined, done: true }
past end 2: { value: undefined, done: true }

It seems that accessing an iterator after the generator has completed will always result in value: undefined.

tldr; (although this is quite lengthy already), we examined the usage of the TReturn parameter in the AsyncGenerator template:

interface AsyncGenerator<T = unknown, TReturn = any, TNext = unknown> extends AsyncIterator<T, TReturn, TNext> {
    // NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places.
    next(...args: [] | [TNext]): Promise<IteratorResult<T, TReturn>>;
    return(value: TReturn | PromiseLike<TReturn>): Promise<IteratorResult<T, TReturn>>;
    throw(e: any): Promise<IteratorResult<T, TReturn>>;
    [Symbol.asyncIterator](): AsyncGenerator<T, TReturn, TNext>;
}

(as per

node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts
), where it corresponds to TReturn in

interface IteratorYieldResult<TYield> {
    done?: false;
    value: TYield;
}

interface IteratorReturnResult<TReturn> {
    done: true;
    value: TReturn;
}

type IteratorResult<T, TReturn = any> = IteratorYieldResult<T> | IteratorReturnResult<TReturn>;

(according to lib.es2015.iterable.d.ts)

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Instructions on invoking a function when a button is clicked

I am attempting to trigger a dataUpdate function upon clicking the edit() button I am striving to modify the record Current Version: Angular CLI: 10.0.6 Angular: 10.0.10 registration.component.html <div> <button type="button&quo ...

Pause and anticipate the occurrence of AdMob's complimentary video reward event within a defined function in Ionic/Typescript

In my ionic app, I have a function that determines if the user has watched a reward video to access another function: let canUseThisFunction = await this.someService.canUseFunction(); if(canUseThisFunction){ console.log("can use"); } else { cons ...

Choosing a personalized component using document selector

Currently, I am working on an application using Stenciljs and have created a custom element like this: <custom-alert alertType="warning" alertId="warningMessage" hide>Be warned</custom-alert> The challenge arises when attem ...

Using Ionic 3 to create a list view that includes buttons linked to the items clicked

I need assistance with modifying the button icon in a list layout: <ion-list inset *ngFor="let audio of event.audios; let i = index"> <ion-item> <div class="item-text-center item-text-wrap"> {{audio.fileName}} </div& ...

.observe({ action: (response) => { this.updateData = response.Items; }. what comes after this

I need some guidance on what comes next within the callback function .subscribe({ next: (data) => { this.newData = data.Items; } ...

I will not be accessing the function inside the .on("click") event handler

Can someone help me troubleshoot why my code is not entering the on click function as expected? What am I missing here? let allDivsOnTheRightPane = rightPane.contents().find(".x-panel-body-noheader > div"); //adjust height of expanded divs after addi ...

I am looking for a way to transfer data collected from an input form directly to my email address without the need to open a new window. As of now, I am utilizing angular

Is there a way to send this data to my email address? I need help implementing a method to achieve this. {Name: "John", phoneNumber: "12364597"} Name: "John" phoneNumber: "12364597" __proto__: Object ...

Struggling to locate components in your React, Next.JS, and Typescript project? Storybook is having trouble finding them

I have set up Storybook with my Next.js, TypeScript, and React project. The main project renders fine, but Storybook is breaking and giving me the error message: "Module not found: Error: Can't resolve 'components/atoms' in...". It appears t ...

TypeScript primitive type is a fundamental data type within the

Does TypeScript have a predefined "primitive" type or similar concept? Like type primitive = 'number' | 'boolean' | 'string';. I'm aware I could define it manually, but having it built-in would be neat. ...

Resolving DOMException issue in Google Chrome: A Step-by-Step Guide

In my browser game, I am experiencing an issue with sound playback specifically in Google Chrome. Although the sound manager functions properly in other browsers, it returns a DOMException error after playing sounds in 50% of cases. I'm unsure what co ...

Tips for validating numeric fields that rely on each other with Yup

I am facing a challenge in setting up a complex validation using the library yup for a model with interdependent numeric fields. To illustrate, consider an object structured like this: { a: number, b: number } The validation I aim to achieve is ...

The function Event.target.value is coming back as null

I've been working on creating a timer window, and I've set up a separate component for it. However, I'm facing an issue with passing the time from the main component to the child component. The problem lies in the fact that the state of the ...

Guide on accomplishing masking in Angular 5

I'm curious if it's achievable to design a mask in Angular 5 that appears as follows: XXX-XX-1234 Moreover, when the user interacts with the text box by clicking on it, the format should transform into: 1234121234 Appreciate your help! ...

Jest tests reveal potential errors indicating an object may be null

When running my Jest (typescript) test cases on mongoose Models, I encounter numerous errors such as: Error TS2531: Object is possibly 'null'. For example, consider the following code snippet where the error is reported on line 3: const user = ...

Leveraging a Derived-Class Object Within the Base-Class to Invoke a Base-Class Function with Derived-Class Information

I have a situation where I need to access a method from a derived class in my base generic component that returns data specific to the derived class. The first issue I encountered is that I am unable to define the method as static in the abstract class! ...

Comparing plain objects and class instances for modeling data objects

What is the recommended approach for creating model objects in Angular using TypeScript? Is it advisable to use type annotation with object notation (where objects are plain instances of Object)? For example, let m: MyModel = { name: 'foo' } ...

Generate a collection of items through replication

Develop a function that takes specific input and generates an array of objects with a length of 10 by incrementing the ID of each duplicate object. The first object in the output array should have "visible" set to true, while all others should have it set ...

Typescript, creating multiple definitions for a function with an object parameter

My dilemma lies in a function that takes an argument object and returns another object. This returned object will have a "bar" key based on the presence of the "includeBar" key as an option. I attempted to handle this scenario with different overloads: int ...

Cannot find property in type, and the parameter is implicitly of an unspecified type

I've been encountering this issue where I keep getting an error message. I attempted to resolve it by setting "noImplicitAny": false in tsconfig.json, but unfortunately that did not work. As for the 'Property does not exist on type' error, I ...

Utilize the index of a for loop to manipulate an Angular string

When working with different objects and creating forms simultaneously, I've come across a challenge. My initial idea for handling the submission was to use the following code: <form (ngSubmit)="submitForm{{u}}()"> However, incorporating the in ...