Skip to content

Commit b8b34a3

Browse files
authored
Actually cache intermediate results in getBaseConstraint (microsoft#25417)
* Have getBaseConstraint push resolutions for the types it actually recurs on * Cache base constraints correctly
1 parent 64555aa commit b8b34a3

9 files changed

+240
-6
lines changed

src/compiler/checker.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,7 @@ namespace ts {
607607
ResolvedBaseConstructorType,
608608
DeclaredType,
609609
ResolvedReturnType,
610-
ResolvedBaseConstraint,
610+
ImmediateBaseConstraint,
611611
}
612612

613613
const enum CheckMode {
@@ -4255,8 +4255,8 @@ namespace ts {
42554255
if (propertyName === TypeSystemPropertyName.ResolvedReturnType) {
42564256
return !!(<Signature>target).resolvedReturnType;
42574257
}
4258-
if (propertyName === TypeSystemPropertyName.ResolvedBaseConstraint) {
4259-
const bc = (<TypeParameter | UnionOrIntersectionType>target).resolvedBaseConstraint;
4258+
if (propertyName === TypeSystemPropertyName.ImmediateBaseConstraint) {
4259+
const bc = (<Type>target).immediateBaseConstraint;
42604260
return !!bc && bc !== circularConstraintType;
42614261
}
42624262

@@ -6854,15 +6854,21 @@ namespace ts {
68546854
return type.resolvedBaseConstraint;
68556855

68566856
function getBaseConstraint(t: Type): Type | undefined {
6857-
if (!pushTypeResolution(t, TypeSystemPropertyName.ResolvedBaseConstraint)) {
6857+
if (t.immediateBaseConstraint) {
6858+
return t.immediateBaseConstraint === noConstraintType ? undefined : t.immediateBaseConstraint;
6859+
}
6860+
if (!pushTypeResolution(t, TypeSystemPropertyName.ImmediateBaseConstraint)) {
68586861
circular = true;
6862+
t.immediateBaseConstraint = circularConstraintType;
68596863
return undefined;
68606864
}
68616865
const result = computeBaseConstraint(getSimplifiedType(t));
68626866
if (!popTypeResolution()) {
68636867
circular = true;
6868+
t.immediateBaseConstraint = circularConstraintType;
68646869
return undefined;
68656870
}
6871+
t.immediateBaseConstraint = !result ? noConstraintType : result;
68666872
return result;
68676873
}
68686874

src/compiler/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3753,6 +3753,8 @@ namespace ts {
37533753
aliasTypeArguments?: ReadonlyArray<Type>; // Alias type arguments (if any)
37543754
/* @internal */
37553755
wildcardInstantiation?: Type; // Instantiation with type parameters mapped to wildcard type
3756+
/* @internal */
3757+
immediateBaseConstraint?: Type; // Immediate base constraint cache
37563758
}
37573759

37583760
/* @internal */

tests/baselines/reference/api/tsserverlibrary.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3259,6 +3259,7 @@ declare namespace ts {
32593259
aliasSymbol?: Symbol;
32603260
aliasTypeArguments?: ReadonlyArray<Type>;
32613261
wildcardInstantiation?: Type;
3262+
immediateBaseConstraint?: Type;
32623263
}
32633264
interface IntrinsicType extends Type {
32643265
intrinsicName: string;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//// [constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts]
2+
// https://github.com/Microsoft/TypeScript/issues/25379
3+
4+
interface Map<K, V> {
5+
// ...
6+
}
7+
8+
export type ImmutableTypes = IImmutableMap<any>;
9+
10+
export type ImmutableModel<T> = { [K in keyof T]: T[K] extends ImmutableTypes ? T[K] : never };
11+
12+
export interface IImmutableMap<T extends ImmutableModel<T>> extends Map<string, any> {
13+
set<K extends keyof T>(key: K, value: T[K]): IImmutableMap<T>;
14+
}
15+
16+
export type ImmutableTypes2 = IImmutableMap2<any>;
17+
type isImmutableType<T> = [T] extends [ImmutableTypes2] ? T : never;
18+
export type ImmutableModel2<T> = { [K in keyof T]: isImmutableType<T[K]> };
19+
export interface IImmutableMap2<T extends ImmutableModel2<T>> extends Map<string, any> {
20+
set<K extends keyof T>(key: K, value: T[K]): IImmutableMap2<T>;
21+
}
22+
23+
24+
//// [constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.js]
25+
"use strict";
26+
// https://github.com/Microsoft/TypeScript/issues/25379
27+
exports.__esModule = true;
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
=== tests/cases/compiler/constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts ===
2+
// https://github.com/Microsoft/TypeScript/issues/25379
3+
4+
interface Map<K, V> {
5+
>Map : Symbol(Map, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 0, 0))
6+
>K : Symbol(K, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 2, 14))
7+
>V : Symbol(V, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 2, 16))
8+
9+
// ...
10+
}
11+
12+
export type ImmutableTypes = IImmutableMap<any>;
13+
>ImmutableTypes : Symbol(ImmutableTypes, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 4, 1))
14+
>IImmutableMap : Symbol(IImmutableMap, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 8, 95))
15+
16+
export type ImmutableModel<T> = { [K in keyof T]: T[K] extends ImmutableTypes ? T[K] : never };
17+
>ImmutableModel : Symbol(ImmutableModel, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 6, 48))
18+
>T : Symbol(T, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 8, 27))
19+
>K : Symbol(K, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 8, 35))
20+
>T : Symbol(T, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 8, 27))
21+
>T : Symbol(T, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 8, 27))
22+
>K : Symbol(K, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 8, 35))
23+
>ImmutableTypes : Symbol(ImmutableTypes, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 4, 1))
24+
>T : Symbol(T, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 8, 27))
25+
>K : Symbol(K, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 8, 35))
26+
27+
export interface IImmutableMap<T extends ImmutableModel<T>> extends Map<string, any> {
28+
>IImmutableMap : Symbol(IImmutableMap, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 8, 95))
29+
>T : Symbol(T, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 10, 31))
30+
>ImmutableModel : Symbol(ImmutableModel, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 6, 48))
31+
>T : Symbol(T, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 10, 31))
32+
>Map : Symbol(Map, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 0, 0))
33+
34+
set<K extends keyof T>(key: K, value: T[K]): IImmutableMap<T>;
35+
>set : Symbol(IImmutableMap.set, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 10, 86))
36+
>K : Symbol(K, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 11, 8))
37+
>T : Symbol(T, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 10, 31))
38+
>key : Symbol(key, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 11, 27))
39+
>K : Symbol(K, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 11, 8))
40+
>value : Symbol(value, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 11, 34))
41+
>T : Symbol(T, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 10, 31))
42+
>K : Symbol(K, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 11, 8))
43+
>IImmutableMap : Symbol(IImmutableMap, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 8, 95))
44+
>T : Symbol(T, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 10, 31))
45+
}
46+
47+
export type ImmutableTypes2 = IImmutableMap2<any>;
48+
>ImmutableTypes2 : Symbol(ImmutableTypes2, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 12, 1))
49+
>IImmutableMap2 : Symbol(IImmutableMap2, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 16, 75))
50+
51+
type isImmutableType<T> = [T] extends [ImmutableTypes2] ? T : never;
52+
>isImmutableType : Symbol(isImmutableType, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 14, 50))
53+
>T : Symbol(T, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 15, 21))
54+
>T : Symbol(T, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 15, 21))
55+
>ImmutableTypes2 : Symbol(ImmutableTypes2, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 12, 1))
56+
>T : Symbol(T, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 15, 21))
57+
58+
export type ImmutableModel2<T> = { [K in keyof T]: isImmutableType<T[K]> };
59+
>ImmutableModel2 : Symbol(ImmutableModel2, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 15, 68))
60+
>T : Symbol(T, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 16, 28))
61+
>K : Symbol(K, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 16, 36))
62+
>T : Symbol(T, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 16, 28))
63+
>isImmutableType : Symbol(isImmutableType, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 14, 50))
64+
>T : Symbol(T, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 16, 28))
65+
>K : Symbol(K, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 16, 36))
66+
67+
export interface IImmutableMap2<T extends ImmutableModel2<T>> extends Map<string, any> {
68+
>IImmutableMap2 : Symbol(IImmutableMap2, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 16, 75))
69+
>T : Symbol(T, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 17, 32))
70+
>ImmutableModel2 : Symbol(ImmutableModel2, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 15, 68))
71+
>T : Symbol(T, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 17, 32))
72+
>Map : Symbol(Map, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 0, 0))
73+
74+
set<K extends keyof T>(key: K, value: T[K]): IImmutableMap2<T>;
75+
>set : Symbol(IImmutableMap2.set, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 17, 88))
76+
>K : Symbol(K, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 18, 8))
77+
>T : Symbol(T, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 17, 32))
78+
>key : Symbol(key, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 18, 27))
79+
>K : Symbol(K, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 18, 8))
80+
>value : Symbol(value, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 18, 34))
81+
>T : Symbol(T, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 17, 32))
82+
>K : Symbol(K, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 18, 8))
83+
>IImmutableMap2 : Symbol(IImmutableMap2, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 16, 75))
84+
>T : Symbol(T, Decl(constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts, 17, 32))
85+
}
86+
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
=== tests/cases/compiler/constraintOfRecursivelyMappedTypeWithConditionalIsResolvable.ts ===
2+
// https://github.com/Microsoft/TypeScript/issues/25379
3+
4+
interface Map<K, V> {
5+
>Map : Map<K, V>
6+
>K : K
7+
>V : V
8+
9+
// ...
10+
}
11+
12+
export type ImmutableTypes = IImmutableMap<any>;
13+
>ImmutableTypes : IImmutableMap<any>
14+
>IImmutableMap : IImmutableMap<T>
15+
16+
export type ImmutableModel<T> = { [K in keyof T]: T[K] extends ImmutableTypes ? T[K] : never };
17+
>ImmutableModel : ImmutableModel<T>
18+
>T : T
19+
>K : K
20+
>T : T
21+
>T : T
22+
>K : K
23+
>ImmutableTypes : IImmutableMap<any>
24+
>T : T
25+
>K : K
26+
27+
export interface IImmutableMap<T extends ImmutableModel<T>> extends Map<string, any> {
28+
>IImmutableMap : IImmutableMap<T>
29+
>T : T
30+
>ImmutableModel : ImmutableModel<T>
31+
>T : T
32+
>Map : Map<K, V>
33+
34+
set<K extends keyof T>(key: K, value: T[K]): IImmutableMap<T>;
35+
>set : <K extends keyof T>(key: K, value: T[K]) => IImmutableMap<T>
36+
>K : K
37+
>T : T
38+
>key : K
39+
>K : K
40+
>value : T[K]
41+
>T : T
42+
>K : K
43+
>IImmutableMap : IImmutableMap<T>
44+
>T : T
45+
}
46+
47+
export type ImmutableTypes2 = IImmutableMap2<any>;
48+
>ImmutableTypes2 : IImmutableMap2<any>
49+
>IImmutableMap2 : IImmutableMap2<T>
50+
51+
type isImmutableType<T> = [T] extends [ImmutableTypes2] ? T : never;
52+
>isImmutableType : isImmutableType<T>
53+
>T : T
54+
>T : T
55+
>ImmutableTypes2 : IImmutableMap2<any>
56+
>T : T
57+
58+
export type ImmutableModel2<T> = { [K in keyof T]: isImmutableType<T[K]> };
59+
>ImmutableModel2 : ImmutableModel2<T>
60+
>T : T
61+
>K : K
62+
>T : T
63+
>isImmutableType : isImmutableType<T>
64+
>T : T
65+
>K : K
66+
67+
export interface IImmutableMap2<T extends ImmutableModel2<T>> extends Map<string, any> {
68+
>IImmutableMap2 : IImmutableMap2<T>
69+
>T : T
70+
>ImmutableModel2 : ImmutableModel2<T>
71+
>T : T
72+
>Map : Map<K, V>
73+
74+
set<K extends keyof T>(key: K, value: T[K]): IImmutableMap2<T>;
75+
>set : <K extends keyof T>(key: K, value: T[K]) => IImmutableMap2<T>
76+
>K : K
77+
>T : T
78+
>key : K
79+
>K : K
80+
>value : T[K]
81+
>T : T
82+
>K : K
83+
>IImmutableMap2 : IImmutableMap2<T>
84+
>T : T
85+
}
86+
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1+
tests/cases/compiler/incorrectRecursiveMappedTypeConstraint.ts(2,24): error TS2313: Type parameter 'T' has a circular constraint.
12
tests/cases/compiler/incorrectRecursiveMappedTypeConstraint.ts(2,32): error TS2313: Type parameter 'P' has a circular constraint.
3+
tests/cases/compiler/incorrectRecursiveMappedTypeConstraint.ts(3,5): error TS2365: Operator '+=' cannot be applied to types 'number' and 'T[K]'.
24

