Add isDeeplyNestedType logic to getResolvedBaseConstraint (#40971)

* Add isDeeplyNestedType logic to getResolvedBaseConstraint

* Accept new baselines

* Add regression test

* Accept new baselines

* Fix lint issue
This commit is contained in:
Anders Hejlsberg
2020-10-07 05:50:06 -07:00
committed by GitHub
parent 14c731689e
commit f34220980b
8 changed files with 242 additions and 13 deletions

View File

@@ -10913,9 +10913,12 @@ namespace ts {
* circularly references the type variable.
*/
function getResolvedBaseConstraint(type: InstantiableType | UnionOrIntersectionType): Type {
if (type.resolvedBaseConstraint) {
return type.resolvedBaseConstraint;
}
let nonTerminating = false;
return type.resolvedBaseConstraint ||
(type.resolvedBaseConstraint = getTypeWithThisArgument(getImmediateBaseConstraint(type), type));
const stack: Type[] = [];
return type.resolvedBaseConstraint = getTypeWithThisArgument(getImmediateBaseConstraint(type), type);
function getImmediateBaseConstraint(t: Type): Type {
if (!t.immediateBaseConstraint) {
@@ -10932,9 +10935,14 @@ namespace ts {
nonTerminating = true;
return t.immediateBaseConstraint = noConstraintType;
}
constraintDepth++;
let result = computeBaseConstraint(getSimplifiedType(t, /*writing*/ false));
constraintDepth--;
let result;
if (!isDeeplyNestedType(t, stack, stack.length)) {
stack.push(t);
constraintDepth++;
result = computeBaseConstraint(getSimplifiedType(t, /*writing*/ false));
constraintDepth--;
stack.pop();
}
if (!popTypeResolution()) {
if (t.flags & TypeFlags.TypeParameter) {
const errorNode = getConstraintDeclaration(<TypeParameter>t);

View File

@@ -2,10 +2,9 @@ tests/cases/compiler/infiniteConstraints.ts(4,37): error TS2536: Type '"val"' ca
tests/cases/compiler/infiniteConstraints.ts(31,43): error TS2322: Type 'Record<"val", "dup">' is not assignable to type 'never'.
tests/cases/compiler/infiniteConstraints.ts(31,63): error TS2322: Type 'Record<"val", "dup">' is not assignable to type 'never'.
tests/cases/compiler/infiniteConstraints.ts(36,71): error TS2536: Type '"foo"' cannot be used to index type 'T[keyof T]'.
tests/cases/compiler/infiniteConstraints.ts(48,16): error TS2589: Type instantiation is excessively deep and possibly infinite.
==== tests/cases/compiler/infiniteConstraints.ts (5 errors) ====
==== tests/cases/compiler/infiniteConstraints.ts (4 errors) ====
// Both of the following types trigger the recursion limiter in getImmediateBaseConstraint
type T1<B extends { [K in keyof B]: Extract<B[Exclude<keyof B, K>], { val: string }>["val"] }> = B;
@@ -64,6 +63,4 @@ tests/cases/compiler/infiniteConstraints.ts(48,16): error TS2589: Type instantia
type Conv<T, U = T> =
{ 0: [T]; 1: Prepend<T, Conv<ExactExtract<U, T>>>;}[U extends T ? 0 : 1];
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2589: Type instantiation is excessively deep and possibly infinite.

View File

@@ -21,7 +21,6 @@ tests/cases/compiler/recursiveConditionalTypes.ts(50,5): error TS2322: Type 'Tup
Type 'number extends N ? number[] : _TupleOf<number, N, []>' is not assignable to type 'TupleOf<number, M>'.
Type 'number[] | _TupleOf<number, N, []>' is not assignable to type 'TupleOf<number, M>'.
Type 'number[]' is not assignable to type 'TupleOf<number, M>'.
tests/cases/compiler/recursiveConditionalTypes.ts(116,5): error TS2589: Type instantiation is excessively deep and possibly infinite.
tests/cases/compiler/recursiveConditionalTypes.ts(116,9): error TS2345: Argument of type 'Grow2<[], T>' is not assignable to parameter of type 'Grow1<[], T>'.
Type '[] | Grow2<[string], T>' is not assignable to type 'Grow1<[], T>'.
Type '[]' is not assignable to type 'Grow1<[], T>'.
@@ -32,7 +31,7 @@ tests/cases/compiler/recursiveConditionalTypes.ts(116,9): error TS2345: Argument
Type 'string' is not assignable to type 'number'.
==== tests/cases/compiler/recursiveConditionalTypes.ts (10 errors) ====
==== tests/cases/compiler/recursiveConditionalTypes.ts (9 errors) ====
// Awaiting promises
type Awaited<T> =
@@ -180,8 +179,6 @@ tests/cases/compiler/recursiveConditionalTypes.ts(116,9): error TS2345: Argument
function f21<T extends number>(x: Grow1<[], T>, y: Grow2<[], T>) {
f21(y, x); // Error
~~~~~~~~~
!!! error TS2589: Type instantiation is excessively deep and possibly infinite.
~
!!! error TS2345: Argument of type 'Grow2<[], T>' is not assignable to parameter of type 'Grow1<[], T>'.
!!! error TS2345: Type '[] | Grow2<[string], T>' is not assignable to type 'Grow1<[], T>'.

View File

@@ -241,4 +241,26 @@ tests/cases/conformance/types/literal/templateLiteralTypes1.ts(205,16): error TS
[true, true] extends [IsNegative<T>, IsNegative<Q>] ? 'Every thing is ok!' : ['strange', IsNegative<T>, IsNegative<Q>];
type BB = AA<-2, -2>;
// Repro from #40970
type PathKeys<T> =
T extends readonly any[] ? Extract<keyof T, `${number}`> | SubKeys<T, Extract<keyof T, `${number}`>> :
T extends object ? Extract<keyof T, string> | SubKeys<T, Extract<keyof T, string>> :
never;
type SubKeys<T, K extends string> = K extends keyof T ? `${K}.${PathKeys<T[K]>}` : never;
declare function getProp2<T, P extends PathKeys<T>>(obj: T, path: P): PropType<T, P>;
const obj2 = {
name: 'John',
age: 42,
cars: [
{ make: 'Ford', age: 10 },
{ make: 'Trabant', age: 35 }
]
} as const;
let make = getProp2(obj2, 'cars.1.make'); // 'Trabant'

View File

@@ -213,6 +213,28 @@ type AA<T extends number, Q extends number> =
[true, true] extends [IsNegative<T>, IsNegative<Q>] ? 'Every thing is ok!' : ['strange', IsNegative<T>, IsNegative<Q>];
type BB = AA<-2, -2>;
// Repro from #40970
type PathKeys<T> =
T extends readonly any[] ? Extract<keyof T, `${number}`> | SubKeys<T, Extract<keyof T, `${number}`>> :
T extends object ? Extract<keyof T, string> | SubKeys<T, Extract<keyof T, string>> :
never;
type SubKeys<T, K extends string> = K extends keyof T ? `${K}.${PathKeys<T[K]>}` : never;
declare function getProp2<T, P extends PathKeys<T>>(obj: T, path: P): PropType<T, P>;
const obj2 = {
name: 'John',
age: 42,
cars: [
{ make: 'Ford', age: 10 },
{ make: 'Trabant', age: 35 }
]
} as const;
let make = getProp2(obj2, 'cars.1.make'); // 'Trabant'
//// [templateLiteralTypes1.js]
@@ -243,6 +265,15 @@ getPropValue(obj, 'a.b'); // {c: number, d: string }
getPropValue(obj, 'a.b.d'); // string
getPropValue(obj, 'a.b.x'); // unknown
getPropValue(obj, s); // unknown
var obj2 = {
name: 'John',
age: 42,
cars: [
{ make: 'Ford', age: 10 },
{ make: 'Trabant', age: 35 }
]
};
var make = getProp2(obj2, 'cars.1.make'); // 'Trabant'
//// [templateLiteralTypes1.d.ts]
@@ -468,3 +499,18 @@ declare type AA<T extends number, Q extends number> = [
true
] extends [IsNegative<T>, IsNegative<Q>] ? 'Every thing is ok!' : ['strange', IsNegative<T>, IsNegative<Q>];
declare type BB = AA<-2, -2>;
declare type PathKeys<T> = T extends readonly any[] ? Extract<keyof T, `${number}`> | SubKeys<T, Extract<keyof T, `${number}`>> : T extends object ? Extract<keyof T, string> | SubKeys<T, Extract<keyof T, string>> : never;
declare type SubKeys<T, K extends string> = K extends keyof T ? `${K}.${PathKeys<T[K]>}` : never;
declare function getProp2<T, P extends PathKeys<T>>(obj: T, path: P): PropType<T, P>;
declare const obj2: {
readonly name: "John";
readonly age: 42;
readonly cars: readonly [{
readonly make: "Ford";
readonly age: 10;
}, {
readonly make: "Trabant";
readonly age: 35;
}];
};
declare let make: "Trabant";

View File

@@ -869,3 +869,82 @@ type BB = AA<-2, -2>;
>BB : Symbol(BB, Decl(templateLiteralTypes1.ts, 211, 123))
>AA : Symbol(AA, Decl(templateLiteralTypes1.ts, 208, 79))
// Repro from #40970
type PathKeys<T> =
>PathKeys : Symbol(PathKeys, Decl(templateLiteralTypes1.ts, 213, 21))
>T : Symbol(T, Decl(templateLiteralTypes1.ts, 217, 14))
T extends readonly any[] ? Extract<keyof T, `${number}`> | SubKeys<T, Extract<keyof T, `${number}`>> :
>T : Symbol(T, Decl(templateLiteralTypes1.ts, 217, 14))
>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(templateLiteralTypes1.ts, 217, 14))
>SubKeys : Symbol(SubKeys, Decl(templateLiteralTypes1.ts, 220, 10))
>T : Symbol(T, Decl(templateLiteralTypes1.ts, 217, 14))
>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(templateLiteralTypes1.ts, 217, 14))
T extends object ? Extract<keyof T, string> | SubKeys<T, Extract<keyof T, string>> :
>T : Symbol(T, Decl(templateLiteralTypes1.ts, 217, 14))
>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(templateLiteralTypes1.ts, 217, 14))
>SubKeys : Symbol(SubKeys, Decl(templateLiteralTypes1.ts, 220, 10))
>T : Symbol(T, Decl(templateLiteralTypes1.ts, 217, 14))
>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(templateLiteralTypes1.ts, 217, 14))
never;
type SubKeys<T, K extends string> = K extends keyof T ? `${K}.${PathKeys<T[K]>}` : never;
>SubKeys : Symbol(SubKeys, Decl(templateLiteralTypes1.ts, 220, 10))
>T : Symbol(T, Decl(templateLiteralTypes1.ts, 222, 13))
>K : Symbol(K, Decl(templateLiteralTypes1.ts, 222, 15))
>K : Symbol(K, Decl(templateLiteralTypes1.ts, 222, 15))
>T : Symbol(T, Decl(templateLiteralTypes1.ts, 222, 13))
>K : Symbol(K, Decl(templateLiteralTypes1.ts, 222, 15))
>PathKeys : Symbol(PathKeys, Decl(templateLiteralTypes1.ts, 213, 21))
>T : Symbol(T, Decl(templateLiteralTypes1.ts, 222, 13))
>K : Symbol(K, Decl(templateLiteralTypes1.ts, 222, 15))
declare function getProp2<T, P extends PathKeys<T>>(obj: T, path: P): PropType<T, P>;
>getProp2 : Symbol(getProp2, Decl(templateLiteralTypes1.ts, 222, 89))
>T : Symbol(T, Decl(templateLiteralTypes1.ts, 224, 26))
>P : Symbol(P, Decl(templateLiteralTypes1.ts, 224, 28))
>PathKeys : Symbol(PathKeys, Decl(templateLiteralTypes1.ts, 213, 21))
>T : Symbol(T, Decl(templateLiteralTypes1.ts, 224, 26))
>obj : Symbol(obj, Decl(templateLiteralTypes1.ts, 224, 52))
>T : Symbol(T, Decl(templateLiteralTypes1.ts, 224, 26))
>path : Symbol(path, Decl(templateLiteralTypes1.ts, 224, 59))
>P : Symbol(P, Decl(templateLiteralTypes1.ts, 224, 28))
>PropType : Symbol(PropType, Decl(templateLiteralTypes1.ts, 138, 69))
>T : Symbol(T, Decl(templateLiteralTypes1.ts, 224, 26))
>P : Symbol(P, Decl(templateLiteralTypes1.ts, 224, 28))
const obj2 = {
>obj2 : Symbol(obj2, Decl(templateLiteralTypes1.ts, 226, 5))
name: 'John',
>name : Symbol(name, Decl(templateLiteralTypes1.ts, 226, 14))
age: 42,
>age : Symbol(age, Decl(templateLiteralTypes1.ts, 227, 17))
cars: [
>cars : Symbol(cars, Decl(templateLiteralTypes1.ts, 228, 12))
{ make: 'Ford', age: 10 },
>make : Symbol(make, Decl(templateLiteralTypes1.ts, 230, 9))
>age : Symbol(age, Decl(templateLiteralTypes1.ts, 230, 23))
{ make: 'Trabant', age: 35 }
>make : Symbol(make, Decl(templateLiteralTypes1.ts, 231, 9))
>age : Symbol(age, Decl(templateLiteralTypes1.ts, 231, 26))
]
} as const;
let make = getProp2(obj2, 'cars.1.make'); // 'Trabant'
>make : Symbol(make, Decl(templateLiteralTypes1.ts, 235, 3))
>getProp2 : Symbol(getProp2, Decl(templateLiteralTypes1.ts, 222, 89))
>obj2 : Symbol(obj2, Decl(templateLiteralTypes1.ts, 226, 5))

View File

@@ -536,3 +536,61 @@ type BB = AA<-2, -2>;
>-2 : -2
>2 : 2
// Repro from #40970
type PathKeys<T> =
>PathKeys : PathKeys<T>
T extends readonly any[] ? Extract<keyof T, `${number}`> | SubKeys<T, Extract<keyof T, `${number}`>> :
T extends object ? Extract<keyof T, string> | SubKeys<T, Extract<keyof T, string>> :
never;
type SubKeys<T, K extends string> = K extends keyof T ? `${K}.${PathKeys<T[K]>}` : never;
>SubKeys : SubKeys<T, K>
declare function getProp2<T, P extends PathKeys<T>>(obj: T, path: P): PropType<T, P>;
>getProp2 : <T, P extends PathKeys<T>>(obj: T, path: P) => PropType<T, P>
>obj : T
>path : P
const obj2 = {
>obj2 : { readonly name: "John"; readonly age: 42; readonly cars: readonly [{ readonly make: "Ford"; readonly age: 10; }, { readonly make: "Trabant"; readonly age: 35; }]; }
>{ name: 'John', age: 42, cars: [ { make: 'Ford', age: 10 }, { make: 'Trabant', age: 35 } ]} as const : { readonly name: "John"; readonly age: 42; readonly cars: readonly [{ readonly make: "Ford"; readonly age: 10; }, { readonly make: "Trabant"; readonly age: 35; }]; }
>{ name: 'John', age: 42, cars: [ { make: 'Ford', age: 10 }, { make: 'Trabant', age: 35 } ]} : { readonly name: "John"; readonly age: 42; readonly cars: readonly [{ readonly make: "Ford"; readonly age: 10; }, { readonly make: "Trabant"; readonly age: 35; }]; }
name: 'John',
>name : "John"
>'John' : "John"
age: 42,
>age : 42
>42 : 42
cars: [
>cars : readonly [{ readonly make: "Ford"; readonly age: 10; }, { readonly make: "Trabant"; readonly age: 35; }]
>[ { make: 'Ford', age: 10 }, { make: 'Trabant', age: 35 } ] : readonly [{ readonly make: "Ford"; readonly age: 10; }, { readonly make: "Trabant"; readonly age: 35; }]
{ make: 'Ford', age: 10 },
>{ make: 'Ford', age: 10 } : { readonly make: "Ford"; readonly age: 10; }
>make : "Ford"
>'Ford' : "Ford"
>age : 10
>10 : 10
{ make: 'Trabant', age: 35 }
>{ make: 'Trabant', age: 35 } : { readonly make: "Trabant"; readonly age: 35; }
>make : "Trabant"
>'Trabant' : "Trabant"
>age : 35
>35 : 35
]
} as const;
let make = getProp2(obj2, 'cars.1.make'); // 'Trabant'
>make : "Trabant"
>getProp2(obj2, 'cars.1.make') : "Trabant"
>getProp2 : <T, P extends PathKeys<T>>(obj: T, path: P) => PropType<T, P>
>obj2 : { readonly name: "John"; readonly age: 42; readonly cars: readonly [{ readonly make: "Ford"; readonly age: 10; }, { readonly make: "Trabant"; readonly age: 35; }]; }
>'cars.1.make' : "cars.1.make"

View File

@@ -215,3 +215,25 @@ type AA<T extends number, Q extends number> =
[true, true] extends [IsNegative<T>, IsNegative<Q>] ? 'Every thing is ok!' : ['strange', IsNegative<T>, IsNegative<Q>];
type BB = AA<-2, -2>;
// Repro from #40970
type PathKeys<T> =
T extends readonly any[] ? Extract<keyof T, `${number}`> | SubKeys<T, Extract<keyof T, `${number}`>> :
T extends object ? Extract<keyof T, string> | SubKeys<T, Extract<keyof T, string>> :
never;
type SubKeys<T, K extends string> = K extends keyof T ? `${K}.${PathKeys<T[K]>}` : never;
declare function getProp2<T, P extends PathKeys<T>>(obj: T, path: P): PropType<T, P>;
const obj2 = {
name: 'John',
age: 42,
cars: [
{ make: 'Ford', age: 10 },
{ make: 'Trabant', age: 35 }
]
} as const;
let make = getProp2(obj2, 'cars.1.make'); // 'Trabant'