为什么 TypeScript 不根据使用变量键的检查来缩小范围?

Why doesn't TypeScript narrow based on checks using a variable key?

提问人:natevw 提问时间:7/26/2023 更新时间:7/26/2023 访问量:49

问:

如果我的代码大致如下:

const obj: Record<string, SomeType>;
if ("key" in obj) {
  const val: SomeType = obj["key"];
  void val;
}

TypeScript 编译器并没有让我对 .val

同样,如果我编写这样的代码:

const val = obj["key"];
if (val) {
  const val2: SomeType = val;
  void val2;
}

TypeScript 编译器知道 / 不能在检查中。valval2| undefined

但是,如果我编写这样的代码:

let k = "key";
if (obj[k]) {
  const val: SomeType = obj[k];
  void val;
}

然后 TypeScript 抱怨说,这仍然可能是.val: Sometype| undefined

即使我在与上一个有点相似的情况下使用 a,它仍然不满意:const

(["a", "b", "c"]).forEach(_k => {
  const k = _k;
  if (obj[k]) {
    const val: SomeType = obj[k];
    //         ^^^ VSCode still thinks it could be `| undefined`!
  }
});

为什么会这样?

TypeScript 推断 类型 缩小

评论

3赞 jcalz 7/26/2023
你遇到了 microsoft/TypeScript#10530。目前,在保护像 这样的属性访问时,必须具有单个已知的文字类型才能使其工作。(检查器使用密钥的类型而不是其标识,因此它无法区分 和 if 和 are 属于同一类型。因此,作为守卫工作的唯一方法是,如果该类型是已知的文字,例如 .)这是否完全解决了这个问题?如果是这样,我会写一个答案来解释;如果没有,我错过了什么?obj[key]keyobj[key1]obj[key2]key1key2"key"
1赞 yshavit 7/26/2023
在更高的层次上,你基本上在这里遇到了 Turning 的停止问题。编译器无法找到所有情况,因此语言实现者必须任意选择一些台词来表示“足够好”。你的用例落在这条线的一侧,但如果不是这样,就会有一些其他的用例稍微复杂一些。
0赞 natevw 7/26/2023
@jcalz 还没有彻底审查#10530,但这似乎确实有帮助,谢谢!也许不能完全解释为什么不起作用,但确实如此,但在循环中再次不起作用——似乎有点不一致,而且模式对我来说还不是 100% 清楚。let k = "key"const k = "key"const k = _k: "a" | "b" | "c"
1赞 jcalz 7/26/2023
当你写的时候,类型被推断为,而当你写的时候,你会得到一个类型。这能解决问题吗?你是想让我写下我的答案,还是你还需要时间来复习一些东西,然后才能决定是否还缺少什么?(编辑:无效,对吧? 🙃 也许你的意思是,但我不知道是什么。我猜我忽略了这一点。let k = "key"kstringconst k = "key""key"const k = _k: "a" | "b" | "c"const k: "a" | "b" | "c" = _k_k

答:

0赞 Linden Wells 7/26/2023 #1

我不太擅长使用类型进行推理,但是在您的第三个块中,更改为 a 会使类型起作用:kconst

const k = "key";
if (obj[k]) {
  const val: string = obj[k];
  void val;
}

这并不能解决您的问题,但您可以断言数组是内联的。 第四个代码块我只能通过添加一个中间步骤来进行类型检查:const

(["a", "b", "c"] as const).forEach(k => {
    const val = obj[k] as const
    if (val) {
      const narrowed_val: string = val;
    }
  });

希望这能帮助你继续你正在做的任何事情,但希望更聪明的打字稿推理者可以准确地解释发生了什么。

注意我把你改成了,以便更容易在vscode中使用。SomeTypestring

评论

1赞 natevw 7/26/2023
谢谢,是的,不幸的是,这些变体确实出于某种原因起作用,但其他变体不起作用!更多的是我试图修复的循环本身,并不真正需要那个,但它为什么不能工作仍然是一个谜。let k
0赞 Linden Wells 7/26/2023
我在 jcalz 的评论中查看了 TS 问题,但阅读了以下回复: github.com/microsoft/TypeScript/issues/51368
0赞 jcalz 7/26/2023
“阅读回复”是什么意思?您能明确说明您想让我们注意什么吗?
0赞 Linden Wells 7/26/2023
>是的,foo[“bar”] 曾经是不可缩小的。现在是,但 foo[keyVar] 不缩小的更大问题仍然存在(在实现方面,这是一个更大的蠕虫罐头)。取自 github.com/microsoft/TypeScript/issues/...
0赞 jcalz 7/26/2023
是的,ms/TS#10530 是他们跟踪更大问题的地方,即使问题的名称不再适用。不知道为什么他们既不更改名称以反映这一点,也不关闭它以支持单独的问题。因此,每当我谈论更大的问题时,我都会很高兴地将人们指向 ms/TS#10530,然后必须进行这种额外的对话,讨论它如何真正成为关注更大问题更新的地方,尽管有这个名字。太好玩了,呜呜。