diff --git a/ets2panda/linter/src/lib/TypeScriptLinter.ts b/ets2panda/linter/src/lib/TypeScriptLinter.ts index 99cce1c98424fc43371285b4e0611b550ac811b1..e60a5a89f536d5f7a4a327c085304e13a4ddd037 100644 --- a/ets2panda/linter/src/lib/TypeScriptLinter.ts +++ b/ets2panda/linter/src/lib/TypeScriptLinter.ts @@ -3676,6 +3676,10 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { const baseReturnType = this.tsTypeChecker.getTypeAtLocation(baseMethod.type); const derivedReturnType = this.tsTypeChecker.getTypeAtLocation(derivedMethod.type); + if (this.isDerivedTypeAssignable(derivedReturnType, baseReturnType)) { + return; + } + if (!this.isTypeAssignable(derivedReturnType, baseReturnType)) { this.incrementCounters(derivedMethod.type, FaultID.MethodInheritRule); } @@ -3714,6 +3718,9 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { // Checks structural assignability between two types. private isTypeAssignable(fromType: ts.Type, toType: ts.Type): boolean { + if (this.isDerivedTypeAssignable(fromType, toType)) { + return true; + } const fromTypes = this.flattenUnionTypes(fromType); const toTypes = new Set(this.flattenUnionTypes(toType)); @@ -3723,6 +3730,35 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { }); } + private isDerivedTypeAssignable(derivedType: ts.Type, baseType: ts.Type): boolean { + const baseSymbol = baseType.getSymbol(); + const derivedSymbol = derivedType.getSymbol(); + + if (!baseSymbol || !derivedSymbol) { + return false; + } + const baseDeclarations = baseSymbol.getDeclarations(); + const derivedDeclarations = derivedSymbol.getDeclarations(); + + if (!baseDeclarations || !derivedDeclarations) { + return false; + } + const baseTypeNode = baseDeclarations[0]; + const derivedTypeNode = derivedDeclarations[0]; + + if (ts.isClassDeclaration(baseTypeNode) && ts.isClassDeclaration(derivedTypeNode)) { + const baseTypes = this.tsTypeChecker.getTypeAtLocation(derivedTypeNode).getBaseTypes(); + const baseTypesExtends = baseTypes?.some((t) => { + return t === baseType; + }); + if (baseTypesExtends) { + return true; + } + } + + return false; + } + // Converts union types into an array of type strings for easy comparison. private flattenUnionTypes(type: ts.Type): string[] { if (type.isUnion()) { diff --git a/ets2panda/linter/test/main/method_inheritance.ets b/ets2panda/linter/test/main/method_inheritance.ets index c92fa1f0d9d509516fdbe3eabe31b71fee67282c..5a40e9462b56cc77b00a88a9b5f4ebfbff26328c 100644 --- a/ets2panda/linter/test/main/method_inheritance.ets +++ b/ets2panda/linter/test/main/method_inheritance.ets @@ -216,3 +216,18 @@ class Derived4 implements Base3 { return new A1(); } } + +class Animal {} +class Dog extends Animal {} + +class Base6 { + public foo(): Animal { + console.log("base") + } +} +// extends +class Derived6 extends Base6 { + public foo(): Dog { // no error + console.log("Derived:") + } +} \ No newline at end of file