Skip to content

Commit 56f12bc

Browse files
committed
Avoid getting apparent type of intersected mapped types in contextual typing
1 parent 53acfd3 commit 56f12bc

File tree

4 files changed

+192
-1
lines changed

4 files changed

+192
-1
lines changed

src/compiler/checker.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -29252,6 +29252,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2925229252
);
2925329253
}
2925429254

29255+
function getApparentTypeOfInstantiatedContextualType(type: Type) {
29256+
return getObjectFlags(type) & ObjectFlags.Mapped ? type : getApparentType(type);
29257+
}
29258+
2925529259
// Return the contextual type for a given expression node. During overload resolution, a contextual type may temporarily
2925629260
// be "pushed" onto a node using the contextualType property.
2925729261
function getApparentTypeOfContextualType(node: Expression | MethodDeclaration, contextFlags: ContextFlags | undefined): Type | undefined {
@@ -29266,7 +29270,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2926629270
// That would evaluate mapped types with array or tuple type constraints too eagerly
2926729271
// and thus it would prevent `getTypeOfPropertyOfContextualType` from obtaining per-position contextual type for elements of array literal expressions.
2926829272
// Apparent type of other mapped types is already the mapped type itself so we can just avoid calling `getApparentType` here for all mapped types.
29269-
t => getObjectFlags(t) & ObjectFlags.Mapped ? t : getApparentType(t),
29273+
t => {
29274+
if (t.flags & TypeFlags.Intersection) {
29275+
return getIntersectionType(map((t as IntersectionType).types, getApparentTypeOfInstantiatedContextualType));
29276+
}
29277+
return getApparentTypeOfInstantiatedContextualType(t);
29278+
},
2927029279
/*noReductions*/ true
2927129280
);
2927229281
return apparentType.flags & TypeFlags.Union && isObjectLiteralExpression(node) ? discriminateContextualTypeByObjectMembers(node, apparentType as UnionType) :

tests/baselines/reference/reverseMappedIntersectionInference.symbols

+76
Original file line numberDiff line numberDiff line change
@@ -186,3 +186,79 @@ const res2 = withTuples([
186186
},
187187
]);
188188

