🌐 AI搜索 & 代理 主页
Skip to content

Commit d059aaa

Browse files
committed
1 parent 35fa361 commit d059aaa

File tree

2 files changed

+59
-6
lines changed

2 files changed

+59
-6
lines changed

src/rules/noBaseToString/noBaseToString.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,14 @@ declare const error: MyError<number>;
504504
error.toString();
505505
`,
506506
},
507+
{
508+
options: { ignoredTypeNames: ["Error"] },
509+
code: `
510+
interface MyError extends Error {}
511+
declare const error: MyError;
512+
error.toString();
513+
`,
514+
},
507515
],
508516
invalid: [
509517
{
@@ -1364,5 +1372,18 @@ x.toString();
13641372
],
13651373
options: { checkUnknown: true },
13661374
},
1375+
{
1376+
code: `
1377+
interface A extends B {}
1378+
interface B extends A {}
1379+
declare const a: A;
1380+
a.toString();
1381+
`,
1382+
errors: [
1383+
{
1384+
message: messages.baseToString({ certainty: "will", name: "a" }),
1385+
},
1386+
],
1387+
},
13671388
],
13681389
});

src/rules/noBaseToString/noBaseToString.ts

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
import { isIntersectionType, isTypeParameter, isUnionType } from "ts-api-utils";
1+
import {
2+
isIntersectionType,
3+
isObjectFlagSet,
4+
isObjectType,
5+
isTypeParameter,
6+
isUnionType,
7+
} from "ts-api-utils";
28
import ts, { SyntaxKind } from "typescript";
39
import { defineRule, getTypeName } from "../_utils/index.ts";
410
import { isIdentifierFromDefaultLibrary } from "../_utils/isBuiltinSymbolLike.ts";
@@ -215,6 +221,34 @@ function collectJoinCertainty(
215221
return "always";
216222
}
217223

224+
function hasBaseTypes(type: ts.Type): type is ts.InterfaceType {
225+
return (
226+
isObjectType(type)
227+
&& isObjectFlagSet(type, ts.ObjectFlags.Interface | ts.ObjectFlags.Class)
228+
);
229+
}
230+
231+
function isIgnoredTypeOrBase(
232+
context: Context,
233+
options: ParsedOptions,
234+
type: ts.Type,
235+
seen = new Set<ts.Type>(),
236+
): boolean {
237+
if (seen.has(type)) return false; // Prevent infinite recursion on circular references
238+
seen.add(type);
239+
240+
let typeName = getTypeName(context.rawChecker, type);
241+
const genericIndex = typeName.indexOf("<");
242+
if (genericIndex !== -1) typeName = typeName.slice(0, genericIndex);
243+
return (
244+
options.ignoredTypeNames.includes(typeName)
245+
|| (hasBaseTypes(type)
246+
&& context.rawChecker
247+
.getBaseTypes(type)
248+
.some((base) => isIgnoredTypeOrBase(context, options, base, seen)))
249+
);
250+
}
251+
218252
function collectToStringCertainty(
219253
context: Context,
220254
options: ParsedOptions,
@@ -243,11 +277,9 @@ function collectToStringCertainty(
243277
return "always";
244278
}
245279

246-
let typeName = getTypeName(context.rawChecker, type);
247-
const genericIndex = typeName.indexOf("<");
248-
if (genericIndex !== -1) typeName = typeName.slice(0, genericIndex);
249-
250-
if (options.ignoredTypeNames.includes(typeName)) return "always";
280+
if (isIgnoredTypeOrBase(context, options, type)) {
281+
return "always";
282+
}
251283

252284
if (type.isIntersection()) {
253285
return collectIntersectionTypeCertainty(type, (t) =>

0 commit comments

Comments
 (0)