Skip to content

Commit cce61d1

Browse files
authored
Variance annotations on class expressions + deferred valiation (microsoft#48645)
* Variance annotations on class expressions + deferred validation * Add regression tests
1 parent c9a4d01 commit cce61d1

File tree

6 files changed

+141
-5
lines changed

6 files changed

+141
-5
lines changed

src/compiler/checker.ts

+12-5
Original file line numberDiff line numberDiff line change
@@ -34838,11 +34838,17 @@ namespace ts {
3483834838
if (constraintType && defaultType) {
3483934839
checkTypeAssignableTo(defaultType, getTypeWithThisArgument(instantiateType(constraintType, makeUnaryTypeMapper(typeParameter, defaultType)), defaultType), node.default, Diagnostics.Type_0_does_not_satisfy_the_constraint_1);
3484034840
}
34841-
if (node.parent.kind === SyntaxKind.InterfaceDeclaration || node.parent.kind === SyntaxKind.ClassDeclaration || node.parent.kind === SyntaxKind.TypeAliasDeclaration) {
34841+
checkNodeDeferred(node);
34842+
addLazyDiagnostic(() => checkTypeNameIsReserved(node.name, Diagnostics.Type_parameter_name_cannot_be_0));
34843+
}
34844+
34845+
function checkTypeParameterDeferred(node: TypeParameterDeclaration) {
34846+
if (isInterfaceDeclaration(node.parent) || isClassLike(node.parent) || isTypeAliasDeclaration(node.parent)) {
34847+
const typeParameter = getDeclaredTypeOfTypeParameter(getSymbolOfNode(node));
3484234848
const modifiers = getVarianceModifiers(typeParameter);
3484334849
if (modifiers) {
3484434850
const symbol = getSymbolOfNode(node.parent);
34845-
if (node.parent.kind === SyntaxKind.TypeAliasDeclaration && !(getObjectFlags(getDeclaredTypeOfSymbol(symbol)) & (ObjectFlags.Anonymous | ObjectFlags.Mapped))) {
34851+
if (isTypeAliasDeclaration(node.parent) && !(getObjectFlags(getDeclaredTypeOfSymbol(symbol)) & (ObjectFlags.Anonymous | ObjectFlags.Mapped))) {
3484634852
error(node, Diagnostics.Variance_annotations_are_only_supported_in_type_aliases_for_object_function_constructor_and_mapped_types);
3484734853
}
3484834854
else if (modifiers === ModifierFlags.In || modifiers === ModifierFlags.Out) {
@@ -34855,7 +34861,6 @@ namespace ts {
3485534861
}
3485634862
}
3485734863
}
34858-
addLazyDiagnostic(() => checkTypeNameIsReserved(node.name, Diagnostics.Type_parameter_name_cannot_be_0));
3485934864
}
3486034865

3486134866
function checkParameter(node: ParameterDeclaration) {
@@ -41354,6 +41359,9 @@ namespace ts {
4135441359
case SyntaxKind.ClassExpression:
4135541360
checkClassExpressionDeferred(node as ClassExpression);
4135641361
break;
41362+
case SyntaxKind.TypeParameter:
41363+
checkTypeParameterDeferred(node as TypeParameterDeclaration);
41364+
break;
4135741365
case SyntaxKind.JsxSelfClosingElement:
4135841366
checkJsxSelfClosingElementDeferred(node as JsxSelfClosingElement);
4135941367
break;
@@ -43551,8 +43559,7 @@ namespace ts {
4355143559
case SyntaxKind.OutKeyword:
4355243560
const inOutFlag = modifier.kind === SyntaxKind.InKeyword ? ModifierFlags.In : ModifierFlags.Out;
4355343561
const inOutText = modifier.kind === SyntaxKind.InKeyword ? "in" : "out";
43554-
if (node.kind !== SyntaxKind.TypeParameter || (node.parent.kind !== SyntaxKind.InterfaceDeclaration &&
43555-
node.parent.kind !== SyntaxKind.ClassDeclaration && node.parent.kind !== SyntaxKind.TypeAliasDeclaration)) {
43562+
if (node.kind !== SyntaxKind.TypeParameter || !(isInterfaceDeclaration(node.parent) || isClassLike(node.parent) || isTypeAliasDeclaration(node.parent))) {
4355643563
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_can_only_appear_on_a_type_parameter_of_a_class_interface_or_type_alias, inOutText);
4355743564
}
4355843565
if (flags & inOutFlag) {

tests/baselines/reference/varianceAnnotations.errors.txt

+14
Original file line numberDiff line numberDiff line change
@@ -324,4 +324,18 @@ tests/cases/conformance/types/typeParameters/typeParameterLists/varianceAnnotati
324324
!!! error TS2345: Types of property '_storedEvent' are incompatible.
325325
!!! error TS2345: Type '{ type: "PLAY"; value: number; } | { type: "RESET"; }' is not assignable to type '{ type: "PLAY"; value: number; }'.
326326
!!! error TS2345: Type '{ type: "RESET"; }' is not assignable to type '{ type: "PLAY"; value: number; }'.
327+
328+
// Repros from #48618
329+
330+
let Anon = class <out T> {
331+
foo(): InstanceType<(typeof Anon<T>)> {
332+
return this;
333+
}
334+
}
335+
336+
let OuterC = class C<out T> {
337+
foo(): C<T> {
338+
return this;
339+
}
340+
}
327341

tests/baselines/reference/varianceAnnotations.js

+41
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,20 @@ interpret(machine);
159159
declare const qq: ActionObject<{ type: "PLAY"; value: number }>;
160160

161161
createMachine<{ type: "PLAY"; value: number } | { type: "RESET" }>(qq); // Error
162+
163+
// Repros from #48618
164+
165+
let Anon = class <out T> {
166+
foo(): InstanceType<(typeof Anon<T>)> {
167+
return this;
168+
}
169+
}
170+
171+
let OuterC = class C<out T> {
172+
foo(): C<T> {
173+
return this;
174+
}
175+
}
162176

163177

164178
//// [varianceAnnotations.js]
@@ -186,6 +200,23 @@ var notString = pu; // Error
186200
var machine = createMachine({});
187201
interpret(machine);
188202
createMachine(qq); // Error
203+
// Repros from #48618
204+
var Anon = /** @class */ (function () {
205+
function class_1() {
206+
}
207+
class_1.prototype.foo = function () {
208+
return this;
209+
};
210+
return class_1;
211+
}());
212+
var OuterC = /** @class */ (function () {
213+
function C() {
214+
}
215+
C.prototype.foo = function () {
216+
return this;
217+
};
218+
return C;
219+
}());
189220

190221

191222
//// [varianceAnnotations.d.ts]
@@ -293,3 +324,13 @@ declare const qq: ActionObject<{
293324
type: "PLAY";
294325
value: number;
295326
}>;
327+
declare let Anon: {
328+
new <out T>(): {
329+
foo(): any;
330+
};
331+
};
332+
declare let OuterC: {
333+
new <out T>(): {
334+
foo(): any;
335+
};
336+
};

tests/baselines/reference/varianceAnnotations.symbols

+32
Original file line numberDiff line numberDiff line change
@@ -448,3 +448,35 @@ createMachine<{ type: "PLAY"; value: number } | { type: "RESET" }>(qq); // Erro
448448
>type : Symbol(type, Decl(varianceAnnotations.ts, 159, 49))
449449
>qq : Symbol(qq, Decl(varianceAnnotations.ts, 157, 13))
450450

451+
// Repros from #48618
452+
453+
let Anon = class <out T> {
454+
>Anon : Symbol(Anon, Decl(varianceAnnotations.ts, 163, 3))
455+
>T : Symbol(T, Decl(varianceAnnotations.ts, 163, 18))
456+
457+
foo(): InstanceType<(typeof Anon<T>)> {
458+
>foo : Symbol(Anon.foo, Decl(varianceAnnotations.ts, 163, 26))
459+
>InstanceType : Symbol(InstanceType, Decl(lib.es5.d.ts, --, --))
460+
>Anon : Symbol(Anon, Decl(varianceAnnotations.ts, 163, 3))
461+
>T : Symbol(T, Decl(varianceAnnotations.ts, 163, 18))
462+
463+
return this;
464+
>this : Symbol(Anon, Decl(varianceAnnotations.ts, 163, 10))
465+
}
466+
}
467+
468+
let OuterC = class C<out T> {
469+
>OuterC : Symbol(OuterC, Decl(varianceAnnotations.ts, 169, 3))
470+
>C : Symbol(C, Decl(varianceAnnotations.ts, 169, 12))
471+
>T : Symbol(T, Decl(varianceAnnotations.ts, 169, 21))
472+
473+
foo(): C<T> {
474+
>foo : Symbol(C.foo, Decl(varianceAnnotations.ts, 169, 29))
475+
>C : Symbol(C, Decl(varianceAnnotations.ts, 169, 12))
476+
>T : Symbol(T, Decl(varianceAnnotations.ts, 169, 21))
477+
478+
return this;
479+
>this : Symbol(C, Decl(varianceAnnotations.ts, 169, 12))
480+
}
481+
}
482+

tests/baselines/reference/varianceAnnotations.types

+28
Original file line numberDiff line numberDiff line change
@@ -346,3 +346,31 @@ createMachine<{ type: "PLAY"; value: number } | { type: "RESET" }>(qq); // Erro
346346
>type : "RESET"
347347
>qq : ActionObject<{ type: "PLAY"; value: number; }>
348348

349+
// Repros from #48618
350+
351+
let Anon = class <out T> {
352+
>Anon : typeof Anon
353+
>class <out T> { foo(): InstanceType<(typeof Anon<T>)> { return this; }} : typeof Anon
354+
355+
foo(): InstanceType<(typeof Anon<T>)> {
356+
>foo : () => InstanceType<(typeof Anon<T>)>
357+
>Anon : typeof Anon
358+
359+
return this;
360+
>this : this
361+
}
362+
}
363+
364+
let OuterC = class C<out T> {
365+
>OuterC : typeof C
366+
>class C<out T> { foo(): C<T> { return this; }} : typeof C
367+
>C : typeof C
368+
369+
foo(): C<T> {
370+
>foo : () => C<T>
371+
372+
return this;
373+
>this : this
374+
}
375+
}
376+

tests/cases/conformance/types/typeParameters/typeParameterLists/varianceAnnotations.ts

+14
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,17 @@ interpret(machine);
161161
declare const qq: ActionObject<{ type: "PLAY"; value: number }>;
162162

163163
createMachine<{ type: "PLAY"; value: number } | { type: "RESET" }>(qq); // Error
164+
165+
// Repros from #48618
166+
167+
let Anon = class <out T> {
168+
foo(): InstanceType<(typeof Anon<T>)> {
169+
return this;
170+
}
171+
}
172+
173+
let OuterC = class C<out T> {
174+
foo(): C<T> {
175+
return this;
176+
}
177+
}

0 commit comments

Comments
 (0)