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
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 111k
- Forks
- 14.3k
- Avg merge
- 2d 4h
- Merged PRs (30d)
- 132
Description
### 🔍 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)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the linked TypeScript Playground example and compare the remaining behavior with issues #10530, #56389, and #57847. The desired result is for bracket accesses whose keys have the same known literal type to narrow like dot-property access, including the `let` key and enum-like `const` object examples; no implementation files or tests are named.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- compilers
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100