TypeScript does not accept an Iterator within a for...of loop

In my browser, the code works fine even without the typings. However, TypeScript is emitting an error stating that gen() does not have a [Symbol.iterator] method, which is required for it to be considered an Iterable. I find this limitation odd because as far as I know, an Iterator should be a valid object to use with a for...of loop.

function *gen(): Iterator<number> {
  yield 1
  yield 2
  yield 3
}

for (const val of gen()) {
  console.log(val)
}

Could you please help me understand what mistake I am making here?

Edit: After removing the return type from the code above and allowing TypeScript to infer it automatically, I got IterableIterator, which satisfied TypeScript. Does this mean I cannot simply use an Iterator in a for...of loop?

Answer №1

In order to properly handle the series of values, it seems like using Iterable<T> instead of Iterator<T> is necessary. The interface Iterable contains the method [Symbol.iterator] that for-of loops call upon, returning an Iterator with a next() method to generate the values.

function* gen(): Iterable<number> {
  yield 1
  yield 2
  yield 3
}

Currently, TypeScript's handling of Generators appears to be incomplete. While there is a built-in type definition for Generator, it lacks generics (which means the produced values default to any). This would result in an error as it is identified only as an Iterator without being marked as Iterable. However, it's evident that the actual Generator object created by your browser functions as both Iterable and Iterator (since it returns itself).

const g = gen();
g[Symbol.iterator]() == g; // true

Answer №2

It is necessary to target a recent version of ES, use the --downlevelIteration flag, and include at least the "dom" and "esnext" libraries for this code to function correctly.

function* gen() {
    yield 1;
    yield 2;
    yield 3;
}

for (let val of gen()) {
    console.log(val);
}

Here is the transpiled demonstration:

"use strict";
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
var __values = (this && this.__values) || function (o) {
    var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0;
    if (m) return m.call(o);
    return {
        next: function () {
            if (o && i >= o.length) o = void 0;
            return { value: o && o[i++], done: !o };
        }
    };
};
var e_1, _a;
function gen() {
    return __generator(this, function (_a) {
        switch (_a.label) {
            case 0: return [4 /*yield*/, 1];
            case 1:
                _a.sent();
                return [4 /*yield*/, 2];
            case 2:
                _a.sent();
                return [4 /*yield*/, 3];
            case 3:
                _a.sent();
                return [2 /*return*/];
        }
    });
}
try {
    for (var _b = __values(gen()), _c = _b.next(); !_c.done; _c = _b.next()) {
        var val = _c.value;
        console.log(val);
    }
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
    try {
        if (_c && !_c.done && (_a = _b["return"])) _a.call(_b);
    }
    finally { if (e_1) throw e_1.error; }
}

Answer №3

After some reflection, I have realized that the root cause of my issue was based on a mistaken assumption - believing that an Iterator could be directly used with for...of.

The underlying reason why the code snippet without type declarations functions in a web browser is because a return value from a generator can serve as both an Iterator and an Iterable. However, for the iteration process using for...of, only an Iterable object is acceptable according to the details provided on the MDN page.

When I explicitly specified the return type as an Iterator for the generator, it inadvertently led TypeScript to narrow its capabilities, causing it to overlook the inherent Iterable functionality.

A subsequent experiment utilizing solely an Iterator unsurprisingly failed in my browsing environment:

const it = {
  next: () => ({value: 1, done: false})
}

for (const val of it) {
  console.log(val)
}

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

Get the base 64 file and convert it to an Excel spreadsheet

The encoded data in base 64 format is provided below. UEsDBBQAAAAIAIlimE8HQU1igQAAALEAAAAQAAAAZG9jUHJvcHMvYXBwLnhtbE2OPQsCMRBE/8px\nvbdBwUJiQNBSsLIPexsvkGRDskJ+vjnBj24ebxhG3wpnKuKpDi2GVI/jIpIPABUXirZOXaduHJdo\npWN5ADvnkc6Mz0hJYKvUHqgJpZnmTf4Ojka ...

Is it possible for Typescript to deduce keys of a generic type within conditional statements?

Within my function, I have a parameter that accepts an enum value as T and a generic type Data<T> which selects between two different data types. I expected to be able to access properties of type BarData within a conditional statement that should m ...

The notion of await goes beyond simply waiting for a promise to be fulfilled

Hey there everyone! I've been struggling with a problem for some time now, and I just can't seem to figure it out by simply searching online. That's why I'm turning to all of you for help. Situation: I'm working on a small applic ...

InjectableToken missing in Angular Standalone Component - Provider Not Found

