diff --git a/linter-4.2/src/Utils.ts b/linter-4.2/src/Utils.ts index 93d8ab0edc83b76c5fb726cddab73711a389478a..084e2f3f89dc9781f3dc7f2321edd44b9a026c4b 100644 --- a/linter-4.2/src/Utils.ts +++ b/linter-4.2/src/Utils.ts @@ -580,17 +580,19 @@ export class TsUtils { !typeADecl.heritageClauses ) continue; for (let heritageClause of typeADecl.heritageClauses) { - if (this.processParentTypes(heritageClause.types, typeB)) return true; + let processInterfaces = typeA.isClass() ? (heritageClause.token != ts.SyntaxKind.ExtendsKeyword) : true; + if (this.processParentTypes(heritageClause.types, typeB, processInterfaces)) return true; } } return false; } - private processParentTypes(parentTypes: ts.NodeArray, typeB: ts.Type): boolean { + private processParentTypes(parentTypes: ts.NodeArray, typeB: ts.Type, processInterfaces: boolean): boolean { for (let baseTypeExpr of parentTypes) { let baseType = this.tsTypeChecker.getTypeAtLocation(baseTypeExpr); - if (baseType && this.relatedByInheritanceOrIdentical(baseType, typeB)) return true; + if (this.isTypeReference(baseType) && baseType.target !== baseType) baseType = baseType.target; + if (baseType && (baseType.isClass() != processInterfaces) && this.relatedByInheritanceOrIdentical(baseType, typeB)) return true; } return false; } diff --git a/linter-4.2/test/type_declarations.ts b/linter-4.2/test/type_declarations.ts index 376f7f0592c414b2617b0d59dea55a2d5808519f..3082b3f695da6cadbe997d4ff50909ba1b7d67df 100644 --- a/linter-4.2/test/type_declarations.ts +++ b/linter-4.2/test/type_declarations.ts @@ -94,4 +94,17 @@ function classExpressionTest() { interface Box { contents: any; -} \ No newline at end of file +} + +class BadA implements BadD { } +class BadB extends BadA { } +interface BadD extends BadB { } + +class BadE implements BadE { } + +class Empty { }; + +function heritageRecursionTest() { + let e1: Empty = new BadA(); + let e2: Empty = new BadE(); +} diff --git a/linter-4.2/test/type_declarations.ts.relax.json b/linter-4.2/test/type_declarations.ts.relax.json index 1afccae65d0a0977ff8a0d74956244e862911db4..3d5416802480ef30d565ddc3477be63d3e255b6b 100644 --- a/linter-4.2/test/type_declarations.ts.relax.json +++ b/linter-4.2/test/type_declarations.ts.relax.json @@ -74,6 +74,26 @@ "column": 13, "problem": "AnyType" }, + { + "line": 101, + "column": 1, + "problem": "InterfaceExtendsClass" + }, + { + "line": 103, + "column": 23, + "problem": "ImplementsClass" + }, + { + "line": 108, + "column": 7, + "problem": "StructuralIdentity" + }, + { + "line": 109, + "column": 7, + "problem": "StructuralIdentity" + }, { "line": 42, "column": 11, diff --git a/linter-4.2/test/type_declarations.ts.strict.json b/linter-4.2/test/type_declarations.ts.strict.json index e34d68a7e3be144f26fbdad23ea26c7249e60a22..6bb3bf580079dad1cefae5e991ed4c24265e14da 100644 --- a/linter-4.2/test/type_declarations.ts.strict.json +++ b/linter-4.2/test/type_declarations.ts.strict.json @@ -84,6 +84,26 @@ "column": 13, "problem": "AnyType" }, + { + "line": 101, + "column": 1, + "problem": "InterfaceExtendsClass" + }, + { + "line": 103, + "column": 23, + "problem": "ImplementsClass" + }, + { + "line": 108, + "column": 7, + "problem": "StructuralIdentity" + }, + { + "line": 109, + "column": 7, + "problem": "StructuralIdentity" + }, { "line": 42, "column": 11, diff --git a/linter/src/utils/TsUtils.ts b/linter/src/utils/TsUtils.ts index 614816610840854fd694e8f75664837a0e260cae..af21be03a81445acc8f283eb82c3d46e85c87218 100644 --- a/linter/src/utils/TsUtils.ts +++ b/linter/src/utils/TsUtils.ts @@ -441,17 +441,19 @@ export class TsUtils { !typeADecl.heritageClauses ) continue; for (let heritageClause of typeADecl.heritageClauses) { - if (this.processParentTypes(heritageClause.types, typeB)) return true; + let processInterfaces = typeA.isClass() ? (heritageClause.token != ts.SyntaxKind.ExtendsKeyword) : true; + if (this.processParentTypes(heritageClause.types, typeB, processInterfaces)) return true; } } return false; } - private processParentTypes(parentTypes: ts.NodeArray, typeB: ts.Type): boolean { + private processParentTypes(parentTypes: ts.NodeArray, typeB: ts.Type, processInterfaces: boolean): boolean { for (let baseTypeExpr of parentTypes) { let baseType = this.tsTypeChecker.getTypeAtLocation(baseTypeExpr); - if (baseType && this.relatedByInheritanceOrIdentical(baseType, typeB)) return true; + if (this.isTypeReference(baseType) && baseType.target !== baseType) baseType = baseType.target; + if (baseType && (baseType.isClass() != processInterfaces) && this.relatedByInheritanceOrIdentical(baseType, typeB)) return true; } return false; } diff --git a/linter/test/type_declarations.ts b/linter/test/type_declarations.ts index 376f7f0592c414b2617b0d59dea55a2d5808519f..3082b3f695da6cadbe997d4ff50909ba1b7d67df 100644 --- a/linter/test/type_declarations.ts +++ b/linter/test/type_declarations.ts @@ -94,4 +94,17 @@ function classExpressionTest() { interface Box { contents: any; -} \ No newline at end of file +} + +class BadA implements BadD { } +class BadB extends BadA { } +interface BadD extends BadB { } + +class BadE implements BadE { } + +class Empty { }; + +function heritageRecursionTest() { + let e1: Empty = new BadA(); + let e2: Empty = new BadE(); +} diff --git a/linter/test/type_declarations.ts.relax.json b/linter/test/type_declarations.ts.relax.json index 1afccae65d0a0977ff8a0d74956244e862911db4..3d5416802480ef30d565ddc3477be63d3e255b6b 100644 --- a/linter/test/type_declarations.ts.relax.json +++ b/linter/test/type_declarations.ts.relax.json @@ -74,6 +74,26 @@ "column": 13, "problem": "AnyType" }, + { + "line": 101, + "column": 1, + "problem": "InterfaceExtendsClass" + }, + { + "line": 103, + "column": 23, + "problem": "ImplementsClass" + }, + { + "line": 108, + "column": 7, + "problem": "StructuralIdentity" + }, + { + "line": 109, + "column": 7, + "problem": "StructuralIdentity" + }, { "line": 42, "column": 11, diff --git a/linter/test/type_declarations.ts.strict.json b/linter/test/type_declarations.ts.strict.json index e34d68a7e3be144f26fbdad23ea26c7249e60a22..6bb3bf580079dad1cefae5e991ed4c24265e14da 100644 --- a/linter/test/type_declarations.ts.strict.json +++ b/linter/test/type_declarations.ts.strict.json @@ -84,6 +84,26 @@ "column": 13, "problem": "AnyType" }, + { + "line": 101, + "column": 1, + "problem": "InterfaceExtendsClass" + }, + { + "line": 103, + "column": 23, + "problem": "ImplementsClass" + }, + { + "line": 108, + "column": 7, + "problem": "StructuralIdentity" + }, + { + "line": 109, + "column": 7, + "problem": "StructuralIdentity" + }, { "line": 42, "column": 11,