Use separate marker types for variance annotation validation (#49616)

* Use separate marker types for variance annotation validation

* Add regression test
This commit is contained in:
Anders Hejlsberg 2022-06-21 06:42:10 -07:00 committed by GitHub
parent 4c2770d769
commit 529ba99e29
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 268 additions and 5 deletions

View File

@ -861,6 +861,10 @@ namespace ts {
markerSubType.constraint = markerSuperType;
const markerOtherType = createTypeParameter();
const markerSuperTypeForCheck = createTypeParameter();
const markerSubTypeForCheck = createTypeParameter();
markerSubTypeForCheck.constraint = markerSuperTypeForCheck;
const noTypePredicate = createTypePredicate(TypePredicateKind.Identifier, "<<unresolved>>", 0, anyType);
const anySignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, /*resolvedTypePredicate*/ undefined, 0, SignatureFlags.None);
@ -5047,8 +5051,8 @@ namespace ts {
if (type.symbol) {
return symbolToTypeNode(type.symbol, context, SymbolFlags.Type);
}
const name = (type === markerSuperType || type === markerSubType) && varianceTypeParameter && varianceTypeParameter.symbol ?
(type === markerSubType ? "sub-" : "super-") + symbolName(varianceTypeParameter.symbol) : "?";
const name = (type === markerSuperTypeForCheck || type === markerSubTypeForCheck) && varianceTypeParameter && varianceTypeParameter.symbol ?
(type === markerSubTypeForCheck ? "sub-" : "super-") + symbolName(varianceTypeParameter.symbol) : "?";
return factory.createTypeReferenceNode(factory.createIdentifier(name), /*typeArguments*/ undefined);
}
if (type.flags & TypeFlags.Union && (type as UnionType).origin) {
@ -18543,7 +18547,7 @@ namespace ts {
generalizedSourceType = getTypeNameForErrorDisplay(generalizedSource);
}
if (target.flags & TypeFlags.TypeParameter && target !== markerSuperType && target !== markerSubType) {
if (target.flags & TypeFlags.TypeParameter && target !== markerSuperTypeForCheck && target !== markerSubTypeForCheck) {
const constraint = getBaseConstraintOfType(target);
let needsOriginalSource;
if (constraint && (isTypeAssignableTo(generalizedSource, constraint) || (needsOriginalSource = isTypeAssignableTo(source, constraint)))) {
@ -35041,8 +35045,8 @@ namespace ts {
error(node, Diagnostics.Variance_annotations_are_only_supported_in_type_aliases_for_object_function_constructor_and_mapped_types);
}
else if (modifiers === ModifierFlags.In || modifiers === ModifierFlags.Out) {
const source = createMarkerType(symbol, typeParameter, modifiers === ModifierFlags.Out ? markerSubType : markerSuperType);
const target = createMarkerType(symbol, typeParameter, modifiers === ModifierFlags.Out ? markerSuperType : markerSubType);
const source = createMarkerType(symbol, typeParameter, modifiers === ModifierFlags.Out ? markerSubTypeForCheck : markerSuperTypeForCheck);
const target = createMarkerType(symbol, typeParameter, modifiers === ModifierFlags.Out ? markerSuperTypeForCheck : markerSubTypeForCheck);
const saveVarianceTypeParameter = typeParameter;
varianceTypeParameter = typeParameter;
checkTypeAssignableTo(source, target, node, Diagnostics.Type_0_is_not_assignable_to_type_1_as_implied_by_variance_annotation);

View File

@ -0,0 +1,48 @@
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.
Types of property 'run' are incompatible.
Type '(animal: sub-T) => void' is not assignable to type '(animal: super-T) => void'.
Types of parameters 'animal' and 'animal' are incompatible.
Type 'super-T' is not assignable to type 'sub-T'.
tests/cases/compiler/varianceAnnotationValidation.ts(27,1): error TS2322: Type 'AnimalContainer<Animal>' is not assignable to type 'AnimalContainer<Dog>'.
Property 'bark' is missing in type 'Animal' but required in type 'Dog'.
==== tests/cases/compiler/varianceAnnotationValidation.ts (2 errors) ====
// Repro from #49607
// Variance annotation error expected
interface Controller<out T> {
~~~~~
!!! error TS2636: Type 'Controller<sub-T>' is not assignable to type 'Controller<super-T>' as implied by variance annotation.
!!! error TS2636: Types of property 'run' are incompatible.
!!! error TS2636: Type '(animal: sub-T) => void' is not assignable to type '(animal: super-T) => void'.
!!! error TS2636: Types of parameters 'animal' and 'animal' are incompatible.
!!! error TS2636: Type 'super-T' is not assignable to type 'sub-T'.
createAnimal: () => T;
run: (animal: T) => void;
}
interface Animal {
run(): void;
};
class Dog implements Animal {
run() {};
bark() {};
}
interface AnimalContainer<T> {
controller: Controller<T>;
}
declare let ca: AnimalContainer<Animal>;
declare let cd: AnimalContainer<Dog>;
ca = cd; // Ok
cd = ca; // Error
~~
!!! error TS2322: Type 'AnimalContainer<Animal>' is not assignable to type 'AnimalContainer<Dog>'.
!!! error TS2322: Property 'bark' is missing in type 'Animal' but required in type 'Dog'.
!!! related TS2728 tests/cases/compiler/varianceAnnotationValidation.ts:16:2: 'bark' is declared here.

View File

@ -0,0 +1,64 @@
//// [varianceAnnotationValidation.ts]
// Repro from #49607
// Variance annotation error expected
interface Controller<out T> {
createAnimal: () => T;
run: (animal: T) => void;
}
interface Animal {
run(): void;
};
class Dog implements Animal {
run() {};
bark() {};
}
interface AnimalContainer<T> {
controller: Controller<T>;
}
declare let ca: AnimalContainer<Animal>;
declare let cd: AnimalContainer<Dog>;
ca = cd; // Ok
cd = ca; // Error
//// [varianceAnnotationValidation.js]
"use strict";
// Repro from #49607
;
var Dog = /** @class */ (function () {
function Dog() {
}
Dog.prototype.run = function () { };
;
Dog.prototype.bark = function () { };
;
return Dog;
}());
ca = cd; // Ok
cd = ca; // Error
//// [varianceAnnotationValidation.d.ts]
interface Controller<out T> {
createAnimal: () => T;
run: (animal: T) => void;
}
interface Animal {
run(): void;
}
declare class Dog implements Animal {
run(): void;
bark(): void;
}
interface AnimalContainer<T> {
controller: Controller<T>;
}
declare let ca: AnimalContainer<Animal>;
declare let cd: AnimalContainer<Dog>;

View File

@ -0,0 +1,66 @@
=== tests/cases/compiler/varianceAnnotationValidation.ts ===
// Repro from #49607
// Variance annotation error expected
interface Controller<out T> {
>Controller : Symbol(Controller, Decl(varianceAnnotationValidation.ts, 0, 0))
>T : Symbol(T, Decl(varianceAnnotationValidation.ts, 4, 21))
createAnimal: () => T;
>createAnimal : Symbol(Controller.createAnimal, Decl(varianceAnnotationValidation.ts, 4, 29))
>T : Symbol(T, Decl(varianceAnnotationValidation.ts, 4, 21))
run: (animal: T) => void;
>run : Symbol(Controller.run, Decl(varianceAnnotationValidation.ts, 5, 23))
>animal : Symbol(animal, Decl(varianceAnnotationValidation.ts, 6, 7))
>T : Symbol(T, Decl(varianceAnnotationValidation.ts, 4, 21))
}
interface Animal {
>Animal : Symbol(Animal, Decl(varianceAnnotationValidation.ts, 7, 1))
run(): void;
>run : Symbol(Animal.run, Decl(varianceAnnotationValidation.ts, 9, 18))
};
class Dog implements Animal {
>Dog : Symbol(Dog, Decl(varianceAnnotationValidation.ts, 11, 2))
>Animal : Symbol(Animal, Decl(varianceAnnotationValidation.ts, 7, 1))
run() {};
>run : Symbol(Dog.run, Decl(varianceAnnotationValidation.ts, 13, 29))
bark() {};
>bark : Symbol(Dog.bark, Decl(varianceAnnotationValidation.ts, 14, 10))
}
interface AnimalContainer<T> {
>AnimalContainer : Symbol(AnimalContainer, Decl(varianceAnnotationValidation.ts, 16, 1))
>T : Symbol(T, Decl(varianceAnnotationValidation.ts, 18, 26))
controller: Controller<T>;
>controller : Symbol(AnimalContainer.controller, Decl(varianceAnnotationValidation.ts, 18, 30))
>Controller : Symbol(Controller, Decl(varianceAnnotationValidation.ts, 0, 0))
>T : Symbol(T, Decl(varianceAnnotationValidation.ts, 18, 26))
}
declare let ca: AnimalContainer<Animal>;
>ca : Symbol(ca, Decl(varianceAnnotationValidation.ts, 22, 11))
>AnimalContainer : Symbol(AnimalContainer, Decl(varianceAnnotationValidation.ts, 16, 1))
>Animal : Symbol(Animal, Decl(varianceAnnotationValidation.ts, 7, 1))
declare let cd: AnimalContainer<Dog>;
>cd : Symbol(cd, Decl(varianceAnnotationValidation.ts, 23, 11))
>AnimalContainer : Symbol(AnimalContainer, Decl(varianceAnnotationValidation.ts, 16, 1))
>Dog : Symbol(Dog, Decl(varianceAnnotationValidation.ts, 11, 2))
ca = cd; // Ok
>ca : Symbol(ca, Decl(varianceAnnotationValidation.ts, 22, 11))
>cd : Symbol(cd, Decl(varianceAnnotationValidation.ts, 23, 11))
cd = ca; // Error
>cd : Symbol(cd, Decl(varianceAnnotationValidation.ts, 23, 11))
>ca : Symbol(ca, Decl(varianceAnnotationValidation.ts, 22, 11))

View File

@ -0,0 +1,51 @@
=== tests/cases/compiler/varianceAnnotationValidation.ts ===
// Repro from #49607
// Variance annotation error expected
interface Controller<out T> {
createAnimal: () => T;
>createAnimal : () => T
run: (animal: T) => void;
>run : (animal: T) => void
>animal : T
}
interface Animal {
run(): void;
>run : () => void
};
class Dog implements Animal {
>Dog : Dog
run() {};
>run : () => void
bark() {};
>bark : () => void
}
interface AnimalContainer<T> {
controller: Controller<T>;
>controller : Controller<T>
}
declare let ca: AnimalContainer<Animal>;
>ca : AnimalContainer<Animal>
declare let cd: AnimalContainer<Dog>;
>cd : AnimalContainer<Dog>
ca = cd; // Ok
>ca = cd : AnimalContainer<Dog>
>ca : AnimalContainer<Animal>
>cd : AnimalContainer<Dog>
cd = ca; // Error
>cd = ca : AnimalContainer<Animal>
>cd : AnimalContainer<Dog>
>ca : AnimalContainer<Animal>

View File

@ -0,0 +1,30 @@
// @strict: true
// @declaration: true
// Repro from #49607
// Variance annotation error expected
interface Controller<out T> {
createAnimal: () => T;
run: (animal: T) => void;
}
interface Animal {
run(): void;
};
class Dog implements Animal {
run() {};
bark() {};
}
interface AnimalContainer<T> {
controller: Controller<T>;
}
declare let ca: AnimalContainer<Animal>;
declare let cd: AnimalContainer<Dog>;
ca = cd; // Ok
cd = ca; // Error