microsoft / microsoft/TypeScript

Narrow object property indexed by bracket notation when the key is a known literal type, regardless of where the key comes from

未关闭
#61,176 1 条评论 11 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

Domain: check: Control Flow Help Wanted Possible Improvement
主要语言
Go
星标
111k
派生
14.3k
平均合并
2 天 4 小时
30 天内合并 PR
132

描述

### 🔍 Search Terms

type guard, narrowing, control flow analysis, property, index, known type, literal type, bracket notation

### ✅ Viability Checklist

- [x] This wouldn't be a breaking change in existing TypeScript/JavaScript code
- [x] This wouldn't change the runtime behavior of existing JavaScript code
- [x] This could be implemented without emitting different JS based on the types of the expressions
- [x] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- [x] This isn't a request to add a new utility type: https://github.com/microsoft/TypeScript/wiki/No-New-Utility-Types
- [x] This feature would agree with the rest of our Design Goals: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals

### ⭐ Suggestion

This is a *different* re-opening or re-focusing of #10530, not the same as #56389, and was not fixed by #57847. The original motivating example for #10530 (treat `obj["key"]` like `obj.key` for narrowing) was fixed long ago, and more recently many of the remaining requests which had been added to #10530 were also fixed (narrowing for `obj[key]` when `key` is a variable whose literal type isn't known, for multiple uses of the same `key`). Those issues are closed. But it seems there are still some issues lumped in with #10530 which were not addressed, so here's the suggestion:

Please enable narrowing of object properties accessed via bracket notation based on the *type* of the key whenever the key's type is a known literal, even if multiple distinct variables or properties are used as keys. That is, if you have `key1` and `key2` and `keyObj.prop` and they are all of the literal type `"foo"` (or have been narrowed to that type), then treat `obj[key1]` and `obj[key2]` and `obj[keyObj.prop]` all as `obj.foo` for control flow purposes.

### 📃 Motivating Example

#51368 gives one, where the variable is a `let` variable annotated with a literal type or `const` asserted :

```ts
let k: "foo" = "foo";
// let k = "foo" as const; // <-- same behavior
const obj = { foo: Math.random() < 0.5 ? "abc" : undefined }
if (obj.foo) { obj[k].toUpperCase() } // error!
// ~~~~~~ possibly undefined
```
Probably I'd say someone should use a `const` there instead of `let`.

----

There's also one from [this Stack Overflow question](https://stackoverflow.com/questions/79438226/typescript-error-property-does-not-exist-on-type-with-optional-object-property), involving [`enum`-like `const`-asserted objects](https://www.typescriptlang.org/docs/handbook/enums.html#objects-vs-enums):

```ts
const Enum = { FOO: "foo", BAR: "bar" } as const;
const obj = { [Enum.FOO]: Math.random() < 0.5 ? "abc" : undefined }
if (obj[Enum.FOO]) obj[Enum.FOO].toUpperCase() // ERROR!
// ~~~~~~~~~~~~~ possibly undefined
```

This one strikes me as quite unfortunate, since an actual `enum` works just fine here:

```ts
enum Enum { FOO = "foo", BAR = "bar" }
const obj = { [Enum.FOO]: Math.random() < 0.5 ? "abc" : undefined }
if (obj[Enum.FOO]) obj[Enum.FOO].toUpperCase() // OKAY
```

### 💻 Use Cases

1. What do you want to use this for?

See #10530 and various issues closed as duplicates for more use cases.

2. Current approaches and workarounds:

If TS knows the literal key then you do too, so you could always just use it directly:

```ts
const Enum = { FOO: "foo", BAR: "bar" } as const;
const obj = { [Enum.FOO]: Math.random() < 0.5 ? "abc" : undefined }
if (obj.foo) obj.foo.toUpperCase() // OKAY
```

That's reasonable, but I think it would be nice if there weren't this caveat for `enum`-like objects where you can't actually use them as keys directly for narrowing purposes.

[Playground link to code](https://www.typescriptlang.org/play/?ts=5.7.3#code/FAOwhgtgpgzgDmAxlABAQRCA9gFzDqAEwBkocUBvYFGlAGzJQGsAuFAIgDMst2UBeDt14BuarURYQMclgBGAKwGUUwtgFl8ACwB0AJzAhCWCAAoAlCgA8KAAw6ArCgD8HMHMR82AVyNROAJYgRCgAvuI0AZwopvIKOsKWFChxANpMALo6OFgAqnBwUHoAwmAwUBZhKAD01ShFelh6AIQRNXW0nV0Afr19KHBYMDABcnQAnii+hP5BRMDA4aCQsAjIKMVSMmjDRQQkjFSdDORMylw8fGUoktI4Yp23MimKyslqKJo4ugZGJpU2exOVzsdyeFA+PyBYKEMJtKIxOIJHhJF4KdJZHL5QolMoVSyhdr1PSNFptWpdSk0Pr9QbDUYTKZQuaERYLcDQeBIVAAURA3ggdACTFQRwkW3IfIFbxQADEAPLytgXXgAGhQACE0AAlZVyMB6PiE65Pe5tU1omWpKUQHQK+UZDTafSGYxmSyAxwuNweLxMmbQkLhToI2KKa38232jKWNI2u2KzF5ApFUrlSoUnna7Xy7WtToUqlFlA00s0gZDEZjSbTWYwtnslZc9YAFT03igNsobSgkZQXeS9vOwnY6q12vO+sNcMeEstgmSEYFCYdTu+Lr+7usdi9ILBftrgdhwdoobjkZXMbRS6jieyyZxafxRPlAGk0ABNBvLTlrVAAdSaJgDSwaZu1nO5+z7Bc5UVZURzHHU9QNI0UBNCUHnFSC4iteNozXH5XX+D0d2BH1wUhAMWRnU9ojDeJEjRZEsHvbFUzxDM6jfT8GyAA)

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

调研方向

从链接的 TypeScript Playground 示例开始,并将其余行为与 issue #10530、#56389 和 #57847 进行比较。期望的结果是,键具有相同已知字面量类型的方括号访问能够像点属性访问一样进行缩窄,包括 `let` 键和类似 enum 的 `const` 对象示例;未指定任何实现文件或测试。

由索引模型根据 Issue 内容生成。

评估

技术栈
typescript
领域
compilers
Issue 类型
功能
难度
5/5
预计耗时
一周以上
活跃度
停滞
描述清晰度
基本清楚
新手友好度
35/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。