From 92d6a8bcdb3ca544eb4562cece4de9e1847aba56 Mon Sep 17 00:00:00 2001 From: Moses Odutusin <48915357+thebolarin@users.noreply.github.com> Date: Thu, 18 Sep 2025 17:15:47 +0100 Subject: [PATCH 1/7] chore: check if superclass is ignored --- .../src/rules/no-base-to-string.ts | 42 ++++++++++++++++++- .../tests/rules/no-base-to-string.test.ts | 38 +++++++++++++++++ 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-base-to-string.ts b/packages/eslint-plugin/src/rules/no-base-to-string.ts index 53d50bd4694f..5cae5f64e800 100644 --- a/packages/eslint-plugin/src/rules/no-base-to-string.ts +++ b/packages/eslint-plugin/src/rules/no-base-to-string.ts @@ -212,6 +212,44 @@ export default createRule({ return Usefulness.Always; } + function hasBaseTypes(type: ts.Type): type is ts.InterfaceType { + return ( + (type.flags & ts.TypeFlags.Object) !== 0 && + (((type as ts.ObjectType).objectFlags & ts.ObjectFlags.Interface) !== + 0 || + ((type as ts.ObjectType).objectFlags & ts.ObjectFlags.Class) !== 0) + ); + } + + function isIgnoredTypeOrBase( + type: ts.Type, + seen = new Set(), + cache = new Map(), + ): boolean { + if (seen.has(type)) { + return false; + } + + const cached = cache.get(type); + if (cached != null) { + return cached; + } + + seen.add(type); + + const typeName = getTypeName(checker, type); + let result = ignoredTypeNames.includes(typeName); + + if (!result && hasBaseTypes(type)) { + result = checker + .getBaseTypes(type) + .some(base => isIgnoredTypeOrBase(base, seen, cache)); + } + + cache.set(type, result); + return result; + } + function collectToStringCertainty( type: ts.Type, visited: Set, @@ -248,8 +286,8 @@ export default createRule({ ) { return Usefulness.Always; } - - if (ignoredTypeNames.includes(getTypeName(checker, type))) { + + if (isIgnoredTypeOrBase(type)) { return Usefulness.Always; } diff --git a/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts b/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts index 7e6fa055b55e..234a723806cc 100644 --- a/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts +++ b/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts @@ -324,6 +324,27 @@ error.toString(); `, options: [{ ignoredTypeNames: ['MyError'] }], }, + { + code: ` +interface Animal {} +interface Serializable {} +interface Cat extends Animal, Serializable {} + +declare const whiskers: Cat; +whiskers.toString(); + `, + options: [{ ignoredTypeNames: ['Animal'] }], + }, + { + code: ` +class UnknownBase {} +class CustomError extends UnknownBase {} + +declare const err: CustomError; +err.toString(); + `, + options: [{ ignoredTypeNames: ['UnknownBase'] }], + }, ` function String(value) { return value; @@ -2265,5 +2286,22 @@ v.join(); }, ], }, + { + code: ` +interface Dog extends Animal {} + +declare const labrador: Dog; +labrador.toString(); + `, + errors: [ + { + data: { + certainty: 'will', + name: 'labrador', + }, + messageId: 'baseToString', + }, + ], + }, ], }); From e6efdecca9ab1357cdee9d7784840a0667ddea1f Mon Sep 17 00:00:00 2001 From: Moses Odutusin <48915357+thebolarin@users.noreply.github.com> Date: Thu, 18 Sep 2025 17:46:16 +0100 Subject: [PATCH 2/7] chore: fix linting issues --- .../tests/rules/no-base-to-string.test.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts b/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts index 234a723806cc..e7cb7bb48cc3 100644 --- a/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts +++ b/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts @@ -337,14 +337,23 @@ whiskers.toString(); }, { code: ` +interface MyError extends Error {} + +declare const error: MyError; +error.toString(); + `, + }, + { + code: ` class UnknownBase {} class CustomError extends UnknownBase {} declare const err: CustomError; err.toString(); - `, + `, options: [{ ignoredTypeNames: ['UnknownBase'] }], }, + ` function String(value) { return value; From 0a5090d1c7deb6eb2855e15592f034226de56afe Mon Sep 17 00:00:00 2001 From: Moses Odutusin <48915357+thebolarin@users.noreply.github.com> Date: Thu, 18 Sep 2025 18:34:57 +0100 Subject: [PATCH 3/7] chore: increase code coverage --- .../tests/rules/no-base-to-string.test.ts | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts b/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts index e7cb7bb48cc3..3f4cd3eb130b 100644 --- a/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts +++ b/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts @@ -353,7 +353,18 @@ err.toString(); `, options: [{ ignoredTypeNames: ['UnknownBase'] }], }, + { + code: ` +interface Animal {} +interface Dog extends Animal {} +interface Cat extends Animal {} +declare const dog: Dog; +declare const cat: Cat; +cat.toString(); + `, + options: [{ ignoredTypeNames: ['Animal'] }], + }, ` function String(value) { return value; @@ -2312,5 +2323,23 @@ labrador.toString(); }, ], }, + { + code: ` +interface A extends B {} +interface B extends A {} + +declare const a: A; +a.toString(); + `, + errors: [ + { + data: { + certainty: 'will', + name: 'a', + }, + messageId: 'baseToString', + }, + ], + }, ], }); From fb612b465e2504e60620dd37bdb33afee9786312 Mon Sep 17 00:00:00 2001 From: Moses Odutusin <48915357+thebolarin@users.noreply.github.com> Date: Fri, 19 Sep 2025 13:05:15 +0100 Subject: [PATCH 4/7] chore: add more test --- .../src/rules/no-base-to-string.ts | 9 +-------- .../tests/rules/no-base-to-string.test.ts | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-base-to-string.ts b/packages/eslint-plugin/src/rules/no-base-to-string.ts index 5cae5f64e800..4a1600479072 100644 --- a/packages/eslint-plugin/src/rules/no-base-to-string.ts +++ b/packages/eslint-plugin/src/rules/no-base-to-string.ts @@ -224,17 +224,11 @@ export default createRule({ function isIgnoredTypeOrBase( type: ts.Type, seen = new Set(), - cache = new Map(), ): boolean { if (seen.has(type)) { return false; } - const cached = cache.get(type); - if (cached != null) { - return cached; - } - seen.add(type); const typeName = getTypeName(checker, type); @@ -243,10 +237,9 @@ export default createRule({ if (!result && hasBaseTypes(type)) { result = checker .getBaseTypes(type) - .some(base => isIgnoredTypeOrBase(base, seen, cache)); + .some(base => isIgnoredTypeOrBase(base, seen)); } - cache.set(type, result); return result; } diff --git a/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts b/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts index 3f4cd3eb130b..a5195fb1ea50 100644 --- a/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts +++ b/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts @@ -2341,5 +2341,25 @@ a.toString(); }, ], }, + { + code: ` + interface Base {} + interface Left extends Base {} + interface Right extends Base {} + interface Diamond extends Left, Right {} + + declare const d: Diamond; + d.toString(); + `, + errors: [ + { + data: { + certainty: 'will', + name: 'd', + }, + messageId: 'baseToString', + }, + ], + }, ], }); From 584d8bfe7a1998431b5f8651cc74e278bd76f10d Mon Sep 17 00:00:00 2001 From: Moses Odutusin <48915357+thebolarin@users.noreply.github.com> Date: Mon, 22 Sep 2025 19:38:09 +0100 Subject: [PATCH 5/7] chore: adds requested changes --- .../src/rules/no-base-to-string.ts | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-base-to-string.ts b/packages/eslint-plugin/src/rules/no-base-to-string.ts index 4a1600479072..addbd2cfd2d7 100644 --- a/packages/eslint-plugin/src/rules/no-base-to-string.ts +++ b/packages/eslint-plugin/src/rules/no-base-to-string.ts @@ -214,10 +214,11 @@ export default createRule({ function hasBaseTypes(type: ts.Type): type is ts.InterfaceType { return ( - (type.flags & ts.TypeFlags.Object) !== 0 && - (((type as ts.ObjectType).objectFlags & ts.ObjectFlags.Interface) !== - 0 || - ((type as ts.ObjectType).objectFlags & ts.ObjectFlags.Class) !== 0) + tsutils.isObjectType(type) && + tsutils.isObjectFlagSet( + type, + ts.ObjectFlags.Interface | ts.ObjectFlags.Class, + ) ); } @@ -232,15 +233,13 @@ export default createRule({ seen.add(type); const typeName = getTypeName(checker, type); - let result = ignoredTypeNames.includes(typeName); - - if (!result && hasBaseTypes(type)) { - result = checker - .getBaseTypes(type) - .some(base => isIgnoredTypeOrBase(base, seen)); - } - - return result; + return ( + ignoredTypeNames.includes(typeName) || + (hasBaseTypes(type) && + checker + .getBaseTypes(type) + .some(base => isIgnoredTypeOrBase(base, seen))) + ); } function collectToStringCertainty( From a92783a3961a4044a82db8cbc25dd0c7c6cb8ba1 Mon Sep 17 00:00:00 2001 From: Moses Odutusin <48915357+thebolarin@users.noreply.github.com> Date: Mon, 22 Sep 2025 20:35:56 +0100 Subject: [PATCH 6/7] chore: fix format issues --- packages/eslint-plugin/src/rules/no-base-to-string.ts | 2 +- packages/eslint-plugin/tests/rules/no-base-to-string.test.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-base-to-string.ts b/packages/eslint-plugin/src/rules/no-base-to-string.ts index addbd2cfd2d7..3612dc3da1ce 100644 --- a/packages/eslint-plugin/src/rules/no-base-to-string.ts +++ b/packages/eslint-plugin/src/rules/no-base-to-string.ts @@ -278,7 +278,7 @@ export default createRule({ ) { return Usefulness.Always; } - + if (isIgnoredTypeOrBase(type)) { return Usefulness.Always; } diff --git a/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts b/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts index a5195fb1ea50..a75e9130b921 100644 --- a/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts +++ b/packages/eslint-plugin/tests/rules/no-base-to-string.test.ts @@ -336,12 +336,12 @@ whiskers.toString(); options: [{ ignoredTypeNames: ['Animal'] }], }, { - code: ` + code: ` interface MyError extends Error {} declare const error: MyError; error.toString(); - `, + `, }, { code: ` From 68d0f696a7a95e0107917987e3b4c7e03352b370 Mon Sep 17 00:00:00 2001 From: Moses Odutusin <48915357+thebolarin@users.noreply.github.com> Date: Mon, 22 Sep 2025 21:37:44 +0100 Subject: [PATCH 7/7] chore: trigger ci