我试图编写一个生成器函数,它将连续返回一对可迭代对象中的前一个和当前值.

function* prevCurrent<T>(iterable: Iterable<T>) {
    let head = true
    let previous: T
    for (const current of iterable) {
        if (head) {
            previous = current
            head = false
            continue
        }
        yield [previous, current]
        previous = current
    }
}

使用方法如下:

const arr = [1, 2, 3]
for (const pair of prevCurrent(arr)) {
    console.log(pair)
}
// [1, 2]
// [2, 3]

然而,在yield行,TypeScript报告:

Variable 'previous' is used before being assigned.

显然,静态分析没有意识到yield只能在for-of的第二次迭代中达到,previous已经设置.

我可以很容易地通过!操作员解决这个问题.但是is there an alternative syntax or logic I could use that static analysis would accept?

推荐答案

编译器不明白headprevious之间有任何关系.你可以把它们都打包在一个变量中,比如prevHeaddiscriminated union type,比如{head: true} | {head: false, previous: T}.但一旦你这样做,你可以把它压缩到只有undefined | { previous: T }.事实上,如果你知道T不能接受undefined,那么你会把它一路折叠到undefined | T.这意味着你会单独使用previous而不是head.

但是假设undefined extends T是可能的,那么我们可以使用等价于undefined | { previous: T }的东西来区分循环的第一次运行和undefined值之后的运行. 让我们这样做,并重命名变量/属性:

function* prevCurrent<T>(iterable: Iterable<T>) {
  let previous: { val: T } | undefined;
  for (const current of iterable) {
    if (!previous) {
      previous = { val: current }
      continue
    }
    yield [previous.val, current]
    previous.val = current
  }
}

这里的变量只有previous,它要么是undefined,要么是一个类型为T的具有val属性的对象.它会编译,因为现在甚至没有办法表示无效的状态.

Playground link to code

Typescript相关问答推荐

类型脚本如何将嵌套的通用类型展开为父通用类型

您可以创建一个类型来表示打字员吗

使用泛型keyof索引类型以在if-condition内类型推断

如何使用Zod使一个基于其他字段值的字段为必填字段?

如何根据特定操作隐藏按钮

STypescript 件的标引签名与功能签名的区别

打印脚本中的动态函数重载

扩展参数必须具有元组类型或传递给REST参数

Angular文件上传到Spring Boot失败,多部分边界拒绝

布局组件中的Angular 17命名路由出口

如何从对象或类动态生成类型

如何编辑切片器以使用CRUD调用函数?

打字错误TS2305:模块常量没有导出的成员

如何在本地存储中声明该类型?

垫子工具栏不起作用的Angular 布线

如何创建允许使用带有联合类型参数的Array.from()的TS函数?

如何在Vuetify 3中导入TypeScript类型?

我应该使用服务方法(依赖于可观察的)在组件中进行计算吗?

如何在由函数参数推断的记录类型中具有多个对象字段类型

使用嵌套属性和动态执行时,Typescript 给出交集而不是并集