189+
type Tuple<T> = readonly [T, ...T[]];
190+
>Tuple : Symbol(Tuple, Decl(reverseMappedIntersectionInference.ts, 66, 3))
191+
>T : Symbol(T, Decl(reverseMappedIntersectionInference.ts, 68, 11))
192+
>T : Symbol(T, Decl(reverseMappedIntersectionInference.ts, 68, 11))
193+
>T : Symbol(T, Decl(reverseMappedIntersectionInference.ts, 68, 11))
194+
195+
declare function withTuplesConstraints<T extends Tuple<any>, E extends Tuple<any>>(
196+
>withTuplesConstraints : Symbol(withTuplesConstraints, Decl(reverseMappedIntersectionInference.ts, 68, 37))
197+
>T : Symbol(T, Decl(reverseMappedIntersectionInference.ts, 70, 39))
198+
>Tuple : Symbol(Tuple, Decl(reverseMappedIntersectionInference.ts, 66, 3))
199+
>E : Symbol(E, Decl(reverseMappedIntersectionInference.ts, 70, 60))
200+
>Tuple : Symbol(Tuple, Decl(reverseMappedIntersectionInference.ts, 66, 3))
201+
202+
arg: Results<T> & Errors<E>
203+
>arg : Symbol(arg, Decl(reverseMappedIntersectionInference.ts, 70, 83))
204+
>Results : Symbol(Results, Decl(reverseMappedIntersectionInference.ts, 0, 0))
205+
>T : Symbol(T, Decl(reverseMappedIntersectionInference.ts, 70, 39))
206+
>Errors : Symbol(Errors, Decl(reverseMappedIntersectionInference.ts, 5, 2))
207+
>E : Symbol(E, Decl(reverseMappedIntersectionInference.ts, 70, 60))
208+
209+
): [T, E];
210+
>T : Symbol(T, Decl(reverseMappedIntersectionInference.ts, 70, 39))
211+
>E : Symbol(E, Decl(reverseMappedIntersectionInference.ts, 70, 60))
212+
213+
const res3 = withTuplesConstraints([
214+
>res3 : Symbol(res3, Decl(reverseMappedIntersectionInference.ts, 74, 5))
215+
>withTuplesConstraints : Symbol(withTuplesConstraints, Decl(reverseMappedIntersectionInference.ts, 68, 37))
216+
{
217+
data: "foo",
218+
>data : Symbol(data, Decl(reverseMappedIntersectionInference.ts, 75, 3))
219+
220+
onSuccess: (dataArg) => {
221+
>onSuccess : Symbol(onSuccess, Decl(reverseMappedIntersectionInference.ts, 76, 16))
222+
>dataArg : Symbol(dataArg, Decl(reverseMappedIntersectionInference.ts, 77, 16))
223+
224+
dataArg;
225+
>dataArg : Symbol(dataArg, Decl(reverseMappedIntersectionInference.ts, 77, 16))
226+
227+
},
228+
error: 404,
229+
>error : Symbol(error, Decl(reverseMappedIntersectionInference.ts, 79, 6))
230+
231+
onError: (errorArg) => {
232+
>onError : Symbol(onError, Decl(reverseMappedIntersectionInference.ts, 80, 15))
233+
>errorArg : Symbol(errorArg, Decl(reverseMappedIntersectionInference.ts, 81, 14))
234+
235+
errorArg;
236+
>errorArg : Symbol(errorArg, Decl(reverseMappedIntersectionInference.ts, 81, 14))
237+
238+
},
239+
},
240+
{
241+
data: true,
242+
>data : Symbol(data, Decl(reverseMappedIntersectionInference.ts, 85, 3))
243+
244+
onSuccess: (dataArg) => {
245+
>onSuccess : Symbol(onSuccess, Decl(reverseMappedIntersectionInference.ts, 86, 15))
246+
>dataArg : Symbol(dataArg, Decl(reverseMappedIntersectionInference.ts, 87, 16))
247+
248+
dataArg;
249+
>dataArg : Symbol(dataArg, Decl(reverseMappedIntersectionInference.ts, 87, 16))
250+
251+
},
252+
error: 500,
253+
>error : Symbol(error, Decl(reverseMappedIntersectionInference.ts, 89, 6))
254+
255+
onError: (errorArg) => {
256+
>onError : Symbol(onError, Decl(reverseMappedIntersectionInference.ts, 90, 15))
257+
>errorArg : Symbol(errorArg, Decl(reverseMappedIntersectionInference.ts, 91, 14))
258+
259+
errorArg;
260+
>errorArg : Symbol(errorArg, Decl(reverseMappedIntersectionInference.ts, 91, 14))
261+
262+
},
263+
},
264+
]);

tests/baselines/reference/reverseMappedIntersectionInference.types

+77
Original file line numberDiff line numberDiff line change
@@ -180,3 +180,80 @@ const res2 = withTuples([
180180
},
181181
]);
182182

