microsoft / microsoft/TypeScript
Multiple keys being remapped to same key causes unstable modifier propagation
- Dominant language
- Go
- Stars
- 111k
- Forks
- 14.3k
- PR merge metrics
- PR metrics pending
Description
### 🔎 Search Terms
Mapped Types, Key Remapping
### 🕗 Version & Regression Information
- This has been happening since first release of key remapping at 4.1 (tested with v4.1.5 at Playground) until nightly (tested with v6.0.0-dev.20250822 at Playground).
### ⏯ Playground Link
https://www.typescriptlang.org/play/?#code/C4TwDgpgBAShC2BDMBpCIAqB7AkgOwEtgDEAbABUQCdgAeDAPigF4oBvAKCm6gG0UoBPFADW6LADMoGKIgDOUARAAewCHgAmCgAYASNkIkQqUAGIEqc4AF8AdPsPGoAfThXr2qAH4zFq1AAuRQBdILwAV1JSAG4Oa1iOUEhfS2AAeTBiLDwyFlgEZDRMXEJiMkoaWk4eKAB6WqgAPS8uHgAiCSwsWwAjajavIKsqIQBzWJqOrt7EAC82sPD4HuNY6wYEpOhzVLgAR3CLCA08uCRUdGx8IhIKajpqnnqmlsnO7r75xeXV1u4pj79QZQYZjNYbDhAA
### 💻 Code
```ts
type RemapKeyToInitialPart = {
[K in keyof T as K extends `${infer First}.${infer _Rest}` ? First : K]: null;
};
type FirstOptional = RemapKeyToInitialPart<{
// ^?
"foo.bar"?: string;
"foo.baz": number;
}>;
type FirstRequired = RemapKeyToInitialPart<{
// ^?
"foo.baz": number;
"foo.bar"?: string;
}>;
```
### 🙁 Actual behavior
`type FirstOptional` and `type FirstRequired` results in different types:
- `type FirstOptional` has *optional* property `foo`
- `type FirstRequired` has *required* property `foo`
### 🙂 Expected behavior
`type FirstOptional` and `type FirstRequired` results in same type. I would expect it to be same as ~~`type FirstRequired`~~ `type FirstOptional`.
### Additional information about the issue
My motivation was to implement a utility `type Expand` which transforms `{ "foo.bar": number }` to `{ foo: { bar: number } }` ([playground](https://www.typescriptlang.org/play/?exactOptionalPropertyTypes=true#code/C4TwDgpgBAogHmAhgOwCYB4AqA+KBeKAbwCgoyoBtAaSgEtkoBrCEAewDMpMpEBnKAGK0ATr2AAFRMOAB5duirYAugC4oAJQhiZAIwBWEAMbAsAGkEixk6XIXZsxAL7FioSBdESps+YvxQaCDhgCDR+AAMAEkJ6dghhQUcAOmixYXoAc0dwqAB+QSg1GgAyKDTMlzdoTW19IxNMc0Dg0NR+cuQM3AJuIJCwokpxOgYqJVy1WPioADUoRzzZwtgEFAwSciGRphYOLh5+Yb7WiOiqZOiphJrgbMWb5eQIADd41S4KcSUAbicHYlQRgANlJoIZWMgxFBgABGNTwJBodAbcgAIgAFqwMhAkuwAK4ZRBJMC0NioiZQZB4gC2Oniv02GKxOPYrFYSR0UlRag6GV+jmwv1hSUx2NyuIJiHFJLYZUQwFovHYtC0lJpdISAB8oHi0BBlU9UK4YSLmbi2RypHKFUqVe1gOlOr8AcDQVBwZDgNCAEzw1ZIlFkJnY83sznCbllB2ZBlo0UsyXE0msclqKm0+l-IXe01iiWE6XJ62K5Wq9MaqDa3WAg0QI3AHPx0OWhK8eUlu1Rx184hAA))
Contributor guide
Research direction
Reproduce the two mapped-type examples using the linked TypeScript Playground and compare FirstOptional with FirstRequired. Investigate the compiler's key-remapping behavior, then add a regression test showing that both declaration orders produce the same property optionality; done means the test passes with identical results.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100