mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-06-11 10:46:28 -05:00
Infer between generic mapped types before inferring from apparent type (#56640)
This commit is contained in:
committed by
GitHub
parent
9e0e9d35b9
commit
be20dbbbbb
@@ -25544,6 +25544,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
else {
|
||||
source = getReducedType(source);
|
||||
if (isGenericMappedType(source) && isGenericMappedType(target)) {
|
||||
invokeOnce(source, target, inferFromGenericMappedTypes);
|
||||
}
|
||||
if (!(priority & InferencePriority.NoConstraints && source.flags & (TypeFlags.Intersection | TypeFlags.Instantiable))) {
|
||||
const apparentSource = getApparentType(source);
|
||||
// getApparentType can return _any_ type, since an indexed access or conditional may simplify to any other type.
|
||||
@@ -25581,6 +25584,19 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
priority = savePriority;
|
||||
}
|
||||
|
||||
// Ensure an inference action is performed only once for the given source and target types.
|
||||
// This includes two things:
|
||||
// Avoiding inferring between the same pair of source and target types,
|
||||
// and avoiding circularly inferring between source and target types.
|
||||
// For an example of the last, consider if we are inferring between source type
|
||||
// `type Deep<T> = { next: Deep<Deep<T>> }` and target type `type Loop<U> = { next: Loop<U> }`.
|
||||
// We would then infer between the types of the `next` property: `Deep<Deep<T>>` = `{ next: Deep<Deep<Deep<T>>> }` and `Loop<U>` = `{ next: Loop<U> }`.
|
||||
// We will then infer again between the types of the `next` property:
|
||||
// `Deep<Deep<Deep<T>>>` and `Loop<U>`, and so on, such that we would be forever inferring
|
||||
// between instantiations of the same types `Deep` and `Loop`.
|
||||
// In particular, we would be inferring from increasingly deep instantiations of `Deep` to `Loop`,
|
||||
// such that we would go on inferring forever, even though we would never infer
|
||||
// between the same pair of types.
|
||||
function invokeOnce<Source extends Type, Target extends Type>(source: Source, target: Target, action: (source: Source, target: Target) => void) {
|
||||
const key = source.id + "," + target.id;
|
||||
const status = visited && visited.get(key);
|
||||
@@ -25888,6 +25904,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
}
|
||||
|
||||
function inferFromGenericMappedTypes(source: MappedType, target: MappedType) {
|
||||
// The source and target types are generic types { [P in S]: X } and { [P in T]: Y }, so we infer
|
||||
// from S to T and from X to Y.
|
||||
inferFromTypes(getConstraintTypeFromMappedType(source), getConstraintTypeFromMappedType(target));
|
||||
inferFromTypes(getTemplateTypeFromMappedType(source), getTemplateTypeFromMappedType(target));
|
||||
const sourceNameType = getNameTypeFromMappedType(source);
|
||||
const targetNameType = getNameTypeFromMappedType(target);
|
||||
if (sourceNameType && targetNameType) inferFromTypes(sourceNameType, targetNameType);
|
||||
}
|
||||
|
||||
function inferFromObjectTypes(source: Type, target: Type) {
|
||||
if (
|
||||
getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (
|
||||
@@ -25899,13 +25925,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return;
|
||||
}
|
||||
if (isGenericMappedType(source) && isGenericMappedType(target)) {
|
||||
// The source and target types are generic types { [P in S]: X } and { [P in T]: Y }, so we infer
|
||||
// from S to T and from X to Y.
|
||||
inferFromTypes(getConstraintTypeFromMappedType(source), getConstraintTypeFromMappedType(target));
|
||||
inferFromTypes(getTemplateTypeFromMappedType(source), getTemplateTypeFromMappedType(target));
|
||||
const sourceNameType = getNameTypeFromMappedType(source);
|
||||
const targetNameType = getNameTypeFromMappedType(target);
|
||||
if (sourceNameType && targetNameType) inferFromTypes(sourceNameType, targetNameType);
|
||||
inferFromGenericMappedTypes(source, target);
|
||||
}
|
||||
if (getObjectFlags(target) & ObjectFlags.Mapped && !(target as MappedType).declaration.nameType) {
|
||||
const constraintType = getConstraintTypeFromMappedType(target as MappedType);
|
||||
|
||||
Reference in New Issue
Block a user