35

4-
==== tests/cases/compiler/incorrectRecursiveMappedTypeConstraint.ts (1 errors) ====
6+
==== tests/cases/compiler/incorrectRecursiveMappedTypeConstraint.ts (3 errors) ====
57
// #17847
68
function sum<T extends { [P in T]: number }, K extends keyof T>(n: number, v: T, k: K) {
9+
~~~~~~~~~~~~~~~~~~~~
10+
!!! error TS2313: Type parameter 'T' has a circular constraint.
711
~
812
!!! error TS2313: Type parameter 'P' has a circular constraint.
913
n += v[k];
14+
~~~~~~~~~
15+
!!! error TS2365: Operator '+=' cannot be applied to types 'number' and 'T[K]'.
1016
}
1117

tests/baselines/reference/incorrectRecursiveMappedTypeConstraint.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ function sum<T extends { [P in T]: number }, K extends keyof T>(n: number, v: T,
1414
>K : K
1515

1616
n += v[k];
17-
>n += v[k] : number
17+
>n += v[k] : any
1818
>n : number
1919
>v[k] : T[K]
2020
>v : T
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// https://github.com/Microsoft/TypeScript/issues/25379
2+
3+
interface Map<K, V> {
4+
// ...
5+
}
6+
7+
export type ImmutableTypes = IImmutableMap<any>;
8+
9+
export type ImmutableModel<T> = { [K in keyof T]: T[K] extends ImmutableTypes ? T[K] : never };
10+
11+
export interface IImmutableMap<T extends ImmutableModel<T>> extends Map<string, any> {
12+
set<K extends keyof T>(key: K, value: T[K]): IImmutableMap<T>;
13+
}
14+
15+
export type ImmutableTypes2 = IImmutableMap2<any>;
16+
type isImmutableType<T> = [T] extends [ImmutableTypes2] ? T : never;
17+
export type ImmutableModel2<T> = { [K in keyof T]: isImmutableType<T[K]> };
18+
export interface IImmutableMap2<T extends ImmutableModel2<T>> extends Map<string, any> {
19+
set<K extends keyof T>(key: K, value: T[K]): IImmutableMap2<T>;
20+
}

0 commit comments

Comments
 (0)