Consistent errors on circular base types (#39675)

* Properly track and report errors on circular base types

* Accept new baselines

* Add regression test
This commit is contained in:
Anders Hejlsberg 2020-07-20 20:35:47 -07:00 committed by GitHub
parent 5ae4b5d715
commit 94d6b4507e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 189 additions and 17 deletions

View File

@ -166,6 +166,7 @@ namespace ts {
ImmediateBaseConstraint,
EnumTagType,
ResolvedTypeArguments,
ResolvedBaseTypes,
}
const enum CheckMode {
@ -7491,6 +7492,8 @@ namespace ts {
return !!(<Type>target).immediateBaseConstraint;
case TypeSystemPropertyName.ResolvedTypeArguments:
return !!(target as TypeReference).resolvedTypeArguments;
case TypeSystemPropertyName.ResolvedBaseTypes:
return !!(target as InterfaceType).baseTypesResolved;
}
return Debug.assertNever(propertyName);
}
@ -8917,22 +8920,36 @@ namespace ts {
return resolvedImplementsTypes;
}
function reportCircularBaseType(node: Node, type: Type) {
error(node, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType));
}
function getBaseTypes(type: InterfaceType): BaseType[] {
if (!type.resolvedBaseTypes) {
if (type.objectFlags & ObjectFlags.Tuple) {
type.resolvedBaseTypes = [getTupleBaseType(<TupleType>type)];
}
else if (type.symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
if (type.symbol.flags & SymbolFlags.Class) {
resolveBaseTypesOfClass(type);
if (!type.baseTypesResolved) {
if (pushTypeResolution(type, TypeSystemPropertyName.ResolvedBaseTypes)) {
if (type.objectFlags & ObjectFlags.Tuple) {
type.resolvedBaseTypes = [getTupleBaseType(<TupleType>type)];
}
if (type.symbol.flags & SymbolFlags.Interface) {
resolveBaseTypesOfInterface(type);
else if (type.symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
if (type.symbol.flags & SymbolFlags.Class) {
resolveBaseTypesOfClass(type);
}
if (type.symbol.flags & SymbolFlags.Interface) {
resolveBaseTypesOfInterface(type);
}
}
else {
Debug.fail("type must be class or interface");
}
if (!popTypeResolution()) {
for (const declaration of type.symbol.declarations) {
if (declaration.kind === SyntaxKind.ClassDeclaration || declaration.kind === SyntaxKind.InterfaceDeclaration) {
reportCircularBaseType(declaration, type);
}
}
}
}
else {
Debug.fail("type must be class or interface");
}
type.baseTypesResolved = true;
}
return type.resolvedBaseTypes;
}
@ -9041,7 +9058,7 @@ namespace ts {
}
}
else {
error(declaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType));
reportCircularBaseType(declaration, type);
}
}
else {

View File

@ -5050,6 +5050,8 @@ namespace ts {
resolvedBaseConstructorType?: Type; // Resolved base constructor type of class
/* @internal */
resolvedBaseTypes: BaseType[]; // Resolved base types
/* @internal */
baseTypesResolved?: boolean;
}
// Object type or intersection of object types

View File

@ -0,0 +1,19 @@
tests/cases/compiler/circularBaseTypes.ts(4,11): error TS2310: Type 'M2' recursively references itself as a base type.
tests/cases/compiler/circularBaseTypes.ts(5,6): error TS2456: Type alias 'M3' circularly references itself.
==== tests/cases/compiler/circularBaseTypes.ts (2 errors) ====
// Repro from #38098
type M<T> = { value: T };
interface M2 extends M<M3> {}; // Error
~~
!!! error TS2310: Type 'M2' recursively references itself as a base type.
type M3 = M2[keyof M2]; // Error
~~
!!! error TS2456: Type alias 'M3' circularly references itself.
function f(m: M3) {
return m.value;
}

View File

@ -0,0 +1,29 @@
//// [circularBaseTypes.ts]
// Repro from #38098
type M<T> = { value: T };
interface M2 extends M<M3> {}; // Error
type M3 = M2[keyof M2]; // Error
function f(m: M3) {
return m.value;
}
//// [circularBaseTypes.js]
"use strict";
// Repro from #38098
; // Error
function f(m) {
return m.value;
}
//// [circularBaseTypes.d.ts]
declare type M<T> = {
value: T;
};
interface M2 extends M<M3> {
}
declare type M3 = M2[keyof M2];
declare function f(m: M3): any;

View File

@ -0,0 +1,28 @@
=== tests/cases/compiler/circularBaseTypes.ts ===
// Repro from #38098
type M<T> = { value: T };
>M : Symbol(M, Decl(circularBaseTypes.ts, 0, 0))
>T : Symbol(T, Decl(circularBaseTypes.ts, 2, 7))
>value : Symbol(value, Decl(circularBaseTypes.ts, 2, 13))
>T : Symbol(T, Decl(circularBaseTypes.ts, 2, 7))
interface M2 extends M<M3> {}; // Error
>M2 : Symbol(M2, Decl(circularBaseTypes.ts, 2, 25))
>M : Symbol(M, Decl(circularBaseTypes.ts, 0, 0))
>M3 : Symbol(M3, Decl(circularBaseTypes.ts, 3, 30))
type M3 = M2[keyof M2]; // Error
>M3 : Symbol(M3, Decl(circularBaseTypes.ts, 3, 30))
>M2 : Symbol(M2, Decl(circularBaseTypes.ts, 2, 25))
>M2 : Symbol(M2, Decl(circularBaseTypes.ts, 2, 25))
function f(m: M3) {
>f : Symbol(f, Decl(circularBaseTypes.ts, 4, 23))
>m : Symbol(m, Decl(circularBaseTypes.ts, 6, 11))
>M3 : Symbol(M3, Decl(circularBaseTypes.ts, 3, 30))
return m.value;
>m : Symbol(m, Decl(circularBaseTypes.ts, 6, 11))
}

View File

@ -0,0 +1,21 @@
=== tests/cases/compiler/circularBaseTypes.ts ===
// Repro from #38098
type M<T> = { value: T };
>M : M<T>
>value : T
interface M2 extends M<M3> {}; // Error
type M3 = M2[keyof M2]; // Error
>M3 : any
function f(m: M3) {
>f : (m: any) => any
>m : any
return m.value;
>m.value : any
>m : any
>value : any
}

View File

@ -0,0 +1,23 @@
tests/cases/compiler/circularConstraintYieldsAppropriateError.ts(10,7): error TS2310: Type 'Foo' recursively references itself as a base type.
==== tests/cases/compiler/circularConstraintYieldsAppropriateError.ts (1 errors) ====
// https://github.com/Microsoft/TypeScript/issues/16861
class BaseType<T> {
bar: T
}
class NextType<C extends { someProp: any }, T = C['someProp']> extends BaseType<T> {
baz: string;
}
class Foo extends NextType<Foo> {
~~~
!!! error TS2310: Type 'Foo' recursively references itself as a base type.
someProp: {
test: true
}
}
const foo = new Foo();
foo.bar.test

View File

@ -7,11 +7,12 @@ tests/cases/compiler/interfaceDeclaration1.ts(22,11): error TS2310: Type 'I5' re
tests/cases/compiler/interfaceDeclaration1.ts(35,7): error TS2420: Class 'C1' incorrectly implements interface 'I3'.
Property 'prototype' is missing in type 'C1' but required in type 'I3'.
tests/cases/compiler/interfaceDeclaration1.ts(41,11): error TS2310: Type 'i8' recursively references itself as a base type.
tests/cases/compiler/interfaceDeclaration1.ts(42,11): error TS2310: Type 'i9' recursively references itself as a base type.
tests/cases/compiler/interfaceDeclaration1.ts(52,11): error TS2320: Interface 'i12' cannot simultaneously extend types 'i10' and 'i11'.
Named property 'foo' of types 'i10' and 'i11' are not identical.
==== tests/cases/compiler/interfaceDeclaration1.ts (9 errors) ====
==== tests/cases/compiler/interfaceDeclaration1.ts (10 errors) ====
interface I1 {
item:number;
~~~~
@ -73,6 +74,8 @@ tests/cases/compiler/interfaceDeclaration1.ts(52,11): error TS2320: Interface 'i
~~
!!! error TS2310: Type 'i8' recursively references itself as a base type.
interface i9 extends i8 { }
~~
!!! error TS2310: Type 'i9' recursively references itself as a base type.
interface i10 {
foo():number;

View File

@ -1,8 +1,12 @@
tests/cases/conformance/interfaces/interfaceDeclarations/interfaceThatIndirectlyInheritsFromItself.ts(1,11): error TS2310: Type 'Base' recursively references itself as a base type.
tests/cases/conformance/interfaces/interfaceDeclarations/interfaceThatIndirectlyInheritsFromItself.ts(5,11): error TS2310: Type 'Derived' recursively references itself as a base type.
tests/cases/conformance/interfaces/interfaceDeclarations/interfaceThatIndirectlyInheritsFromItself.ts(9,11): error TS2310: Type 'Derived2' recursively references itself as a base type.
tests/cases/conformance/interfaces/interfaceDeclarations/interfaceThatIndirectlyInheritsFromItself.ts(14,15): error TS2310: Type 'Base<T>' recursively references itself as a base type.
tests/cases/conformance/interfaces/interfaceDeclarations/interfaceThatIndirectlyInheritsFromItself.ts(18,15): error TS2310: Type 'Derived<T>' recursively references itself as a base type.
tests/cases/conformance/interfaces/interfaceDeclarations/interfaceThatIndirectlyInheritsFromItself.ts(22,15): error TS2310: Type 'Derived2<T>' recursively references itself as a base type.
==== tests/cases/conformance/interfaces/interfaceDeclarations/interfaceThatIndirectlyInheritsFromItself.ts (2 errors) ====
==== tests/cases/conformance/interfaces/interfaceDeclarations/interfaceThatIndirectlyInheritsFromItself.ts (6 errors) ====
interface Base extends Derived2 { // error
~~~~
!!! error TS2310: Type 'Base' recursively references itself as a base type.
@ -10,10 +14,14 @@ tests/cases/conformance/interfaces/interfaceDeclarations/interfaceThatIndirectly
}
interface Derived extends Base {
~~~~~~~
!!! error TS2310: Type 'Derived' recursively references itself as a base type.
y: string;
}
interface Derived2 extends Derived {
~~~~~~~~
!!! error TS2310: Type 'Derived2' recursively references itself as a base type.
z: string;
}
@ -25,10 +33,14 @@ tests/cases/conformance/interfaces/interfaceDeclarations/interfaceThatIndirectly
}
interface Derived<T> extends Base<T> {
~~~~~~~
!!! error TS2310: Type 'Derived<T>' recursively references itself as a base type.
y: string;
}
interface Derived2<T> extends Derived<T> {
~~~~~~~~
!!! error TS2310: Type 'Derived2<T>' recursively references itself as a base type.
z: string;
}
}

View File

@ -1,12 +1,15 @@
tests/cases/compiler/recursiveBaseCheck5.ts(1,11): error TS2310: Type 'I1<T>' recursively references itself as a base type.
tests/cases/compiler/recursiveBaseCheck5.ts(2,11): error TS2310: Type 'I2<T>' recursively references itself as a base type.
tests/cases/compiler/recursiveBaseCheck5.ts(4,9): error TS2339: Property 'blah' does not exist on type 'X<unknown, unknown>'.
==== tests/cases/compiler/recursiveBaseCheck5.ts (2 errors) ====
==== tests/cases/compiler/recursiveBaseCheck5.ts (3 errors) ====
interface I1<T> extends I2<string> { }
~~
!!! error TS2310: Type 'I1<T>' recursively references itself as a base type.
interface I2<T> extends I1<T> { }
~~
!!! error TS2310: Type 'I2<T>' recursively references itself as a base type.
class X<T, U> implements I2<T> { }
(new X).blah;
~~~~

View File

@ -1,8 +1,9 @@
tests/cases/compiler/recursiveInheritance.ts(1,11): error TS2310: Type 'I5' recursively references itself as a base type.
tests/cases/compiler/recursiveInheritance.ts(5,11): error TS2310: Type 'i8' recursively references itself as a base type.
tests/cases/compiler/recursiveInheritance.ts(6,11): error TS2310: Type 'i9' recursively references itself as a base type.
==== tests/cases/compiler/recursiveInheritance.ts (2 errors) ====
==== tests/cases/compiler/recursiveInheritance.ts (3 errors) ====
interface I5 extends I5 { // error
~~
!!! error TS2310: Type 'I5' recursively references itself as a base type.
@ -13,4 +14,6 @@ tests/cases/compiler/recursiveInheritance.ts(5,11): error TS2310: Type 'i8' recu
~~
!!! error TS2310: Type 'i8' recursively references itself as a base type.
interface i9 extends i8 { } // error
~~
!!! error TS2310: Type 'i9' recursively references itself as a base type.

View File

@ -0,0 +1,12 @@
// @strict: true
// @declaration: true
// Repro from #38098
type M<T> = { value: T };
interface M2 extends M<M3> {}; // Error
type M3 = M2[keyof M2]; // Error
function f(m: M3) {
return m.value;
}