In my standalone component, I am using an Injection Token to set a path (the paths are not the same for all micro-frontends). However, I do not provide this token in the component itself because I need to override it using providers in my app-module.ts. H ...

Unable to get jQuery scrollTo() function to work within Angular 4

I have encountered an issue while using a library called ng2-pdf-viewer. The default way to stick to a page in the PDF document was not working as expected. To overcome this, I utilized jQuery's scrollTo() method to navigate to the specific .page clas ...

The 'innerText' property is not found in the 'Element' type

Currently, I am working with Typescript and Puppeteer. My goal is to extract the innerText from an element. const data = await page.$eval(selector, node => node.innerText); However, I encountered an error: The property 'innerText' is not ...

Facing a TypeScript Error when using the ts-node-dev --rs (restart) option in Node environment

When I use the --rs option (--rs - Allow to restart with "rs" line entered in stdio, disabled by default), it gives me an error when running npm run publish: https://i.sstatic.net/Vxnel.png Here is the package.json file: { "name": & ...

It is impossible to search within a read-only array union

Is there a way to search for an element within a readonly array union in TypeScript? const areas = { area1: { adjacencies: [2, 3, 4, 5] }, area2: { adjacencies: [6, 7, 8] } } as const; let area: keyof typeof areas; if (Math.random() < 0. ...

Utilize key-value pairs to reference variables when importing as a namespace

Is it feasible to utilize a string for performing a lookup on an imported namespace, or am I approaching this the wrong way? Consider a file named my_file.ts with contents similar to: export const MyThing: CustomType = { propertyOne: "name", ...

Unexpected token error on an optional property in Visual Studio Code

I encountered a problem with a project I cloned. Below is the code snippet created using this project: https://github.com/enuchi/React-Google-Apps-Script export interface Vehicle { wheels: number; insurance?: string; } export default class Car { whe ...

Creating Mongoose models in TypeScript with multiple connections

Attempting to build a model with multiple connections as per the documentation, I encountered the following error: TS2345: Argument of type 'Schema<Document<any>, Model<Document<any>>>' is not assignable to parameter of ty ...

Passing data from a container component to a Redux Form component in React with TypeScript

One of my Form container components looks like this: class PersonalDetailContainer extends React.Component<PropTypes> { onSubmit = async (fields: PersonalFields) => { this.props.savePersonalDetail(fields); }; render(): JSX.Element { ...

Learn how to construct a specialized folder arrangement and include TypeScript within an Express server before launching it on the Heroku platform

Creating and deploying my MEAN app on Heroku has been a breeze so far. The front end is up and running smoothly on the provided website, but I'm stuck when it comes to compiling and building the server part of my app, especially since it's in Typ ...

Are there any comparable features in Angular 8 to Angular 1's $filter('orderBy') function?

Just starting out with Angular and curious about the alternative for $filter('orderBy') that is used in an AngularJS controller. AngularJS example: $scope.itemsSorted = $filter('orderBy')($scope.newFilteredData, 'page_index&apos ...

Angular-highcharts used to create a stacked bar chart display

const dataArray = [{ name: "LTNS", id: 1, percentage: 60, price: 900000 }, { name: "NPCS", id: 2, percentage: 30, price: 342000 }, { name: "MARCOS", id: 3, percentage: 10, price: 600000 }] To create a stacked ...

Guide on how to utilize jest for mocking MongoDB manager in typescript

Is there a way to return a mongodb.connection.db() type value and mock its collection for testing purposes? I have implemented a mongoClient connection and use its db() function. If everything is set up correctly, I should be able to access the collections ...

Is there a syntax issue in your Angular/Typescript code?

I am currently developing a login system using Angular and Firestore, and here is the code I have written: import { Injectable } from '@angular/core'; import { Router } from '@angular/router'; import * as firebase from 'firebase/ ...

Transform React class components into function components

In my code, there is an abstract class: export default abstract class TableAction<T = any, R = any> extends React.Component<T, R> { protected abstract onClick(e: React.MouseEvent<HTMLButtonElement>): void | Promise<void>; pr ...

Retrieving user input from one component to be used in another component in Angular

I'm currently working on a structure that involves a navbar component and a form component https://i.stack.imgur.com/nPRLO.png Initially, I have a navbar component where I load user data using an ID stored in the session. In the right side component ...

The Conundrum of Angular 5 Circular Dependencies

I've been working on a project that involves circular dependencies between its models. After reading through this StackOverflow post and its suggested solutions, I realized that my scenario might not fit into the category of mixed concerns often assoc ...