Mapped type with enum keys emits string keys in d.ts type, but uses enum keys in .ts type
#60.247 geöffnet am 17. Okt. 2024
Repository-Metriken
- Stars
- (48.455 Stars)
- PR-Merge-Metriken
- (Durchschn. Merge 6T 17h) (9 gemergte PRs in 30 T)
Beschreibung
🔎 Search Terms
mapped type, enum, declaration
🕗 Version & Regression Information
- This is the behavior in every version I tried
⏯ Playground Link
💻 Code
enum Foo {
A = 'a',
B = 'b',
}
declare function foo<T>(arg: T): TaggedUnion<T>;
type TaggedUnion<T> = { [K in keyof T]: { key: K, value: T[K] } }[keyof T];
export const test = foo({
[Foo.A]: '',
[Foo.B]: 1,
});
// the key satisfies foo
(test.key satisfies Foo);
// and can switch without error
switch (test.key) {
case Foo.A: break;
case Foo.B: break;
default:
// ✅ safe
(test satisfies never);
}
/** ---------------------------- */
// this is using the generated types from the .d.ts, just renamed to avoid clash
type TaggedUnionDTS<T> = {
[K in keyof T]: {
key: K;
value: T[K];
};
}[keyof T];
export declare const testDTS: TaggedUnionDTS<{
a: string;
b: number;
}>;
// the key does not satisfy Foo
(testDTS.key satisfies Foo);
// and can switch without error
switch (testDTS.key) {
case Foo.A: break;
case Foo.B: break;
default:
// ❌ errors
(test satisfies never);
}
🙁 Actual behavior
When the above code is used and referenced directly via .ts files (i.e. in the IDE) then it all works as expected. TS is able to understand that the .key property is Foo and so it understands that the switch refines the variable and so in the default the variable correctly refines to never.
However when you consume the code via the .d.ts output - the relationship with Foo is lost entirely and so the same switch now is no longer able to refine the variable and so in the default the variable has not refined at all.
🙂 Expected behavior
Either .d.ts code should include the relationship with Foo, or the .ts code should not have a relationship to Foo. I.e. the behaviour should be consistent between the two.
Additional information about the issue
An engineer encountered this at Canva.
The test variable was declared in a file that was previously within the same project as the switch.
During a refactor they moved the test variable to a new project, causing the CI typecheck to switch from using the .ts directly to using the .d.ts.
This caused the switch to not refine, causing a TS error on the default case.