Skip to content

Commit

Permalink
Avoid getting apparent type of intersected mapped types in contextual…
Browse files Browse the repository at this point in the history
… typing
  • Loading branch information
Andarist committed Mar 20, 2023
1 parent 53acfd3 commit 56f12bc
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 1 deletion.
11 changes: 10 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29252,6 +29252,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
);
}

function getApparentTypeOfInstantiatedContextualType(type: Type) {
return getObjectFlags(type) & ObjectFlags.Mapped ? type : getApparentType(type);
}

// Return the contextual type for a given expression node. During overload resolution, a contextual type may temporarily
// be "pushed" onto a node using the contextualType property.
function getApparentTypeOfContextualType(node: Expression | MethodDeclaration, contextFlags: ContextFlags | undefined): Type | undefined {
Expand All @@ -29266,7 +29270,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// That would evaluate mapped types with array or tuple type constraints too eagerly
// and thus it would prevent `getTypeOfPropertyOfContextualType` from obtaining per-position contextual type for elements of array literal expressions.
// Apparent type of other mapped types is already the mapped type itself so we can just avoid calling `getApparentType` here for all mapped types.
t => getObjectFlags(t) & ObjectFlags.Mapped ? t : getApparentType(t),
t => {
if (t.flags & TypeFlags.Intersection) {
return getIntersectionType(map((t as IntersectionType).types, getApparentTypeOfInstantiatedContextualType));
}
return getApparentTypeOfInstantiatedContextualType(t);
},
/*noReductions*/ true
);
return apparentType.flags & TypeFlags.Union && isObjectLiteralExpression(node) ? discriminateContextualTypeByObjectMembers(node, apparentType as UnionType) :
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,3 +186,79 @@ const res2 = withTuples([
},
]);

type Tuple<T> = readonly [T, ...T[]];
>Tuple : Symbol(Tuple, Decl(reverseMappedIntersectionInference.ts, 66, 3))
>T : Symbol(T, Decl(reverseMappedIntersectionInference.ts, 68, 11))
>T : Symbol(T, Decl(reverseMappedIntersectionInference.ts, 68, 11))
>T : Symbol(T, Decl(reverseMappedIntersectionInference.ts, 68, 11))

declare function withTuplesConstraints<T extends Tuple<any>, E extends Tuple<any>>(
>withTuplesConstraints : Symbol(withTuplesConstraints, Decl(reverseMappedIntersectionInference.ts, 68, 37))
>T : Symbol(T, Decl(reverseMappedIntersectionInference.ts, 70, 39))
>Tuple : Symbol(Tuple, Decl(reverseMappedIntersectionInference.ts, 66, 3))
>E : Symbol(E, Decl(reverseMappedIntersectionInference.ts, 70, 60))
>Tuple : Symbol(Tuple, Decl(reverseMappedIntersectionInference.ts, 66, 3))

arg: Results<T> & Errors<E>
>arg : Symbol(arg, Decl(reverseMappedIntersectionInference.ts, 70, 83))
>Results : Symbol(Results, Decl(reverseMappedIntersectionInference.ts, 0, 0))
>T : Symbol(T, Decl(reverseMappedIntersectionInference.ts, 70, 39))
>Errors : Symbol(Errors, Decl(reverseMappedIntersectionInference.ts, 5, 2))
>E : Symbol(E, Decl(reverseMappedIntersectionInference.ts, 70, 60))

): [T, E];
>T : Symbol(T, Decl(reverseMappedIntersectionInference.ts, 70, 39))
>E : Symbol(E, Decl(reverseMappedIntersectionInference.ts, 70, 60))

