TypeScript 缩小常量索引访问失败的数组获取器

hjzp0vay  于 6个月前  发布在  TypeScript
关注(0)|答案(7)|浏览(57)

🔎 搜索词

常量索引访问,数组

🕗 版本与回归信息

  • 在版本 5.4.55.5.beta 之间发生了变化
  • 在提交或 PR 中发生了变化

⏯ Playground链接

  • 由于库导入,我无法在Playground中复现此问题*

💻 代码

import crypto from 'crypto-js'

const bar = "abc"
const foo: crypto.lib.WordArray = crypto.enc.Utf8.parse(bar) // Returns type: { words: number[], ... }
for (let i = 0; i < foo.words.length; i++) {
  foo.words[i] += 1
//~~~~~~~~~~~~ error TS2532: Object is possibly 'undefined'.

  // Alternatively:
  foo.words[i] = foo.words[i] + 1
//               ~~~~~~~~~~~~ <same error>
}

在循环体内为假值添加适当的检查并不能解决问题,例如:

if (foo && foo.words && foo.words[i]) {
 // Same code as above, same error
}

然而以下方法可以解决问题:

const word = foo.words[i]
if (word) {
  foo.words[i] = word + 1 // OK
}

🙁 实际行为

错误 TS2532:对象可能是“未定义”。

🙂 预期行为

没有错误 - 之前类型检查是正常的

关于此问题的其他信息

自从新的 Control Flow Narrowing for Constant Indexed Accesses 功能推出以来,这个问题是新的。回退到之前的 TypeScript 版本可以解决这个问题

ljsrvy3e

ljsrvy3e1#

你可以在playground中经常导入npm包,这是没有问题的。它能够获取它们的类型。
我在这里没有看到任何错误,无论是TS playground的夜间版本:foo.words 类型为 number[] ,所以我不确定你的测试中是如何让undefined 进入这一切的。也许你正在使用noUncheckedIndexedAccess?
这确实会显示行为上的区别:
TS 5.4
TS nightly
这反过来看起来像是正确性上的改进。

z9gpfhce

z9gpfhce2#

如果这只是一个数组,我不明白为什么你需要crypto-js作为重现的一部分。请提供一个最小示例,带有配置,展示发生了什么

qyyhg6bp

qyyhg6bp3#

@RyanCavanaugh 是的,我知道这一点,但在没有这个包的情况下,我无法找到一个可复现的案例。
@Andarist 我认为你抓住了问题的关键-非常感谢,这肯定是问题所在。不确定这里的操作是什么,我会关闭这个问题,但请根据需要重新打开。

rsaldnfx

rsaldnfx4#

这里是在TS Playground(基于@Andarist的示例)中的最小复现:
包括代码以便于跟踪:

// @noUncheckedIndexedAccess: true
interface Foo { words: number[] }
const foo: Foo = { words: [1, 2, 3] }

for (let i = 0; i < foo.words.length; i++) {
  foo.words[i] += 1 // Alternative 1) Same error as below

  foo.words[i] = foo.words[i] + 1 // Alternative 2) Same error as below

  // Alternative 3) Explicit falsey check still fails
  if (foo.words[i]) {
    foo.words[i] = foo.words[i] + 1
    //             ^Object is possibly 'undefined'.(2532)^
  }
}

bweufnob

bweufnob5#

这里的问题是 i 被分配给了 so,因此无法通过这个检查:
https://github.com/microsoft/TypeScript/pull/57847/files#diff-d9ab6589e714c71e657f601cf30ff51dfc607fc98419bf72e04f6b0fa92cc4b8R26613
i 在循环中是特殊的。我们知道它只在循环块之后被分配。因此,对于这种情况,也许可以进行一些额外的微调。

mbjcgjjk

mbjcgjjk6#

哦,这对重新赋值的变量是不起作用的:

function f1(obj: Record<string, unknown>) {
  let key: string = "";
  if (typeof obj[key] === "string") {
    obj[key].toUpperCase();
  }
  key = ""; // toggle to see the error to go away and come back
}

看到错误出现在似乎与刚刚添加的行没有真正关联的前一行,这有点令人毛骨悚然。

相关问题