|
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"; |
2 | 8 | import ts, { SyntaxKind } from "typescript"; |
3 | 9 | import { defineRule, getTypeName } from "../_utils/index.ts"; |
4 | 10 | import { isIdentifierFromDefaultLibrary } from "../_utils/isBuiltinSymbolLike.ts"; |
@@ -215,6 +221,34 @@ function collectJoinCertainty( |
215 | 221 | return "always"; |
216 | 222 | } |
217 | 223 |
|
| 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 | + |
218 | 252 | function collectToStringCertainty( |
219 | 253 | context: Context, |
220 | 254 | options: ParsedOptions, |
@@ -243,11 +277,9 @@ function collectToStringCertainty( |
243 | 277 | return "always"; |
244 | 278 | } |
245 | 279 |
|
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 | + } |
251 | 283 |
|
252 | 284 | if (type.isIntersection()) { |
253 | 285 | return collectIntersectionTypeCertainty(type, (t) => |
|
0 commit comments