diff --git a/ets2panda/linter/src/lib/TypeScriptLinter.ts b/ets2panda/linter/src/lib/TypeScriptLinter.ts index ca04dbcfcd70cd084c01b8f28c23f983dd279534..b309b414838687c558bddec2450fc0357af9fd56 100644 --- a/ets2panda/linter/src/lib/TypeScriptLinter.ts +++ b/ets2panda/linter/src/lib/TypeScriptLinter.ts @@ -3477,6 +3477,9 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { // Check if every type in baseType is also present in derivedType for (const typeStr of baseTypeSet) { if (!derivedTypeSet.has(typeStr)) { + if (TypeScriptLinter.areWrapperAndPrimitiveTypesEqual(typeStr, derivedTypeSet)) { + continue; + } return false; } } @@ -3494,10 +3497,30 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { // All types in `fromTypes` should exist in `toTypes` for assignability. return fromTypes.every((typeStr) => { - return toTypes.has(typeStr); + if (toTypes.has(typeStr)) { + return true; + } + return TypeScriptLinter.areWrapperAndPrimitiveTypesEqual(typeStr, toTypes); }); } + // Check if a type string has an equivalent primitive/wrapper type in a set + private static areWrapperAndPrimitiveTypesEqual(typeStr: string, typeSet: Set): boolean { + const typePairs = [ + ['String', 'string'], + ['Number', 'number'], + ['Boolean', 'boolean'] + ]; + + for (const [wrapper, primitive] of typePairs) { + if ((typeStr === wrapper && typeSet.has(primitive)) || + (typeStr === primitive && typeSet.has(wrapper))) { + return true; + } + } + return false; + } + private isDerivedTypeAssignable(derivedType: ts.Type, baseType: ts.Type): boolean { const baseSymbol = baseType.getSymbol(); const derivedSymbol = derivedType.getSymbol(); @@ -3529,12 +3552,29 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { // Converts union types into an array of type strings for easy comparison. private flattenUnionTypes(type: ts.Type): string[] { - if (type.isUnion()) { - return type.types.map((t) => { - return this.tsTypeChecker.typeToString(t); - }); - } - return [this.tsTypeChecker.typeToString(type)]; + if (type.isUnion()) { + return type.types.map((t) => { + return TypeScriptLinter.normalizeTypeString(this.tsTypeChecker.typeToString(t)); + }); + } + return [TypeScriptLinter.normalizeTypeString(this.tsTypeChecker.typeToString(type))]; + } + + // Normalize type string to handle primitive wrapper types consistently + private static normalizeTypeString(typeStr: string): string { + // Handle all primitive wrapper types + const wrapperToPrimitive: Record = { + 'String': 'string', + 'Number': 'number', + 'Boolean': 'boolean' + }; + + // Replace wrapper types with their primitive counterparts + let normalized = typeStr; + for (const [wrapper, primitive] of Object.entries(wrapperToPrimitive)) { + normalized = normalized.replace(new RegExp(wrapper, 'g'), primitive); + } + return normalized; } private checkClassImplementsMethod(classDecl: ts.ClassDeclaration, methodName: string): boolean { diff --git a/ets2panda/linter/test/main/method_inheritance.ets b/ets2panda/linter/test/main/method_inheritance.ets index 12c5ac3f16600aae1b20511c342f35a0aa1d262d..fe85d554a311e7709436d4d4ac2a805b4438c010 100644 --- a/ets2panda/linter/test/main/method_inheritance.ets +++ b/ets2panda/linter/test/main/method_inheritance.ets @@ -359,5 +359,16 @@ class BBB2 extends AAA { class BBB3 extends AAA { test() { //error + } +} + + +abstract class Test1 { + abstract test(params: Map): void; +} + +class Test2 extends Test1 { + test(params: Map): void { // no Error + } } \ No newline at end of file