Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add isDeeplyNestedType logic to getResolvedBaseConstraint #40971

Merged
merged 5 commits into from
Oct 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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);
Expand Down
5 changes: 1 addition & 4 deletions tests/baselines/reference/infiniteConstraints.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.

Original file line number Diff line number Diff line change
Expand Up @@ -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>'.
Expand All @@ -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> =
Expand Down Expand Up @@ -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>'.
Expand Down
22 changes: 22 additions & 0 deletions tests/baselines/reference/templateLiteralTypes1.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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'

46 changes: 46 additions & 0 deletions tests/baselines/reference/templateLiteralTypes1.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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";
79 changes: 79 additions & 0 deletions tests/baselines/reference/templateLiteralTypes1.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -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))

58 changes: 58 additions & 0 deletions tests/baselines/reference/templateLiteralTypes1.types
Original file line number Diff line number Diff line change
Expand Up @@ -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"

22 changes: 22 additions & 0 deletions tests/cases/conformance/types/literal/templateLiteralTypes1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'