183+
type Tuple<T> = readonly [T, ...T[]];
184+
>Tuple : Tuple<T>
185+
186+
declare function withTuplesConstraints<T extends Tuple<any>, E extends Tuple<any>>(
187+
>withTuplesConstraints : <T extends Tuple<any>, E extends Tuple<any>>(arg: Results<T> & Errors<E>) => [T, E]
188+
189+
arg: Results<T> & Errors<E>
190+
>arg : Results<T> & Errors<E>
191+
192+
): [T, E];
193+
194+
const res3 = withTuplesConstraints([
195+
>res3 : [[string, boolean], [number, number]]
196+
>withTuplesConstraints([ { data: "foo", onSuccess: (dataArg) => { dataArg; }, error: 404, onError: (errorArg) => { errorArg; }, }, { data: true, onSuccess: (dataArg) => { dataArg; }, error: 500, onError: (errorArg) => { errorArg; }, },]) : [[string, boolean], [number, number]]
197+
>withTuplesConstraints : <T extends Tuple<any>, E extends Tuple<any>>(arg: Results<T> & Errors<E>) => [T, E]
198+
>[ { data: "foo", onSuccess: (dataArg) => { dataArg; }, error: 404, onError: (errorArg) => { errorArg; }, }, { data: true, onSuccess: (dataArg) => { dataArg; }, error: 500, onError: (errorArg) => { errorArg; }, },] : [{ data: string; onSuccess: (dataArg: string) => void; error: number; onError: (errorArg: number) => void; }, { data: true; onSuccess: (dataArg: boolean) => void; error: number; onError: (errorArg: number) => void; }]
199+
{
200+
>{ data: "foo", onSuccess: (dataArg) => { dataArg; }, error: 404, onError: (errorArg) => { errorArg; }, } : { data: string; onSuccess: (dataArg: string) => void; error: number; onError: (errorArg: number) => void; }
201+
202+
data: "foo",
203+
>data : string
204+
>"foo" : "foo"
205+
206+
onSuccess: (dataArg) => {
207+
>onSuccess : (dataArg: string) => void
208+
>(dataArg) => { dataArg; } : (dataArg: string) => void
209+
>dataArg : string
210+
211+
dataArg;
212+
>dataArg : string
213+
214+
},
215+
error: 404,
216+
>error : number
217+
>404 : 404
218+
219+
onError: (errorArg) => {
220+
>onError : (errorArg: number) => void
221+
>(errorArg) => { errorArg; } : (errorArg: number) => void
222+
>errorArg : number
223+
224+
errorArg;
225+
>errorArg : number
226+
227+
},
228+
},
229+
{
230+
>{ data: true, onSuccess: (dataArg) => { dataArg; }, error: 500, onError: (errorArg) => { errorArg; }, } : { data: true; onSuccess: (dataArg: boolean) => void; error: number; onError: (errorArg: number) => void; }
231+
232+
data: true,
233+
>data : true
234+
>true : true
235+
236+
onSuccess: (dataArg) => {
237+
>onSuccess : (dataArg: boolean) => void
238+
>(dataArg) => { dataArg; } : (dataArg: boolean) => void
239+
>dataArg : boolean
240+
241+
dataArg;
242+
>dataArg : boolean
243+
244+
},
245+
error: 500,
246+
>error : number
247+
>500 : 500
248+
249+
onError: (errorArg) => {
250+
>onError : (errorArg: number) => void
251+
>(errorArg) => { errorArg; } : (errorArg: number) => void
252+
>errorArg : number
253+
254+
errorArg;
255+
>errorArg : number
256+
257+
},
258+
},
259+
]);

tests/cases/compiler/reverseMappedIntersectionInference.ts

+29
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,32 @@ const res2 = withTuples([
6868
},
6969
},
7070
]);
71+
72+
type Tuple<T> = readonly [T, ...T[]];
73+
74+
declare function withTuplesConstraints<T extends Tuple<any>, E extends Tuple<any>>(
75+
arg: Results<T> & Errors<E>
76+
): [T, E];
77+
78+
const res3 = withTuplesConstraints([
79+
{
80+
data: "foo",
81+
onSuccess: (dataArg) => {
82+
dataArg;
83+
},
84+
error: 404,
85+
onError: (errorArg) => {
86+
errorArg;
87+
},
88+
},
89+
{
90+
data: true,
91+
onSuccess: (dataArg) => {
92+
dataArg;
93+
},
94+
error: 500,
95+
onError: (errorArg) => {
96+
errorArg;
97+
},
98+
},
99+
]);

0 commit comments

Comments
 (0)