mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 12:32:08 -06:00
Infer between generic mapped types before inferring from apparent type (#56640)
This commit is contained in:
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);
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
mappedTypeInferenceFromApparentType.ts(10,1): error TS2322: Type 'foo' is not assignable to type 'bar'.
|
||||
Types of parameters 'target' and 'source' are incompatible.
|
||||
Type '{ [K in keyof U]: Obj[K]; }' is not assignable to type '{ [K in keyof U]: U[K]; }'.
|
||||
Type 'Obj[K]' is not assignable to type 'U[K]'.
|
||||
Type 'Obj' is not assignable to type 'U'.
|
||||
'U' could be instantiated with an arbitrary type which could be unrelated to 'Obj'.
|
||||
|
||||
|
||||
==== mappedTypeInferenceFromApparentType.ts (1 errors) ====
|
||||
type Obj = {
|
||||
[s: string]: number;
|
||||
};
|
||||
|
||||
type foo = <T>(target: { [K in keyof T]: T[K] }) => void;
|
||||
type bar = <U extends string[]>(source: { [K in keyof U]: Obj[K] }) => void;
|
||||
|
||||
declare let f: foo;
|
||||
declare let b: bar;
|
||||
b = f;
|
||||
~
|
||||
!!! error TS2322: Type 'foo' is not assignable to type 'bar'.
|
||||
!!! error TS2322: Types of parameters 'target' and 'source' are incompatible.
|
||||
!!! error TS2322: Type '{ [K in keyof U]: Obj[K]; }' is not assignable to type '{ [K in keyof U]: U[K]; }'.
|
||||
!!! error TS2322: Type 'Obj[K]' is not assignable to type 'U[K]'.
|
||||
!!! error TS2322: Type 'Obj' is not assignable to type 'U'.
|
||||
!!! error TS2322: 'U' could be instantiated with an arbitrary type which could be unrelated to 'Obj'.
|
||||
@ -0,0 +1,41 @@
|
||||
//// [tests/cases/compiler/mappedTypeInferenceFromApparentType.ts] ////
|
||||
|
||||
=== mappedTypeInferenceFromApparentType.ts ===
|
||||
type Obj = {
|
||||
>Obj : Symbol(Obj, Decl(mappedTypeInferenceFromApparentType.ts, 0, 0))
|
||||
|
||||
[s: string]: number;
|
||||
>s : Symbol(s, Decl(mappedTypeInferenceFromApparentType.ts, 1, 5))
|
||||
|
||||
};
|
||||
|
||||
type foo = <T>(target: { [K in keyof T]: T[K] }) => void;
|
||||
>foo : Symbol(foo, Decl(mappedTypeInferenceFromApparentType.ts, 2, 2))
|
||||
>T : Symbol(T, Decl(mappedTypeInferenceFromApparentType.ts, 4, 12))
|
||||
>target : Symbol(target, Decl(mappedTypeInferenceFromApparentType.ts, 4, 15))
|
||||
>K : Symbol(K, Decl(mappedTypeInferenceFromApparentType.ts, 4, 26))
|
||||
>T : Symbol(T, Decl(mappedTypeInferenceFromApparentType.ts, 4, 12))
|
||||
>T : Symbol(T, Decl(mappedTypeInferenceFromApparentType.ts, 4, 12))
|
||||
>K : Symbol(K, Decl(mappedTypeInferenceFromApparentType.ts, 4, 26))
|
||||
|
||||
type bar = <U extends string[]>(source: { [K in keyof U]: Obj[K] }) => void;
|
||||
>bar : Symbol(bar, Decl(mappedTypeInferenceFromApparentType.ts, 4, 57))
|
||||
>U : Symbol(U, Decl(mappedTypeInferenceFromApparentType.ts, 5, 12))
|
||||
>source : Symbol(source, Decl(mappedTypeInferenceFromApparentType.ts, 5, 32))
|
||||
>K : Symbol(K, Decl(mappedTypeInferenceFromApparentType.ts, 5, 43))
|
||||
>U : Symbol(U, Decl(mappedTypeInferenceFromApparentType.ts, 5, 12))
|
||||
>Obj : Symbol(Obj, Decl(mappedTypeInferenceFromApparentType.ts, 0, 0))
|
||||
>K : Symbol(K, Decl(mappedTypeInferenceFromApparentType.ts, 5, 43))
|
||||
|
||||
declare let f: foo;
|
||||
>f : Symbol(f, Decl(mappedTypeInferenceFromApparentType.ts, 7, 11))
|
||||
>foo : Symbol(foo, Decl(mappedTypeInferenceFromApparentType.ts, 2, 2))
|
||||
|
||||
declare let b: bar;
|
||||
>b : Symbol(b, Decl(mappedTypeInferenceFromApparentType.ts, 8, 11))
|
||||
>bar : Symbol(bar, Decl(mappedTypeInferenceFromApparentType.ts, 4, 57))
|
||||
|
||||
b = f;
|
||||
>b : Symbol(b, Decl(mappedTypeInferenceFromApparentType.ts, 8, 11))
|
||||
>f : Symbol(f, Decl(mappedTypeInferenceFromApparentType.ts, 7, 11))
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
//// [tests/cases/compiler/mappedTypeInferenceFromApparentType.ts] ////
|
||||
|
||||
=== mappedTypeInferenceFromApparentType.ts ===
|
||||
type Obj = {
|
||||
>Obj : { [s: string]: number; }
|
||||
|
||||
[s: string]: number;
|
||||
>s : string
|
||||
|
||||
};
|
||||
|
||||
type foo = <T>(target: { [K in keyof T]: T[K] }) => void;
|
||||
>foo : <T>(target: { [K in keyof T]: T[K]; }) => void
|
||||
>target : { [K in keyof T]: T[K]; }
|
||||
|
||||
type bar = <U extends string[]>(source: { [K in keyof U]: Obj[K] }) => void;
|
||||
>bar : <U extends string[]>(source: { [K in keyof U]: Obj[K]; }) => void
|
||||
>source : { [K in keyof U]: Obj[K]; }
|
||||
|
||||
declare let f: foo;
|
||||
>f : foo
|
||||
|
||||
declare let b: bar;
|
||||
>b : bar
|
||||
|
||||
b = f;
|
||||
>b = f : foo
|
||||
>b : bar
|
||||
>f : foo
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
//// [tests/cases/compiler/mappedTypeInferenceToMappedType.ts] ////
|
||||
|
||||
=== mappedTypeInferenceToMappedType.ts ===
|
||||
// #56133
|
||||
|
||||
declare class Base<T> {
|
||||
>Base : Symbol(Base, Decl(mappedTypeInferenceToMappedType.ts, 0, 0))
|
||||
>T : Symbol(T, Decl(mappedTypeInferenceToMappedType.ts, 2, 19))
|
||||
|
||||
someProp: T;
|
||||
>someProp : Symbol(Base.someProp, Decl(mappedTypeInferenceToMappedType.ts, 2, 23))
|
||||
>T : Symbol(T, Decl(mappedTypeInferenceToMappedType.ts, 2, 19))
|
||||
|
||||
method<U extends unknown[]>(x: { [K in keyof U]: U[K] }): Base<U>;
|
||||
>method : Symbol(Base.method, Decl(mappedTypeInferenceToMappedType.ts, 3, 16))
|
||||
>U : Symbol(U, Decl(mappedTypeInferenceToMappedType.ts, 4, 11))
|
||||
>x : Symbol(x, Decl(mappedTypeInferenceToMappedType.ts, 4, 32))
|
||||
>K : Symbol(K, Decl(mappedTypeInferenceToMappedType.ts, 4, 38))
|
||||
>U : Symbol(U, Decl(mappedTypeInferenceToMappedType.ts, 4, 11))
|
||||
>U : Symbol(U, Decl(mappedTypeInferenceToMappedType.ts, 4, 11))
|
||||
>K : Symbol(K, Decl(mappedTypeInferenceToMappedType.ts, 4, 38))
|
||||
>Base : Symbol(Base, Decl(mappedTypeInferenceToMappedType.ts, 0, 0))
|
||||
>U : Symbol(U, Decl(mappedTypeInferenceToMappedType.ts, 4, 11))
|
||||
}
|
||||
|
||||
declare class Derived<T> extends Base<T> {
|
||||
>Derived : Symbol(Derived, Decl(mappedTypeInferenceToMappedType.ts, 5, 1))
|
||||
>T : Symbol(T, Decl(mappedTypeInferenceToMappedType.ts, 7, 22))
|
||||
>Base : Symbol(Base, Decl(mappedTypeInferenceToMappedType.ts, 0, 0))
|
||||
>T : Symbol(T, Decl(mappedTypeInferenceToMappedType.ts, 7, 22))
|
||||
|
||||
method<V extends unknown[]>(x: { [K in keyof V]: V[K] }): Base<V>;
|
||||
>method : Symbol(Derived.method, Decl(mappedTypeInferenceToMappedType.ts, 7, 42))
|
||||
>V : Symbol(V, Decl(mappedTypeInferenceToMappedType.ts, 8, 11))
|
||||
>x : Symbol(x, Decl(mappedTypeInferenceToMappedType.ts, 8, 32))
|
||||
>K : Symbol(K, Decl(mappedTypeInferenceToMappedType.ts, 8, 38))
|
||||
>V : Symbol(V, Decl(mappedTypeInferenceToMappedType.ts, 8, 11))
|
||||
>V : Symbol(V, Decl(mappedTypeInferenceToMappedType.ts, 8, 11))
|
||||
>K : Symbol(K, Decl(mappedTypeInferenceToMappedType.ts, 8, 38))
|
||||
>Base : Symbol(Base, Decl(mappedTypeInferenceToMappedType.ts, 0, 0))
|
||||
>V : Symbol(V, Decl(mappedTypeInferenceToMappedType.ts, 8, 11))
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
//// [tests/cases/compiler/mappedTypeInferenceToMappedType.ts] ////
|
||||
|
||||
=== mappedTypeInferenceToMappedType.ts ===
|
||||
// #56133
|
||||
|
||||
declare class Base<T> {
|
||||
>Base : Base<T>
|
||||
|
||||
someProp: T;
|
||||
>someProp : T
|
||||
|
||||
method<U extends unknown[]>(x: { [K in keyof U]: U[K] }): Base<U>;
|
||||
>method : <U extends unknown[]>(x: { [K in keyof U]: U[K]; }) => Base<U>
|
||||
>x : { [K in keyof U]: U[K]; }
|
||||
}
|
||||
|
||||
declare class Derived<T> extends Base<T> {
|
||||
>Derived : Derived<T>
|
||||
>Base : Base<T>
|
||||
|
||||
method<V extends unknown[]>(x: { [K in keyof V]: V[K] }): Base<V>;
|
||||
>method : <V extends unknown[]>(x: { [K in keyof V]: V[K]; }) => Base<V>
|
||||
>x : { [K in keyof V]: V[K]; }
|
||||
}
|
||||
13
tests/cases/compiler/mappedTypeInferenceFromApparentType.ts
Normal file
13
tests/cases/compiler/mappedTypeInferenceFromApparentType.ts
Normal file
@ -0,0 +1,13 @@
|
||||
// @strict: true
|
||||
// @noEmit: true
|
||||
|
||||
type Obj = {
|
||||
[s: string]: number;
|
||||
};
|
||||
|
||||
type foo = <T>(target: { [K in keyof T]: T[K] }) => void;
|
||||
type bar = <U extends string[]>(source: { [K in keyof U]: Obj[K] }) => void;
|
||||
|
||||
declare let f: foo;
|
||||
declare let b: bar;
|
||||
b = f;
|
||||
13
tests/cases/compiler/mappedTypeInferenceToMappedType.ts
Normal file
13
tests/cases/compiler/mappedTypeInferenceToMappedType.ts
Normal file
@ -0,0 +1,13 @@
|
||||
// @strict: true
|
||||
// @noEmit: true
|
||||
|
||||
// #56133
|
||||
|
||||
declare class Base<T> {
|
||||
someProp: T;
|
||||
method<U extends unknown[]>(x: { [K in keyof U]: U[K] }): Base<U>;
|
||||
}
|
||||
|
||||
declare class Derived<T> extends Base<T> {
|
||||
method<V extends unknown[]>(x: { [K in keyof V]: V[K] }): Base<V>;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user