mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-05 08:11:30 -06:00
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:
parent
5ae4b5d715
commit
94d6b4507e
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
19
tests/baselines/reference/circularBaseTypes.errors.txt
Normal file
19
tests/baselines/reference/circularBaseTypes.errors.txt
Normal 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;
|
||||
}
|
||||
|
||||
29
tests/baselines/reference/circularBaseTypes.js
Normal file
29
tests/baselines/reference/circularBaseTypes.js
Normal 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;
|
||||
28
tests/baselines/reference/circularBaseTypes.symbols
Normal file
28
tests/baselines/reference/circularBaseTypes.symbols
Normal 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))
|
||||
}
|
||||
|
||||
21
tests/baselines/reference/circularBaseTypes.types
Normal file
21
tests/baselines/reference/circularBaseTypes.types
Normal 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
|
||||
}
|
||||
|
||||
@ -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
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
~~~~
|
||||
|
||||
@ -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.
|
||||
|
||||
12
tests/cases/compiler/circularBaseTypes.ts
Normal file
12
tests/cases/compiler/circularBaseTypes.ts
Normal 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;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user