TypeScript отказывается от Iterator в for...of

Следующий код работает в моем браузере без типизации, но TypeScript выдает ошибку, сообщающую, что gen() не имеет метода [Symbol.iterator], что ожидается для Iterable. Мне это ограничение кажется странным как AFAIK Iterator является допустимым объектом для передачи for...of.

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

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

Не могли бы вы объяснить мне, что я делаю неправильно здесь?

Редактировать: удаление возвращаемого типа из предыдущего кода, позволяющее TypeScript угадать, что это дало мне IterableIterator, что делает TypeScript счастливым. Так что мне нельзя использовать простой Iterator в for...of?

Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой Zod и раскрыть некоторые ее особенности, например, возможности валидации и трансформации данных, а также...
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Мне нравится библиотека Mantine Component , но заставить ее работать без проблем с Remix бывает непросто.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
TypeScript против JavaScript
TypeScript против JavaScript
TypeScript vs JavaScript - в чем различия и какой из них выбрать?
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Не все нужно хранить на стороне сервера. Иногда все, что вам нужно, это постоянное хранилище на стороне клиента для хранения уникальных для клиента...
Что такое ленивая загрузка в Angular и как ее применять
Что такое ленивая загрузка в Angular и как ее применять
Ленивая загрузка - это техника, используемая в Angular для повышения производительности приложения путем загрузки модулей только тогда, когда они...
2
0
295
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вам нужно ориентироваться на последнюю версию ES, передать флаг --downlevelIteration и иметь как минимум библиотеки «dom» и «esnext», чтобы это работало.

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

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

Транспилированная демонстрация:

"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.info(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; }
}

Я знаю, что могу перебирать его вручную, но я хотел бы использовать его в конструкции for...of. TypeScript описывается как надмножество JavaScript, и код, который я предоставил, кажется мне действительным JavaScript, поэтому я предположил, что он будет работать.

Yovar 17.01.2019 23:08

Извиняюсь за задержку, я экспериментировал в VSCode. Работает нормально, при правильной настройке в tsconfig.json.

Fenton 17.01.2019 23:37

Он работает, но без объявления возвращаемого типа генератора. Опустив его, TypeScript выбирает правильный, и оказывается, что это IterableIterator, и только потому, что этот тип реализует Iterable, for...of работает. Как я уже упоминал в своем ответе, я ошибался, думая, что Iterator подходит для for...of.

Yovar 17.01.2019 23:48

Очевидно, вам нужно использовать Iterable<T> вместо Iterator<T>. Iterable — это интерфейс, содержащий метод [Symbol.iterator], вызываемый циклами for-of, который должен возвращать Iterator, чей метод next() создает серию значений.

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

Текущая обработка генераторов в TypeScript кажется неполной. Он имеет встроенное определение типа Generator, но не является универсальным (выдаваемые значения всегда any). Это также вызовет ошибку здесь, потому что он помечен только как Iterator, а не Iterable, но мы можем видеть, что фактический объект Generator, который создает ваш браузер, также является Iterable (возвращая себя, потому что это также Iterator).

const g = gen();
g[Symbol.iterator]() == g; // true
Ответ принят как подходящий

Я думаю, что ответ на мою проблему заключается в том, что я сделал неправильное заявление, думая, что Iterator является допустимым объектом для передачи for...of.

Причина, по которой пример кода без типизации работает в браузере, заключается в том, что возвращаемое значение генератора является как Iterator, так и Iterable, но только Iterable принимается для for...of, как написано в MDN-страница.

Давая генератору явный возвращаемый тип Iterator, я сужал его таким образом, что TypeScript думал, что у него нет возможности Iterable.

Следующий код, использующий только Iterator, не работает, как и ожидалось, в моем браузере

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

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

Другие вопросы по теме