Skip to content

Commit 529ba99

Browse files
authored
Use separate marker types for variance annotation validation (#49616)
* Use separate marker types for variance annotation validation * Add regression test
1 parent 4c2770d commit 529ba99

6 files changed

Lines changed: 268 additions & 5 deletions

File tree

src/compiler/checker.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,10 @@ namespace ts {
861861
markerSubType.constraint = markerSuperType;
862862
const markerOtherType = createTypeParameter();
863863

864+
const markerSuperTypeForCheck = createTypeParameter();
865+
const markerSubTypeForCheck = createTypeParameter();
866+
markerSubTypeForCheck.constraint = markerSuperTypeForCheck;
867+
864868
const noTypePredicate = createTypePredicate(TypePredicateKind.Identifier, "<<unresolved>>", 0, anyType);
865869

866870
const anySignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, /*resolvedTypePredicate*/ undefined, 0, SignatureFlags.None);
@@ -5047,8 +5051,8 @@ namespace ts {
50475051
if (type.symbol) {
50485052
return symbolToTypeNode(type.symbol, context, SymbolFlags.Type);
50495053
}
5050-
const name = (type === markerSuperType || type === markerSubType) && varianceTypeParameter && varianceTypeParameter.symbol ?
5051-
(type === markerSubType ? "sub-" : "super-") + symbolName(varianceTypeParameter.symbol) : "?";
5054+
const name = (type === markerSuperTypeForCheck || type === markerSubTypeForCheck) && varianceTypeParameter && varianceTypeParameter.symbol ?
5055+
(type === markerSubTypeForCheck ? "sub-" : "super-") + symbolName(varianceTypeParameter.symbol) : "?";
50525056
return factory.createTypeReferenceNode(factory.createIdentifier(name), /*typeArguments*/ undefined);
50535057
}
50545058
if (type.flags & TypeFlags.Union && (type as UnionType).origin) {
@@ -18543,7 +18547,7 @@ namespace ts {
1854318547
generalizedSourceType = getTypeNameForErrorDisplay(generalizedSource);
1854418548
}
1854518549

18546-
if (target.flags & TypeFlags.TypeParameter && target !== markerSuperType && target !== markerSubType) {
18550+
if (target.flags & TypeFlags.TypeParameter && target !== markerSuperTypeForCheck && target !== markerSubTypeForCheck) {
1854718551
const constraint = getBaseConstraintOfType(target);
1854818552
let needsOriginalSource;
1854918553
if (constraint && (isTypeAssignableTo(generalizedSource, constraint) || (needsOriginalSource = isTypeAssignableTo(source, constraint)))) {
@@ -35041,8 +35045,8 @@ namespace ts {
3504135045
error(node, Diagnostics.Variance_annotations_are_only_supported_in_type_aliases_for_object_function_constructor_and_mapped_types);
3504235046
}
3504335047
else if (modifiers === ModifierFlags.In || modifiers === ModifierFlags.Out) {
35044-
const source = createMarkerType(symbol, typeParameter, modifiers === ModifierFlags.Out ? markerSubType : markerSuperType);
35045-
const target = createMarkerType(symbol, typeParameter, modifiers === ModifierFlags.Out ? markerSuperType : markerSubType);
35048+
const source = createMarkerType(symbol, typeParameter, modifiers === ModifierFlags.Out ? markerSubTypeForCheck : markerSuperTypeForCheck);
35049+
const target = createMarkerType(symbol, typeParameter, modifiers === ModifierFlags.Out ? markerSuperTypeForCheck : markerSubTypeForCheck);
3504635050
const saveVarianceTypeParameter = typeParameter;
3504735051
varianceTypeParameter = typeParameter;
3504835052
checkTypeAssignableTo(source, target, node, Diagnostics.Type_0_is_not_assignable_to_type_1_as_implied_by_variance_annotation);
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
tests/cases/compiler/varianceAnnotationValidation.ts(5,22): error TS2636: Type 'Controller<sub-T>' is not assignable to type 'Controller<super-T>' as implied by variance annotation.
2+
Types of property 'run' are incompatible.
3+
Type '(animal: sub-T) => void' is not assignable to type '(animal: super-T) => void'.
4+
Types of parameters 'animal' and 'animal' are incompatible.
5+
Type 'super-T' is not assignable to type 'sub-T'.
6+
tests/cases/compiler/varianceAnnotationValidation.ts(27,1): error TS2322: Type 'AnimalContainer<Animal>' is not assignable to type 'AnimalContainer<Dog>'.
7+
Property 'bark' is missing in type 'Animal' but required in type 'Dog'.
8+
9+
10+
==== tests/cases/compiler/varianceAnnotationValidation.ts (2 errors) ====
11+
// Repro from #49607
12+
13+
// Variance annotation error expected
14+
15+
interface Controller<out T> {
16+
~~~~~
17+
!!! error TS2636: Type 'Controller<sub-T>' is not assignable to type 'Controller<super-T>' as implied by variance annotation.
18+
!!! error TS2636: Types of property 'run' are incompatible.
19+
!!! error TS2636: Type '(animal: sub-T) => void' is not assignable to type '(animal: super-T) => void'.
20+
!!! error TS2636: Types of parameters 'animal' and 'animal' are incompatible.
21+
!!! error TS2636: Type 'super-T' is not assignable to type 'sub-T'.
22+
createAnimal: () => T;
23+
run: (animal: T) => void;
24+
}
25+
26+
interface Animal {
27+
run(): void;
28+
};
29+
30+
class Dog implements Animal {
31+
run() {};
32+
bark() {};
33+
}
34+
35+
interface AnimalContainer<T> {
36+
controller: Controller<T>;
37+
}
38+
39+
declare let ca: AnimalContainer<Animal>;
40+
declare let cd: AnimalContainer<Dog>;
41+
42+
ca = cd; // Ok
43+
cd = ca; // Error
44+
~~
45+
!!! error TS2322: Type 'AnimalContainer<Animal>' is not assignable to type 'AnimalContainer<Dog>'.
46+
!!! error TS2322: Property 'bark' is missing in type 'Animal' but required in type 'Dog'.
47+
!!! related TS2728 tests/cases/compiler/varianceAnnotationValidation.ts:16:2: 'bark' is declared here.
48+
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//// [varianceAnnotationValidation.ts]
2+
// Repro from #49607
3+
4+
// Variance annotation error expected
5+
6+
interface Controller<out T> {
7+
createAnimal: () => T;
8+
run: (animal: T) => void;
9+
}
10+
11+
interface Animal {
12+
run(): void;
13+
};
14+
15+
class Dog implements Animal {
16+
run() {};
17+
bark() {};
18+
}
19+
20+
interface AnimalContainer<T> {
21+
controller: Controller<T>;
22+
}
23+
24+
declare let ca: AnimalContainer<Animal>;
25+
declare let cd: AnimalContainer<Dog>;
26+
27+
ca = cd; // Ok
28+
cd = ca; // Error
29+
30+
31+
//// [varianceAnnotationValidation.js]
32+
"use strict";
33+
// Repro from #49607
34+
;
35+
var Dog = /** @class */ (function () {
36+
function Dog() {
37+
}
38+
Dog.prototype.run = function () { };
39+
;
40+
Dog.prototype.bark = function () { };
41+
;
42+
return Dog;
43+
}());
44+
ca = cd; // Ok
45+
cd = ca; // Error
46+
47+
48+
//// [varianceAnnotationValidation.d.ts]
49+
interface Controller<out T> {
50+
createAnimal: () => T;
51+
run: (animal: T) => void;
52+
}
53+
interface Animal {
54+
run(): void;
55+
}
56+
declare class Dog implements Animal {
57+
run(): void;
58+
bark(): void;
59+
}
60+
interface AnimalContainer<T> {
61+
controller: Controller<T>;
62+
}
63+
declare let ca: AnimalContainer<Animal>;
64+
declare let cd: AnimalContainer<Dog>;
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
=== tests/cases/compiler/varianceAnnotationValidation.ts ===
2+
// Repro from #49607
3+
4+
// Variance annotation error expected
5+
6+
interface Controller<out T> {
7+
>Controller : Symbol(Controller, Decl(varianceAnnotationValidation.ts, 0, 0))
8+
>T : Symbol(T, Decl(varianceAnnotationValidation.ts, 4, 21))
9+
10+
createAnimal: () => T;
11+
>createAnimal : Symbol(Controller.createAnimal, Decl(varianceAnnotationValidation.ts, 4, 29))
12+
>T : Symbol(T, Decl(varianceAnnotationValidation.ts, 4, 21))
13+
14+
run: (animal: T) => void;
15+
>run : Symbol(Controller.run, Decl(varianceAnnotationValidation.ts, 5, 23))
16+
>animal : Symbol(animal, Decl(varianceAnnotationValidation.ts, 6, 7))
17+
>T : Symbol(T, Decl(varianceAnnotationValidation.ts, 4, 21))
18+
}
19+
20+
interface Animal {
21+
>Animal : Symbol(Animal, Decl(varianceAnnotationValidation.ts, 7, 1))
22+
23+
run(): void;
24+
>run : Symbol(Animal.run, Decl(varianceAnnotationValidation.ts, 9, 18))
25+
26+
};
27+
28+
class Dog implements Animal {
29+
>Dog : Symbol(Dog, Decl(varianceAnnotationValidation.ts, 11, 2))
30+
>Animal : Symbol(Animal, Decl(varianceAnnotationValidation.ts, 7, 1))
31+
32+
run() {};
33+
>run : Symbol(Dog.run, Decl(varianceAnnotationValidation.ts, 13, 29))
34+
35+
bark() {};
36+
>bark : Symbol(Dog.bark, Decl(varianceAnnotationValidation.ts, 14, 10))
37+
}
38+
39+
interface AnimalContainer<T> {
40+
>AnimalContainer : Symbol(AnimalContainer, Decl(varianceAnnotationValidation.ts, 16, 1))
41+
>T : Symbol(T, Decl(varianceAnnotationValidation.ts, 18, 26))
42+
43+
controller: Controller<T>;
44+
>controller : Symbol(AnimalContainer.controller, Decl(varianceAnnotationValidation.ts, 18, 30))
45+
>Controller : Symbol(Controller, Decl(varianceAnnotationValidation.ts, 0, 0))
46+
>T : Symbol(T, Decl(varianceAnnotationValidation.ts, 18, 26))
47+
}
48+
49+
declare let ca: AnimalContainer<Animal>;
50+
>ca : Symbol(ca, Decl(varianceAnnotationValidation.ts, 22, 11))
51+
>AnimalContainer : Symbol(AnimalContainer, Decl(varianceAnnotationValidation.ts, 16, 1))
52+
>Animal : Symbol(Animal, Decl(varianceAnnotationValidation.ts, 7, 1))
53+
54+
declare let cd: AnimalContainer<Dog>;
55+
>cd : Symbol(cd, Decl(varianceAnnotationValidation.ts, 23, 11))
56+
>AnimalContainer : Symbol(AnimalContainer, Decl(varianceAnnotationValidation.ts, 16, 1))
57+
>Dog : Symbol(Dog, Decl(varianceAnnotationValidation.ts, 11, 2))
58+
59+
ca = cd; // Ok
60+
>ca : Symbol(ca, Decl(varianceAnnotationValidation.ts, 22, 11))
61+
>cd : Symbol(cd, Decl(varianceAnnotationValidation.ts, 23, 11))
62+
63+
cd = ca; // Error
64+
>cd : Symbol(cd, Decl(varianceAnnotationValidation.ts, 23, 11))
65+
>ca : Symbol(ca, Decl(varianceAnnotationValidation.ts, 22, 11))
66+
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
=== tests/cases/compiler/varianceAnnotationValidation.ts ===
2+
// Repro from #49607
3+
4+
// Variance annotation error expected
5+
6+
interface Controller<out T> {
7+
createAnimal: () => T;
8+
>createAnimal : () => T
9+
10+
run: (animal: T) => void;
11+
>run : (animal: T) => void
12+
>animal : T
13+
}
14+
15+
interface Animal {
16+
run(): void;
17+
>run : () => void
18+
19+
};
20+
21+
class Dog implements Animal {
22+
>Dog : Dog
23+
24+
run() {};
25+
>run : () => void
26+
27+
bark() {};
28+
>bark : () => void
29+
}
30+
31+
interface AnimalContainer<T> {
32+
controller: Controller<T>;
33+
>controller : Controller<T>
34+
}
35+
36+
declare let ca: AnimalContainer<Animal>;
37+
>ca : AnimalContainer<Animal>
38+
39+
declare let cd: AnimalContainer<Dog>;
40+
>cd : AnimalContainer<Dog>
41+
42+
ca = cd; // Ok
43+
>ca = cd : AnimalContainer<Dog>
44+
>ca : AnimalContainer<Animal>
45+
>cd : AnimalContainer<Dog>
46+
47+
cd = ca; // Error
48+
>cd = ca : AnimalContainer<Animal>
49+
>cd : AnimalContainer<Dog>
50+
>ca : AnimalContainer<Animal>
51+
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// @strict: true
2+
// @declaration: true
3+
4+
// Repro from #49607
5+
6+
// Variance annotation error expected
7+
8+
interface Controller<out T> {
9+
createAnimal: () => T;
10+
run: (animal: T) => void;
11+
}
12+
13+
interface Animal {
14+
run(): void;
15+
};
16+
17+
class Dog implements Animal {
18+
run() {};
19+
bark() {};
20+
}
21+
22+
interface AnimalContainer<T> {
23+
controller: Controller<T>;
24+
}
25+
26+
declare let ca: AnimalContainer<Animal>;
27+
declare let cd: AnimalContainer<Dog>;
28+
29+
ca = cd; // Ok
30+
cd = ca; // Error

0 commit comments

Comments
 (0)