Merge pull request #20370 from Microsoft/cutoff-inference-for-recursive-mapped-types
Cut off inference for recursive mapped types
This commit is contained in:
commit
8d7c2a2a77
5 changed files with 90 additions and 4 deletions
|
@ -11118,7 +11118,7 @@ namespace ts {
|
||||||
* property is computed by inferring from the source property type to X for the type
|
* property is computed by inferring from the source property type to X for the type
|
||||||
* variable T[P] (i.e. we treat the type T[P] as the type variable we're inferring for).
|
* variable T[P] (i.e. we treat the type T[P] as the type variable we're inferring for).
|
||||||
*/
|
*/
|
||||||
function inferTypeForHomomorphicMappedType(source: Type, target: MappedType): Type {
|
function inferTypeForHomomorphicMappedType(source: Type, target: MappedType, mappedTypeStack: string[]): Type {
|
||||||
const properties = getPropertiesOfType(source);
|
const properties = getPropertiesOfType(source);
|
||||||
let indexInfo = getIndexInfoOfType(source, IndexKind.String);
|
let indexInfo = getIndexInfoOfType(source, IndexKind.String);
|
||||||
if (properties.length === 0 && !indexInfo) {
|
if (properties.length === 0 && !indexInfo) {
|
||||||
|
@ -11151,7 +11151,7 @@ namespace ts {
|
||||||
|
|
||||||
function inferTargetType(sourceType: Type): Type {
|
function inferTargetType(sourceType: Type): Type {
|
||||||
inference.candidates = undefined;
|
inference.candidates = undefined;
|
||||||
inferTypes(inferences, sourceType, templateType);
|
inferTypes(inferences, sourceType, templateType, 0, mappedTypeStack);
|
||||||
return inference.candidates ? getUnionType(inference.candidates, /*subtypeReduction*/ true) : emptyObjectType;
|
return inference.candidates ? getUnionType(inference.candidates, /*subtypeReduction*/ true) : emptyObjectType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11169,7 +11169,7 @@ namespace ts {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0) {
|
function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0, mappedTypeStack?: string[]) {
|
||||||
let symbolStack: Symbol[];
|
let symbolStack: Symbol[];
|
||||||
let visited: Map<boolean>;
|
let visited: Map<boolean>;
|
||||||
inferFromTypes(originalSource, originalTarget);
|
inferFromTypes(originalSource, originalTarget);
|
||||||
|
@ -11386,7 +11386,13 @@ namespace ts {
|
||||||
// such that direct inferences to T get priority over inferences to Partial<T>, for example.
|
// such that direct inferences to T get priority over inferences to Partial<T>, for example.
|
||||||
const inference = getInferenceInfoForType((<IndexType>constraintType).type);
|
const inference = getInferenceInfoForType((<IndexType>constraintType).type);
|
||||||
if (inference && !inference.isFixed) {
|
if (inference && !inference.isFixed) {
|
||||||
const inferredType = inferTypeForHomomorphicMappedType(source, <MappedType>target);
|
const key = (source.symbol ? getSymbolId(source.symbol) + "," : "") + getSymbolId(target.symbol);
|
||||||
|
if (contains(mappedTypeStack, key)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
(mappedTypeStack || (mappedTypeStack = [])).push(key);
|
||||||
|
const inferredType = inferTypeForHomomorphicMappedType(source, <MappedType>target, mappedTypeStack);
|
||||||
|
mappedTypeStack.pop();
|
||||||
if (inferredType) {
|
if (inferredType) {
|
||||||
const savePriority = priority;
|
const savePriority = priority;
|
||||||
priority |= InferencePriority.MappedType;
|
priority |= InferencePriority.MappedType;
|
||||||
|
|
10
tests/baselines/reference/mappedTypeRecursiveInference.js
Normal file
10
tests/baselines/reference/mappedTypeRecursiveInference.js
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
//// [mappedTypeRecursiveInference.ts]
|
||||||
|
interface A { a: A }
|
||||||
|
declare let a: A;
|
||||||
|
type Deep<T> = { [K in keyof T]: Deep<T[K]> }
|
||||||
|
declare function foo<T>(deep: Deep<T>): T;
|
||||||
|
const out = foo(a);
|
||||||
|
|
||||||
|
|
||||||
|
//// [mappedTypeRecursiveInference.js]
|
||||||
|
var out = foo(a);
|
|
@ -0,0 +1,32 @@
|
||||||
|
=== tests/cases/compiler/mappedTypeRecursiveInference.ts ===
|
||||||
|
interface A { a: A }
|
||||||
|
>A : Symbol(A, Decl(mappedTypeRecursiveInference.ts, 0, 0))
|
||||||
|
>a : Symbol(A.a, Decl(mappedTypeRecursiveInference.ts, 0, 13))
|
||||||
|
>A : Symbol(A, Decl(mappedTypeRecursiveInference.ts, 0, 0))
|
||||||
|
|
||||||
|
declare let a: A;
|
||||||
|
>a : Symbol(a, Decl(mappedTypeRecursiveInference.ts, 1, 11))
|
||||||
|
>A : Symbol(A, Decl(mappedTypeRecursiveInference.ts, 0, 0))
|
||||||
|
|
||||||
|
type Deep<T> = { [K in keyof T]: Deep<T[K]> }
|
||||||
|
>Deep : Symbol(Deep, Decl(mappedTypeRecursiveInference.ts, 1, 17))
|
||||||
|
>T : Symbol(T, Decl(mappedTypeRecursiveInference.ts, 2, 10))
|
||||||
|
>K : Symbol(K, Decl(mappedTypeRecursiveInference.ts, 2, 18))
|
||||||
|
>T : Symbol(T, Decl(mappedTypeRecursiveInference.ts, 2, 10))
|
||||||
|
>Deep : Symbol(Deep, Decl(mappedTypeRecursiveInference.ts, 1, 17))
|
||||||
|
>T : Symbol(T, Decl(mappedTypeRecursiveInference.ts, 2, 10))
|
||||||
|
>K : Symbol(K, Decl(mappedTypeRecursiveInference.ts, 2, 18))
|
||||||
|
|
||||||
|
declare function foo<T>(deep: Deep<T>): T;
|
||||||
|
>foo : Symbol(foo, Decl(mappedTypeRecursiveInference.ts, 2, 45))
|
||||||
|
>T : Symbol(T, Decl(mappedTypeRecursiveInference.ts, 3, 21))
|
||||||
|
>deep : Symbol(deep, Decl(mappedTypeRecursiveInference.ts, 3, 24))
|
||||||
|
>Deep : Symbol(Deep, Decl(mappedTypeRecursiveInference.ts, 1, 17))
|
||||||
|
>T : Symbol(T, Decl(mappedTypeRecursiveInference.ts, 3, 21))
|
||||||
|
>T : Symbol(T, Decl(mappedTypeRecursiveInference.ts, 3, 21))
|
||||||
|
|
||||||
|
const out = foo(a);
|
||||||
|
>out : Symbol(out, Decl(mappedTypeRecursiveInference.ts, 4, 5))
|
||||||
|
>foo : Symbol(foo, Decl(mappedTypeRecursiveInference.ts, 2, 45))
|
||||||
|
>a : Symbol(a, Decl(mappedTypeRecursiveInference.ts, 1, 11))
|
||||||
|
|
33
tests/baselines/reference/mappedTypeRecursiveInference.types
Normal file
33
tests/baselines/reference/mappedTypeRecursiveInference.types
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
=== tests/cases/compiler/mappedTypeRecursiveInference.ts ===
|
||||||
|
interface A { a: A }
|
||||||
|
>A : A
|
||||||
|
>a : A
|
||||||
|
>A : A
|
||||||
|
|
||||||
|
declare let a: A;
|
||||||
|
>a : A
|
||||||
|
>A : A
|
||||||
|
|
||||||
|
type Deep<T> = { [K in keyof T]: Deep<T[K]> }
|
||||||
|
>Deep : Deep<T>
|
||||||
|
>T : T
|
||||||
|
>K : K
|
||||||
|
>T : T
|
||||||
|
>Deep : Deep<T>
|
||||||
|
>T : T
|
||||||
|
>K : K
|
||||||
|
|
||||||
|
declare function foo<T>(deep: Deep<T>): T;
|
||||||
|
>foo : <T>(deep: Deep<T>) => T
|
||||||
|
>T : T
|
||||||
|
>deep : Deep<T>
|
||||||
|
>Deep : Deep<T>
|
||||||
|
>T : T
|
||||||
|
>T : T
|
||||||
|
|
||||||
|
const out = foo(a);
|
||||||
|
>out : { a: {}; }
|
||||||
|
>foo(a) : { a: {}; }
|
||||||
|
>foo : <T>(deep: Deep<T>) => T
|
||||||
|
>a : A
|
||||||
|
|
5
tests/cases/compiler/mappedTypeRecursiveInference.ts
Normal file
5
tests/cases/compiler/mappedTypeRecursiveInference.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
interface A { a: A }
|
||||||
|
declare let a: A;
|
||||||
|
type Deep<T> = { [K in keyof T]: Deep<T[K]> }
|
||||||
|
declare function foo<T>(deep: Deep<T>): T;
|
||||||
|
const out = foo(a);
|
Loading…
Reference in a new issue