const res3 = withTuplesConstraints([
>res3 : Symbol(res3, Decl(reverseMappedIntersectionInference.ts, 74, 5))
>withTuplesConstraints : Symbol(withTuplesConstraints, Decl(reverseMappedIntersectionInference.ts, 68, 37))
{
data: "foo",
>data : Symbol(data, Decl(reverseMappedIntersectionInference.ts, 75, 3))

onSuccess: (dataArg) => {
>onSuccess : Symbol(onSuccess, Decl(reverseMappedIntersectionInference.ts, 76, 16))
>dataArg : Symbol(dataArg, Decl(reverseMappedIntersectionInference.ts, 77, 16))

dataArg;
>dataArg : Symbol(dataArg, Decl(reverseMappedIntersectionInference.ts, 77, 16))

},
error: 404,
>error : Symbol(error, Decl(reverseMappedIntersectionInference.ts, 79, 6))

onError: (errorArg) => {
>onError : Symbol(onError, Decl(reverseMappedIntersectionInference.ts, 80, 15))
>errorArg : Symbol(errorArg, Decl(reverseMappedIntersectionInference.ts, 81, 14))

errorArg;
>errorArg : Symbol(errorArg, Decl(reverseMappedIntersectionInference.ts, 81, 14))

},
},
{
data: true,
>data : Symbol(data, Decl(reverseMappedIntersectionInference.ts, 85, 3))

onSuccess: (dataArg) => {
>onSuccess : Symbol(onSuccess, Decl(reverseMappedIntersectionInference.ts, 86, 15))
>dataArg : Symbol(dataArg, Decl(reverseMappedIntersectionInference.ts, 87, 16))

dataArg;
>dataArg : Symbol(dataArg, Decl(reverseMappedIntersectionInference.ts, 87, 16))

},
error: 500,
>error : Symbol(error, Decl(reverseMappedIntersectionInference.ts, 89, 6))

onError: (errorArg) => {
>onError : Symbol(onError, Decl(reverseMappedIntersectionInference.ts, 90, 15))
>errorArg : Symbol(errorArg, Decl(reverseMappedIntersectionInference.ts, 91, 14))

errorArg;
>errorArg : Symbol(errorArg, Decl(reverseMappedIntersectionInference.ts, 91, 14))

},
},
]);
77 changes: 77 additions & 0 deletions tests/baselines/reference/reverseMappedIntersectionInference.types
Original file line number Diff line number Diff line change
Expand Up @@ -180,3 +180,80 @@ const res2 = withTuples([
},
]);

type Tuple<T> = readonly [T, ...T[]];
>Tuple : Tuple<T>

declare function withTuplesConstraints<T extends Tuple<any>, E extends Tuple<any>>(
>withTuplesConstraints : <T extends Tuple<any>, E extends Tuple<any>>(arg: Results<T> & Errors<E>) => [T, E]

arg: Results<T> & Errors<E>
>arg : Results<T> & Errors<E>

): [T, E];

const res3 = withTuplesConstraints([
>res3 : [[string, boolean], [number, number]]
>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]]
>withTuplesConstraints : <T extends Tuple<any>, E extends Tuple<any>>(arg: Results<T> & Errors<E>) => [T, E]
>[ { 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; }]
{
>{ data: "foo", onSuccess: (dataArg) => { dataArg; }, error: 404, onError: (errorArg) => { errorArg; }, } : { data: string; onSuccess: (dataArg: string) => void; error: number; onError: (errorArg: number) => void; }

data: "foo",
>data : string
>"foo" : "foo"

onSuccess: (dataArg) => {
>onSuccess : (dataArg: string) => void
>(dataArg) => { dataArg; } : (dataArg: string) => void
>dataArg : string

dataArg;
>dataArg : string

},
error: 404,
>error : number
>404 : 404

onError: (errorArg) => {
>onError : (errorArg: number) => void
>(errorArg) => { errorArg; } : (errorArg: number) => void
>errorArg : number

errorArg;
>errorArg : number

},
},
{
>{ data: true, onSuccess: (dataArg) => { dataArg; }, error: 500, onError: (errorArg) => { errorArg; }, } : { data: true; onSuccess: (dataArg: boolean) => void; error: number; onError: (errorArg: number) => void; }

data: true,
>data : true
>true : true

onSuccess: (dataArg) => {
>onSuccess : (dataArg: boolean) => void
>(dataArg) => { dataArg; } : (dataArg: boolean) => void
>dataArg : boolean

dataArg;
>dataArg : boolean

},
error: 500,
>error : number
>500 : 500

onError: (errorArg) => {
>onError : (errorArg: number) => void
>(errorArg) => { errorArg; } : (errorArg: number) => void
>errorArg : number

errorArg;
>errorArg : number

},
},
]);
29 changes: 29 additions & 0 deletions tests/cases/compiler/reverseMappedIntersectionInference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,32 @@ const res2 = withTuples([
},
},
]);

type Tuple<T> = readonly [T, ...T[]];

declare function withTuplesConstraints<T extends Tuple<any>, E extends Tuple<any>>(
arg: Results<T> & Errors<E>
): [T, E];

const res3 = withTuplesConstraints([
{
data: "foo",
onSuccess: (dataArg) => {
dataArg;
},
error: 404,
onError: (errorArg) => {
errorArg;
},
},
{
data: true,
onSuccess: (dataArg) => {
dataArg;
},
error: 500,
onError: (errorArg) => {
errorArg;
},
},
]);

0 comments on commit 56f12bc

Please sign in to comment.