Fix type parameter leak when using 'this' in reverse mapped types#62931
Fix type parameter leak when using 'this' in reverse mapped types#62931thromel wants to merge 1 commit intomicrosoft:mainfrom
Conversation
602ed22 to
7155d8f
Compare
| // @strict: true | ||
|
|
||
| // Issue #62779: Type parameter leak caused by `this` and reverse mapped type | ||
| declare function testReverseMapped<T extends Record<string, unknown>>(obj: { |
There was a problem hiding this comment.
declare function testReverseMapped2<T extends Record<string, unknown>, T2>(
obj: T2 & {
[K in keyof T]: () => T[K];
},
): T;
const obj2 = testReverseMapped2({
a() {
return 0;
},
b() {
return this.a();
},
});
declare function testReverseMapped3<T extends Record<string, unknown>, T2>(
obj: T2 | {
[K in keyof T]: () => T[K];
},
): T;
const obj3 = testReverseMapped3({
a() {
return 0;
},
b() {
// Property 'a' does not exist on type '{} | { [K in keyof T]: () => T[K]; }'.
// Property 'a' does not exist on type '{}'.(2339)
return this.a();
},
});IMHO, at the very least - the type contained in this error message indicates the fix presented in this PR isn't quite right.
Fixes microsoft#62779 When object literal methods reference `this` inside a function with a reverse mapped type parameter, the type parameter T was leaking through. For example: ```typescript declare function test<T extends Record<string, unknown>>(obj: { [K in keyof T]: () => T[K]; }): T; const obj = test({ a() { return 0; }, b() { return this.a(); }, }); // Was: { a: number; b: T[string]; } (widened to unknown) // Now: { a: number; b: number; } ``` The fix modifies `getContextualThisParameterType` to use the actual object literal type when in an inference context and the contextual type contains a mapped type. This allows methods to see each other's already-inferred types when resolving `this` references. This also handles intersection and union types containing mapped types.
7155d8f to
c2c4e13
Compare
|
Thanks for the feedback! I've updated the fix to use With this change, both test cases now work:
Is this the behavior you were expecting, or did you have a different approach in mind? |
|
With 6.0 out as the final release vehicle for this codebase, we're closing all PRs that don't fit the merge criteria for post-6.0 patches. If you think this was a mistake and this PR fits the post-6.0 patch criteria, please post to the 6.0 iteration issue with details (specifically, which PR and which patch criteria it satisfies). Next steps for PRs:
|
Fixes #62779
Problem
When object literal methods reference
thisinside a function with a reverse mapped type parameter, the type parameterTwas leaking through:Cause
In
getContextualThisParameterType, when computing thethistype for methodb, the code was using the contextual type (the mapped type{ [K in keyof T]: () => T[K] }) with unresolved type parameters. This causedthis.a()to be typed asT["a"]rather than the already-inferrednumber.Solution
When in an inference context, use the actual object literal type via
checkExpressionCached(containingLiteral)rather than the contextual mapped type. This allows methods to see each other's already-inferred types when resolvingthisreferences.Test Plan
reverseMappedThisTypeInference.tsdemonstrating the fix