Merge pull request #31141 from Microsoft/fixInferenceToIndexedAccessWithSubstitution
Fix inference to indexed access type containing substitution type
This commit is contained in:
commit
9509a54e66
5 changed files with 167 additions and 5 deletions
|
@ -10349,8 +10349,16 @@ namespace ts {
|
||||||
return links.resolvedType;
|
return links.resolvedType;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getActualTypeVariable(type: Type) {
|
function getActualTypeVariable(type: Type): Type {
|
||||||
return type.flags & TypeFlags.Substitution ? (<SubstitutionType>type).typeVariable : type;
|
if (type.flags & TypeFlags.Substitution) {
|
||||||
|
return (<SubstitutionType>type).typeVariable;
|
||||||
|
}
|
||||||
|
if (type.flags & TypeFlags.IndexedAccess && (
|
||||||
|
(<IndexedAccessType>type).objectType.flags & TypeFlags.Substitution ||
|
||||||
|
(<IndexedAccessType>type).indexType.flags & TypeFlags.Substitution)) {
|
||||||
|
return getIndexedAccessType(getActualTypeVariable((<IndexedAccessType>type).objectType), getActualTypeVariable((<IndexedAccessType>type).indexType));
|
||||||
|
}
|
||||||
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -15030,6 +15038,9 @@ namespace ts {
|
||||||
target = removeTypesFromUnionOrIntersection(<UnionOrIntersectionType>target, matchingTypes);
|
target = removeTypesFromUnionOrIntersection(<UnionOrIntersectionType>target, matchingTypes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (target.flags & (TypeFlags.IndexedAccess | TypeFlags.Substitution)) {
|
||||||
|
target = getActualTypeVariable(target);
|
||||||
|
}
|
||||||
if (target.flags & TypeFlags.TypeVariable) {
|
if (target.flags & TypeFlags.TypeVariable) {
|
||||||
// If target is a type parameter, make an inference, unless the source type contains
|
// If target is a type parameter, make an inference, unless the source type contains
|
||||||
// the anyFunctionType (the wildcard type that's used to avoid contextually typing functions).
|
// the anyFunctionType (the wildcard type that's used to avoid contextually typing functions).
|
||||||
|
@ -15091,9 +15102,6 @@ namespace ts {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (target.flags & TypeFlags.Substitution) {
|
|
||||||
inferFromTypes(source, (target as SubstitutionType).typeVariable);
|
|
||||||
}
|
|
||||||
if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target) {
|
if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target) {
|
||||||
// If source and target are references to the same generic type, infer from type arguments
|
// If source and target are references to the same generic type, infer from type arguments
|
||||||
const sourceTypes = (<TypeReference>source).typeArguments || emptyArray;
|
const sourceTypes = (<TypeReference>source).typeArguments || emptyArray;
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
//// [substitutionTypesInIndexedAccessTypes.ts]
|
||||||
|
// Repro from #31086
|
||||||
|
|
||||||
|
type UserArgs = {
|
||||||
|
select?: boolean
|
||||||
|
};
|
||||||
|
|
||||||
|
type Subset<T, U> = { [key in keyof T]: key extends keyof U ? T[key] : never };
|
||||||
|
|
||||||
|
declare function withBoundary<T extends UserArgs>(args?: Subset<T, UserArgs>): T;
|
||||||
|
declare function withoutBoundary<T extends UserArgs>(args?: T): T;
|
||||||
|
|
||||||
|
const boundaryResult = withBoundary({
|
||||||
|
select: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const withoutBoundaryResult = withoutBoundary({
|
||||||
|
select: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
//// [substitutionTypesInIndexedAccessTypes.js]
|
||||||
|
"use strict";
|
||||||
|
// Repro from #31086
|
||||||
|
var boundaryResult = withBoundary({
|
||||||
|
select: true
|
||||||
|
});
|
||||||
|
var withoutBoundaryResult = withoutBoundary({
|
||||||
|
select: true
|
||||||
|
});
|
|
@ -0,0 +1,58 @@
|
||||||
|
=== tests/cases/compiler/substitutionTypesInIndexedAccessTypes.ts ===
|
||||||
|
// Repro from #31086
|
||||||
|
|
||||||
|
type UserArgs = {
|
||||||
|
>UserArgs : Symbol(UserArgs, Decl(substitutionTypesInIndexedAccessTypes.ts, 0, 0))
|
||||||
|
|
||||||
|
select?: boolean
|
||||||
|
>select : Symbol(select, Decl(substitutionTypesInIndexedAccessTypes.ts, 2, 17))
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
type Subset<T, U> = { [key in keyof T]: key extends keyof U ? T[key] : never };
|
||||||
|
>Subset : Symbol(Subset, Decl(substitutionTypesInIndexedAccessTypes.ts, 4, 2))
|
||||||
|
>T : Symbol(T, Decl(substitutionTypesInIndexedAccessTypes.ts, 6, 12))
|
||||||
|
>U : Symbol(U, Decl(substitutionTypesInIndexedAccessTypes.ts, 6, 14))
|
||||||
|
>key : Symbol(key, Decl(substitutionTypesInIndexedAccessTypes.ts, 6, 23))
|
||||||
|
>T : Symbol(T, Decl(substitutionTypesInIndexedAccessTypes.ts, 6, 12))
|
||||||
|
>key : Symbol(key, Decl(substitutionTypesInIndexedAccessTypes.ts, 6, 23))
|
||||||
|
>U : Symbol(U, Decl(substitutionTypesInIndexedAccessTypes.ts, 6, 14))
|
||||||
|
>T : Symbol(T, Decl(substitutionTypesInIndexedAccessTypes.ts, 6, 12))
|
||||||
|
>key : Symbol(key, Decl(substitutionTypesInIndexedAccessTypes.ts, 6, 23))
|
||||||
|
|
||||||
|
declare function withBoundary<T extends UserArgs>(args?: Subset<T, UserArgs>): T;
|
||||||
|
>withBoundary : Symbol(withBoundary, Decl(substitutionTypesInIndexedAccessTypes.ts, 6, 79))
|
||||||
|
>T : Symbol(T, Decl(substitutionTypesInIndexedAccessTypes.ts, 8, 30))
|
||||||
|
>UserArgs : Symbol(UserArgs, Decl(substitutionTypesInIndexedAccessTypes.ts, 0, 0))
|
||||||
|
>args : Symbol(args, Decl(substitutionTypesInIndexedAccessTypes.ts, 8, 50))
|
||||||
|
>Subset : Symbol(Subset, Decl(substitutionTypesInIndexedAccessTypes.ts, 4, 2))
|
||||||
|
>T : Symbol(T, Decl(substitutionTypesInIndexedAccessTypes.ts, 8, 30))
|
||||||
|
>UserArgs : Symbol(UserArgs, Decl(substitutionTypesInIndexedAccessTypes.ts, 0, 0))
|
||||||
|
>T : Symbol(T, Decl(substitutionTypesInIndexedAccessTypes.ts, 8, 30))
|
||||||
|
|
||||||
|
declare function withoutBoundary<T extends UserArgs>(args?: T): T;
|
||||||
|
>withoutBoundary : Symbol(withoutBoundary, Decl(substitutionTypesInIndexedAccessTypes.ts, 8, 81))
|
||||||
|
>T : Symbol(T, Decl(substitutionTypesInIndexedAccessTypes.ts, 9, 33))
|
||||||
|
>UserArgs : Symbol(UserArgs, Decl(substitutionTypesInIndexedAccessTypes.ts, 0, 0))
|
||||||
|
>args : Symbol(args, Decl(substitutionTypesInIndexedAccessTypes.ts, 9, 53))
|
||||||
|
>T : Symbol(T, Decl(substitutionTypesInIndexedAccessTypes.ts, 9, 33))
|
||||||
|
>T : Symbol(T, Decl(substitutionTypesInIndexedAccessTypes.ts, 9, 33))
|
||||||
|
|
||||||
|
const boundaryResult = withBoundary({
|
||||||
|
>boundaryResult : Symbol(boundaryResult, Decl(substitutionTypesInIndexedAccessTypes.ts, 11, 5))
|
||||||
|
>withBoundary : Symbol(withBoundary, Decl(substitutionTypesInIndexedAccessTypes.ts, 6, 79))
|
||||||
|
|
||||||
|
select: true,
|
||||||
|
>select : Symbol(select, Decl(substitutionTypesInIndexedAccessTypes.ts, 11, 37))
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
const withoutBoundaryResult = withoutBoundary({
|
||||||
|
>withoutBoundaryResult : Symbol(withoutBoundaryResult, Decl(substitutionTypesInIndexedAccessTypes.ts, 15, 5))
|
||||||
|
>withoutBoundary : Symbol(withoutBoundary, Decl(substitutionTypesInIndexedAccessTypes.ts, 8, 81))
|
||||||
|
|
||||||
|
select: true,
|
||||||
|
>select : Symbol(select, Decl(substitutionTypesInIndexedAccessTypes.ts, 15, 47))
|
||||||
|
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
=== tests/cases/compiler/substitutionTypesInIndexedAccessTypes.ts ===
|
||||||
|
// Repro from #31086
|
||||||
|
|
||||||
|
type UserArgs = {
|
||||||
|
>UserArgs : UserArgs
|
||||||
|
|
||||||
|
select?: boolean
|
||||||
|
>select : boolean | undefined
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
type Subset<T, U> = { [key in keyof T]: key extends keyof U ? T[key] : never };
|
||||||
|
>Subset : Subset<T, U>
|
||||||
|
|
||||||
|
declare function withBoundary<T extends UserArgs>(args?: Subset<T, UserArgs>): T;
|
||||||
|
>withBoundary : <T extends UserArgs>(args?: Subset<T, UserArgs> | undefined) => T
|
||||||
|
>args : Subset<T, UserArgs> | undefined
|
||||||
|
|
||||||
|
declare function withoutBoundary<T extends UserArgs>(args?: T): T;
|
||||||
|
>withoutBoundary : <T extends UserArgs>(args?: T | undefined) => T
|
||||||
|
>args : T | undefined
|
||||||
|
|
||||||
|
const boundaryResult = withBoundary({
|
||||||
|
>boundaryResult : { select: true; }
|
||||||
|
>withBoundary({ select: true,}) : { select: true; }
|
||||||
|
>withBoundary : <T extends UserArgs>(args?: Subset<T, UserArgs> | undefined) => T
|
||||||
|
>{ select: true,} : { select: true; }
|
||||||
|
|
||||||
|
select: true,
|
||||||
|
>select : true
|
||||||
|
>true : true
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
const withoutBoundaryResult = withoutBoundary({
|
||||||
|
>withoutBoundaryResult : { select: true; }
|
||||||
|
>withoutBoundary({ select: true,}) : { select: true; }
|
||||||
|
>withoutBoundary : <T extends UserArgs>(args?: T | undefined) => T
|
||||||
|
>{ select: true,} : { select: true; }
|
||||||
|
|
||||||
|
select: true,
|
||||||
|
>select : true
|
||||||
|
>true : true
|
||||||
|
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
// @strict: true
|
||||||
|
|
||||||
|
// Repro from #31086
|
||||||
|
|
||||||
|
type UserArgs = {
|
||||||
|
select?: boolean
|
||||||
|
};
|
||||||
|
|
||||||
|
type Subset<T, U> = { [key in keyof T]: key extends keyof U ? T[key] : never };
|
||||||
|
|
||||||
|
declare function withBoundary<T extends UserArgs>(args?: Subset<T, UserArgs>): T;
|
||||||
|
declare function withoutBoundary<T extends UserArgs>(args?: T): T;
|
||||||
|
|
||||||
|
const boundaryResult = withBoundary({
|
||||||
|
select: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const withoutBoundaryResult = withoutBoundary({
|
||||||
|
select: true,
|
||||||
|
});
|
Loading…
Reference in a new issue