Skip to content

Commit c25749f

Browse files
committed
Significant improvements to higherKindedTypes.
Improvements basically come from treating GenericTypeParameters as both a TypeParameter and a GenericType, which means they are also treated as an interface type since GenericType extends InterfaceType. Treating GenericTypeParameters as InterfaceTypes works by making it look like an interface that declares no members of its own but inherits members from its constraint if it has one. There also are changes related to inferring GenericTypeParameters where they are not fully erased in getBaseSignature because we need to be able to infer `T<_T>` from something like `T<_T> extends Functor<_T, T>` in order to capture the fact that it references itself in its constraint. See class `InvalidFunctor2` and `expectErrorD1` in `higherKindedTypesLift.ts` for an example of this.
1 parent 244f7f7 commit c25749f

9 files changed

+515
-363
lines changed

src/compiler/binder.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1409,6 +1409,7 @@ namespace ts {
14091409
case SyntaxKind.ModuleDeclaration:
14101410
case SyntaxKind.TypeAliasDeclaration:
14111411
case SyntaxKind.MappedType:
1412+
case SyntaxKind.TypeParameter:
14121413
return ContainerFlags.IsContainer | ContainerFlags.HasLocals;
14131414

14141415
case SyntaxKind.SourceFile:
@@ -1530,6 +1531,7 @@ namespace ts {
15301531
case SyntaxKind.JSDocCallbackTag:
15311532
case SyntaxKind.TypeAliasDeclaration:
15321533
case SyntaxKind.MappedType:
1534+
case SyntaxKind.TypeParameter:
15331535
// All the children of these container types are never visible through another
15341536
// symbol (i.e. through another symbol's 'exports' or 'members'). Instead,
15351537
// they're only accessed 'lexically' (i.e. from code that exists underneath
@@ -2713,12 +2715,6 @@ namespace ts {
27132715
bindAnonymousDeclaration(node, SymbolFlags.TypeParameter, getDeclarationName(node)!); // TODO: GH#18217
27142716
}
27152717
}
2716-
else if (node.parent.kind === SyntaxKind.TypeParameter) {
2717-
if (!node.parent.locals) {
2718-
node.parent.locals = createSymbolTable();
2719-
}
2720-
declareSymbol(node.parent.locals, node.parent.symbol, node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes);
2721-
}
27222718
else {
27232719
declareSymbolAndAddToSymbolTable(node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes);
27242720
}

src/compiler/checker.ts

Lines changed: 218 additions & 112 deletions
Large diffs are not rendered by default.

src/compiler/types.ts

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3766,6 +3766,8 @@ namespace ts {
37663766
aliasTypeArguments?: Type[]; // Alias type arguments (if any)
37673767
/* @internal */
37683768
wildcardInstantiation?: Type; // Instantiation with type parameters mapped to wildcard type
3769+
/* @internal */
3770+
wrapperType?: WrapperType; // wrapper type for this type
37693771
}
37703772

37713773
/* @internal */
@@ -3814,6 +3816,10 @@ namespace ts {
38143816
ReverseMapped = 1 << 11, // Object contains a property from a reverse-mapped type
38153817
JsxAttributes = 1 << 12, // Jsx attributes type
38163818
MarkerType = 1 << 13, // Marker type used for variance probing
3819+
GenericTypeParameter = 1 << 14, // uninstantiated generic type parameter (meaning a type parameter that has its own type parameters)
3820+
/* @internal */
3821+
Wrapper = 1 << 15, // wrapper for mapped GenericTypeParameters, which is needed to avoid instantiation order problems in some cases in getInferredType()
3822+
38173823
ClassOrInterface = Class | Interface
38183824
}
38193825

@@ -3856,8 +3862,9 @@ namespace ts {
38563862
* explicit "this" argument.
38573863
*/
38583864
export interface TypeReference extends ObjectType {
3859-
target: GenericType; // Type reference target
3860-
typeArguments?: Type[]; // Type reference type arguments (undefined if none)
3865+
target: GenericType; // Type reference target
3866+
typeArguments?: Type[]; // Type reference type arguments (undefined if none)
3867+
typeParameterReference?: boolean; // true if target is a GenericTypeParameter but NOT true if this is the GenericTypeParameter (ie if this === target)
38613868
}
38623869

38633870
/* @internal */
@@ -3869,7 +3876,7 @@ namespace ts {
38693876
Independent = 4, // Unwitnessed type parameter
38703877
}
38713878

3872-
// Generic class and interface types
3879+
// Generic class, interface and type parameter types
38733880
export interface GenericType extends InterfaceType, TypeReference {
38743881
/* @internal */
38753882
instantiations: Map<TypeReference>; // Generic instantiation cache
@@ -3986,19 +3993,28 @@ namespace ts {
39863993
/* @internal */
39873994
default?: Type;
39883995
/* @internal */
3989-
target?: TypeParameter; // Instantiation target
3996+
original?: TypeParameter; // The type parameter this type parameter was cloned from
39903997
/* @internal */
3991-
mapper?: TypeMapper; // Instantiation mapper
3998+
mapper?: TypeMapper; // mapper for cloned type parameters
39923999
/* @internal */
39934000
isThisType?: boolean;
39944001
/* @internal */
3995-
resolvedDefaultType?: Type;
3996-
/* @internal */
3997-
typeParameters?: TypeParameter[];
4002+
isGeneric?: boolean;
4003+
}
4004+
4005+
// Generic type parameters (TypeFlags.TypeParameter, TypeFlags.Object, ObjectFlags.GenericTypeParameter)
4006+
export interface GenericTypeParameter extends GenericType, TypeParameter, InterfaceTypeWithDeclaredMembers {
4007+
localTypeParameters: TypeParameter[]; // GenericTypeParameters always have localTypeParameters since that's what distinguishes them from non-generic type parameters.
39984008
/* @internal */
3999-
typeArguments?: TypeParameter[]; // Only set for references
4009+
original?: GenericTypeParameter; // The type parameter this type parameter was cloned from
40004010
/* @internal */
4001-
genericTarget?: TypeParameter; // This is the original generic type parameter a type parameter reference points to
4011+
isGeneric: true;
4012+
}
4013+
4014+
/* @internal */
4015+
// ObjectFlags.Wrapper
4016+
export interface WrapperType extends ObjectType {
4017+
wrappedType: Type;
40024018
}
40034019

40044020
// Indexed access types (TypeFlags.IndexedAccess)

tests/baselines/reference/higherKindedTypes.errors.txt

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
1-
tests/cases/compiler/higherKindedTypes.ts(37,5): error TS2719: Type 'F' is not assignable to type 'F'. Two different types with this name exist, but they are unrelated.
2-
Type 'Functor<A, F>' is not assignable to type 'F'.
3-
Types of property 'map' are incompatible.
4-
Type '<B>(f: (a: A) => B) => F' is not assignable to type '<B>(f: (a: B) => B) => F'.
5-
Types of parameters 'f' and 'f' are incompatible.
6-
Types of parameters 'a' and 'a' are incompatible.
7-
Type 'A' is not assignable to type 'B'.
8-
tests/cases/compiler/higherKindedTypes.ts(42,23): error TS2339: Property 'map' does not exist on type 'F'.
1+
tests/cases/compiler/higherKindedTypes.ts(37,5): error TS2322: Type 'F<A>' is not assignable to type 'F<B>'.
2+
Type 'A' is not assignable to type 'B'.
3+
tests/cases/compiler/higherKindedTypes.ts(42,23): error TS2339: Property 'map' does not exist on type 'F<A>'.
94

105

116
==== tests/cases/compiler/higherKindedTypes.ts (2 errors) ====
@@ -47,20 +42,15 @@ tests/cases/compiler/higherKindedTypes.ts(42,23): error TS2339: Property 'map' d
4742
function staticMapBadImplementation<F<_T> extends Functor<_T, F>, A, B>(fa: F<A>, f: (a: A) => B): F<B> {
4843
return fa;
4944
~~~~~~~~~~
50-
!!! error TS2719: Type 'F' is not assignable to type 'F'. Two different types with this name exist, but they are unrelated.
51-
!!! error TS2719: Type 'Functor<A, F>' is not assignable to type 'F'.
52-
!!! error TS2719: Types of property 'map' are incompatible.
53-
!!! error TS2719: Type '<B>(f: (a: A) => B) => F' is not assignable to type '<B>(f: (a: B) => B) => F'.
54-
!!! error TS2719: Types of parameters 'f' and 'f' are incompatible.
55-
!!! error TS2719: Types of parameters 'a' and 'a' are incompatible.
56-
!!! error TS2719: Type 'A' is not assignable to type 'B'.
45+
!!! error TS2322: Type 'F<A>' is not assignable to type 'F<B>'.
46+
!!! error TS2322: Type 'A' is not assignable to type 'B'.
5747
}
5848

5949
function staticMapNoConstraint<F<_T>, A, B>(fa: F<A>, f: (a: A) => B): F<B> {
6050
// expect error here since F has no constraint so we have no idea what shape it will be
6151
const result = fa.map(f);
6252
~~~
63-
!!! error TS2339: Property 'map' does not exist on type 'F'.
53+
!!! error TS2339: Property 'map' does not exist on type 'F<A>'.
6454
return result;
6555
}
6656

tests/baselines/reference/higherKindedTypes.types

Lines changed: 37 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
=== tests/cases/compiler/higherKindedTypes.ts ===
22
interface Functor<A, Container<_T>> {
3-
>Functor : Functor<A, Container>
3+
>Functor : Functor<A, Container<_T>>
44
>A : A
5-
>Container : Container
5+
>Container : Container<_T>
66
>_T : _T
77

88
map<B>(f: (a: A) => B): Container<B>;
9-
>map : <B>(f: (a: A) => B) => Container
9+
>map : <B>(f: (a: A) => B) => Container<B>
1010
>B : B
1111
>f : (a: A) => B
1212
>a : A
1313
>A : A
1414
>B : B
15-
>Container : Container
15+
>Container : Container<_T>
1616
>B : B
1717
}
1818

1919
interface FunctorX<A> extends Functor<A, FunctorX> {
2020
>FunctorX : FunctorX<A>
2121
>A : A
22-
>Functor : Functor<A, Container>
22+
>Functor : Functor<A, Container<_T>>
2323
>A : A
2424
>FunctorX : FunctorX<A>
2525

@@ -40,7 +40,7 @@ interface FunctorX<A> extends Functor<A, FunctorX> {
4040
interface FunctorY<A> extends Functor<A, FunctorY> {
4141
>FunctorY : FunctorY<A>
4242
>A : A
43-
>Functor : Functor<A, Container>
43+
>Functor : Functor<A, Container<_T>>
4444
>A : A
4545
>FunctorY : FunctorY<A>
4646

@@ -135,81 +135,81 @@ const expectY2: FunctorY<string[]> = resultY2;
135135

136136

137137
function staticMap<F<_T> extends Functor<_T, F>, A, B>(fa: F<A>, f: (a: A) => B): F<B> {
138-
>staticMap : <F extends Functor<{}, F>, A, B>(fa: F, f: (a: A) => B) => F
139-
>F : F
138+
>staticMap : <F extends Functor<_T, F<_T>>, A, B>(fa: F<A>, f: (a: A) => B) => F<B>
139+
>F : F<_T>
140140
>_T : _T
141-
>Functor : Functor<A, Container>
141+
>Functor : Functor<A, Container<_T>>
142142
>_T : _T
143-
>F : F
143+
>F : F<_T>
144144
>A : A
145145
>B : B
146-
>fa : F
147-
>F : F
146+
>fa : F<A>
147+
>F : F<_T>
148148
>A : A
149149
>f : (a: A) => B
150150
>a : A
151151
>A : A
152152
>B : B
153-
>F : F
153+
>F : F<_T>
154154
>B : B
155155

156156
const result = fa.map(f);
157-
>result : F
158-
>fa.map(f) : F
159-
>fa.map : <B>(f: (a: A) => B) => F
160-
>fa : F
161-
>map : <B>(f: (a: A) => B) => F
157+
>result : F<B>
158+
>fa.map(f) : F<B>
159+
>fa.map : <B>(f: (a: A) => B) => F<B>
160+
>fa : F<A>
161+
>map : <B>(f: (a: A) => B) => F<B>
162162
>f : (a: A) => B
163163

164164
return result;
165-
>result : F
165+
>result : F<B>
166166
}
167167

168168
function staticMapBadImplementation<F<_T> extends Functor<_T, F>, A, B>(fa: F<A>, f: (a: A) => B): F<B> {
169-
>staticMapBadImplementation : <F extends Functor<{}, F>, A, B>(fa: F, f: (a: A) => B) => F
170-
>F : F
169+
>staticMapBadImplementation : <F extends Functor<_T, F<_T>>, A, B>(fa: F<A>, f: (a: A) => B) => F<B>
170+
>F : F<_T>
171171
>_T : _T
172-
>Functor : Functor<A, Container>
172+
>Functor : Functor<A, Container<_T>>
173173
>_T : _T
174-
>F : F
174+
>F : F<_T>
175175
>A : A
176176
>B : B
177-
>fa : F
178-
>F : F
177+
>fa : F<A>
178+
>F : F<_T>
179179
>A : A
180180
>f : (a: A) => B
181181
>a : A
182182
>A : A
183183
>B : B
184-
>F : F
184+
>F : F<_T>
185185
>B : B
186186

187187
return fa;
188-
>fa : F
188+
>fa : F<A>
189189
}
190190

191191
function staticMapNoConstraint<F<_T>, A, B>(fa: F<A>, f: (a: A) => B): F<B> {
192-
>staticMapNoConstraint : <F, A, B>(fa: F, f: (a: A) => B) => F
193-
>F : F
192+
>staticMapNoConstraint : <F, A, B>(fa: F<A>, f: (a: A) => B) => F<B>
193+
>F : F<_T>
194194
>_T : _T
195195
>A : A
196196
>B : B
197-
>fa : F
198-
>F : F
197+
>fa : F<A>
198+
>F : F<_T>
199199
>A : A
200200
>f : (a: A) => B
201201
>a : A
202202
>A : A
203203
>B : B
204-
>F : F
204+
>F : F<_T>
205205
>B : B
206206

207207
// expect error here since F has no constraint so we have no idea what shape it will be
208208
const result = fa.map(f);
209209
>result : any
210210
>fa.map(f) : any
211211
>fa.map : any
212-
>fa : F
212+
>fa : F<A>
213213
>map : any
214214
>f : (a: A) => B
215215

@@ -220,7 +220,7 @@ function staticMapNoConstraint<F<_T>, A, B>(fa: F<A>, f: (a: A) => B): F<B> {
220220
const resultX3 = staticMap(initialX, val => val.length);
221221
>resultX3 : FunctorX<number>
222222
>staticMap(initialX, val => val.length) : FunctorX<number>
223-
>staticMap : <F extends Functor<{}, F>, A, B>(fa: F, f: (a: A) => B) => F
223+
>staticMap : <F extends Functor<_T, F<_T>>, A, B>(fa: F<A>, f: (a: A) => B) => F<B>
224224
>initialX : FunctorX<string>
225225
>val => val.length : (val: string) => number
226226
>val : string
@@ -236,7 +236,7 @@ const expectX3: FunctorX<number> = resultX3;
236236
const resultY3 = staticMap(initialY, val => val.length);
237237
>resultY3 : FunctorY<number>
238238
>staticMap(initialY, val => val.length) : FunctorY<number>
239-
>staticMap : <F extends Functor<{}, F>, A, B>(fa: F, f: (a: A) => B) => F
239+
>staticMap : <F extends Functor<_T, F<_T>>, A, B>(fa: F<A>, f: (a: A) => B) => F<B>
240240
>initialY : FunctorY<string>
241241
>val => val.length : (val: string) => number
242242
>val : string
@@ -252,7 +252,7 @@ const expectY3: FunctorY<number> = resultY3;
252252
const resultX4 = staticMap(initialX, val => [val]);
253253
>resultX4 : FunctorX<string[]>
254254
>staticMap(initialX, val => [val]) : FunctorX<string[]>
255-
>staticMap : <F extends Functor<{}, F>, A, B>(fa: F, f: (a: A) => B) => F
255+
>staticMap : <F extends Functor<_T, F<_T>>, A, B>(fa: F<A>, f: (a: A) => B) => F<B>
256256
>initialX : FunctorX<string>
257257
>val => [val] : (val: string) => string[]
258258
>val : string
@@ -267,7 +267,7 @@ const expectX4: FunctorX<string[]> = resultX4;
267267
const resultY4 = staticMap(initialY, val => [val]);
268268
>resultY4 : FunctorY<string[]>
269269
>staticMap(initialY, val => [val]) : FunctorY<string[]>
270-
>staticMap : <F extends Functor<{}, F>, A, B>(fa: F, f: (a: A) => B) => F
270+
>staticMap : <F extends Functor<_T, F<_T>>, A, B>(fa: F<A>, f: (a: A) => B) => F<B>
271271
>initialY : FunctorY<string>
272272
>val => [val] : (val: string) => string[]
273273
>val : string

tests/baselines/reference/higherKindedTypesExpectedErrors.errors.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
tests/cases/compiler/higherKindedTypesExpectedErrors.ts(6,11): error TS2430: Interface 'FunctorX<A>' incorrectly extends interface 'Functor<FunctorX<A>, A>'.
1+
tests/cases/compiler/higherKindedTypesExpectedErrors.ts(6,11): error TS2430: Interface 'FunctorX<A>' incorrectly extends interface 'Functor<FunctorX<_TX>, A>'.
22
Types of property 'map' are incompatible.
33
Type '<B>(f: (a: A) => B) => B[]' is not assignable to type '<BX>(fmapx: (fmapxax: A) => BX) => FunctorX<BX>'.
44
Type 'BX[]' is not assignable to type 'FunctorX<BX>'.
@@ -13,7 +13,7 @@ tests/cases/compiler/higherKindedTypesExpectedErrors.ts(6,11): error TS2430: Int
1313
// Expect error since array doesn't have xVal property
1414
interface FunctorX<A> extends Functor<FunctorX, A> {
1515
~~~~~~~~
16-
!!! error TS2430: Interface 'FunctorX<A>' incorrectly extends interface 'Functor<FunctorX<A>, A>'.
16+
!!! error TS2430: Interface 'FunctorX<A>' incorrectly extends interface 'Functor<FunctorX<_TX>, A>'.
1717
!!! error TS2430: Types of property 'map' are incompatible.
1818
!!! error TS2430: Type '<B>(f: (a: A) => B) => B[]' is not assignable to type '<BX>(fmapx: (fmapxax: A) => BX) => FunctorX<BX>'.
1919
!!! error TS2430: Type 'BX[]' is not assignable to type 'FunctorX<BX>'.

tests/baselines/reference/higherKindedTypesExpectedErrors.types

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
11
=== tests/cases/compiler/higherKindedTypesExpectedErrors.ts ===
22
interface Functor<FX<_TX>, AX> {
3-
>Functor : Functor<FX, AX>
4-
>FX : FX
3+
>Functor : Functor<FX<_TX>, AX>
4+
>FX : FX<_TX>
55
>_TX : _TX
66
>AX : AX
77

88
map<BX>(fmapx: (fmapxax: AX) => BX): FX<BX>;
9-
>map : <BX>(fmapx: (fmapxax: AX) => BX) => FX
9+
>map : <BX>(fmapx: (fmapxax: AX) => BX) => FX<BX>
1010
>BX : BX
1111
>fmapx : (fmapxax: AX) => BX
1212
>fmapxax : AX
1313
>AX : AX
1414
>BX : BX
15-
>FX : FX
15+
>FX : FX<_TX>
1616
>BX : BX
1717
}
1818

1919
// Expect error since array doesn't have xVal property
2020
interface FunctorX<A> extends Functor<FunctorX, A> {
2121
>FunctorX : FunctorX<A>
2222
>A : A
23-
>Functor : Functor<FX, AX>
23+
>Functor : Functor<FX<_TX>, AX>
2424
>FunctorX : FunctorX<A>
2525
>A : A
2626

0 commit comments

Comments
 (0)