如何在typescript中使用生成器键入迭代器结果

chhkpiq4  于 12个月前  发布在  TypeScript
关注(0)|答案(2)|浏览(115)

我使用的是typescript,并创建了以下代码

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

const iter: Iterator<number> = myIter();
const item:IteratorYieldResult<number> = iter.next();

字符串
演示
它会产生一个打字错误


的数据
在悬停时告诉我错误是什么



我检查了next到底返回了什么,发现了这个接口定义:

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


这就是我迷路的地方,因为我的迭代器被定义为Iteractor<number>,所以TReturn是什么。另一方面,它默认设置为any,所以我猜它是可选的,我可以省略它。但由于某种原因,它不能以IteratorResult的方式工作。无论如何,一些指导将不胜感激?
我不知道这里有什么问题,但也许有人能帮我解决这个问题?

4urapxun

4urapxun1#

我不喜欢在每个调用点都检查done,因为我知道流应该包含多少元素,所以我写了一个小 Package 器来简化next()的返回值:

export class SimpleIterator<T> {
    constructor(private generator: AsyncGenerator<T>) {
    }

    async next(): Promise<T> {
        const {value, done} = await this.generator.next()
        if(done) throw new Error("Reached end of stream")
        return value
    }
}

字符串
现在你可以做

const iter = new simpleIterator(getSomeAsyncGenerator())
const value = await iter.next()


它不是一个真正的“迭代器”,因为它没有实现交互器接口,所以你不能在for中使用它,但这不是我想要的方式(生成器已经支持for,所以如果你想要的话直接使用它)

vsaztqbk

vsaztqbk2#

如前所述,迭代器的next()方法返回IteratorResult类型的值。定义如下:

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

字符串
IteratorYieldResultIteratorReturnResult的并集,它们本身定义如下:

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

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


这意味着编译器告诉你iter.next()返回 * 要么 * 一个IteratorYieldResult对象,带有false/missing done属性,和一个number类型的value,* 要么 * 它返回一个IteratorReturnResult对象,带有 * true * done属性,和一个any类型的value
你把它赋值给一个IteratorYieldResult类型的变量,编译器告诉你“等等,它可能不是这个类型,它可能是一个IteratorReturnResult“。
现在,因为你自己编写了生成器,你知道第一次调用next()你会得到一个IteratorYieldResult,因为在函数返回之前有三个yield语句。但这是类型系统没有的信息。就像这样:

function foo(): number | string {
    return "hello";
}
const str: string = foo(); // error! number not assignable to string


函数foo()根据它的类型签名返回numberstring。你可以知道它总是返回string,但是编译器不能告诉这一点,因为它只是查看签名,而不是深入实现。所以它抱怨:“嘿,也许str在运行时将是number,而不是你声称的string”。
那么在这里做什么才是“正确的”呢?我想这取决于你想做什么。在这个玩具例子中,你可以告诉编译器不需要担心,你肯定会得到一个IteratorYieldResult而不是IteratorReturnResult。这种“告诉”编译器某个东西的类型比它可以验证的要窄的方式是通过类型Assert来完成的:

const itemAsserted = iter.next() as IteratorYieldResult<number>; // okay


或者,你可以做一般的检查,你 * 不 * 声称知道什么出来(这是有意义的,如果myIter()的实现可能会改变,或者如果你在循环中做iter.next()):

const itemSafe = iter.next();
// const itemSafe: IteratorResult<number, any>
if (!itemSafe.done) {
    itemSafe; // const itemSafe: IteratorYieldResult<number>
    itemSafe.value.toFixed(); // okay
}


这里我们只是让编译器告诉我们iter.next()返回一个IteratorResult<number, any>。然后我们通过检查done属性来获取返回值并进行 * 测试 *。如果它不是true,然后编译器会帮助你将结果缩小到IteratorYieldResult<number>。你不需要Assert任何东西,因为编译器理解所做的类型检查以上;这是因为IteratorResult是一个可区分的联合,其中可以检查单个属性(在本例中为done)以确定您正在查看的是联合的哪个成员。
好的,我希望这对你有意义和帮助。祝你好运!
Playground链接到代码

相关问题