1
+ import type { ScopeVariable } from '@typescript-eslint/scope-manager' ;
1
2
import type { TSESLint , TSESTree } from '@typescript-eslint/utils' ;
2
3
import type { ReportFixFunction } from '@typescript-eslint/utils/ts-eslint' ;
3
4
@@ -6,6 +7,7 @@ import { AST_NODE_TYPES, ASTUtils } from '@typescript-eslint/utils';
6
7
import {
7
8
createRule ,
8
9
getFixOrSuggest ,
10
+ isNodeEqual ,
9
11
isParenthesized ,
10
12
nullThrows ,
11
13
} from '../util' ;
@@ -78,16 +80,12 @@ export default createRule<Options, MessageIds>({
78
80
if ( parentId ) {
79
81
const scope = context . sourceCode . getScope ( parentId ) ;
80
82
const superVar = ASTUtils . findVariable ( scope , parentId . name ) ;
81
- if ( superVar ) {
82
- const isCircular = superVar . references . some (
83
- item =>
84
- item . isTypeReference &&
85
- node . range [ 0 ] <= item . identifier . range [ 0 ] &&
86
- node . range [ 1 ] >= item . identifier . range [ 1 ] ,
87
- ) ;
88
- if ( isCircular ) {
89
- return ;
90
- }
83
+
84
+ if (
85
+ superVar &&
86
+ isDeeplyReferencingType ( node , superVar , new Set ( [ parentId ] ) )
87
+ ) {
88
+ return ;
91
89
}
92
90
}
93
91
@@ -269,3 +267,89 @@ function findParentDeclaration(
269
267
}
270
268
return undefined ;
271
269
}
270
+
271
+ function isDeeplyReferencingType (
272
+ node : TSESTree . Node ,
273
+ superVar : ScopeVariable ,
274
+ visited : Set < TSESTree . Node > ,
275
+ ) : boolean {
276
+ if ( visited . has ( node ) ) {
277
+ // something on the chain is circular but it's not the reference being checked
278
+ return false ;
279
+ }
280
+
281
+ visited . add ( node ) ;
282
+
283
+ switch ( node . type ) {
284
+ case AST_NODE_TYPES . TSTypeLiteral :
285
+ return node . members . some ( member =>
286
+ isDeeplyReferencingType ( member , superVar , visited ) ,
287
+ ) ;
288
+ case AST_NODE_TYPES . TSTypeAliasDeclaration :
289
+ return isDeeplyReferencingType ( node . typeAnnotation , superVar , visited ) ;
290
+ case AST_NODE_TYPES . TSIndexedAccessType :
291
+ return [ node . indexType , node . objectType ] . some ( type =>
292
+ isDeeplyReferencingType ( type , superVar , visited ) ,
293
+ ) ;
294
+ case AST_NODE_TYPES . TSConditionalType :
295
+ return [
296
+ node . checkType ,
297
+ node . extendsType ,
298
+ node . falseType ,
299
+ node . trueType ,
300
+ ] . some ( type => isDeeplyReferencingType ( type , superVar , visited ) ) ;
301
+ case AST_NODE_TYPES . TSUnionType :
302
+ case AST_NODE_TYPES . TSIntersectionType :
303
+ return node . types . some ( type =>
304
+ isDeeplyReferencingType ( type , superVar , visited ) ,
305
+ ) ;
306
+ case AST_NODE_TYPES . TSInterfaceDeclaration :
307
+ return node . body . body . some ( type =>
308
+ isDeeplyReferencingType ( type , superVar , visited ) ,
309
+ ) ;
310
+ case AST_NODE_TYPES . TSTypeAnnotation :
311
+ return isDeeplyReferencingType ( node . typeAnnotation , superVar , visited ) ;
312
+ case AST_NODE_TYPES . TSIndexSignature : {
313
+ if ( node . typeAnnotation ) {
314
+ return isDeeplyReferencingType ( node . typeAnnotation , superVar , visited ) ;
315
+ }
316
+ break ;
317
+ }
318
+ case AST_NODE_TYPES . TSTypeParameterInstantiation : {
319
+ return node . params . some ( param =>
320
+ isDeeplyReferencingType ( param , superVar , visited ) ,
321
+ ) ;
322
+ }
323
+ case AST_NODE_TYPES . TSTypeReference : {
324
+ if ( isDeeplyReferencingType ( node . typeName , superVar , visited ) ) {
325
+ return true ;
326
+ }
327
+
328
+ if (
329
+ node . typeArguments &&
330
+ isDeeplyReferencingType ( node . typeArguments , superVar , visited )
331
+ ) {
332
+ return true ;
333
+ }
334
+
335
+ break ;
336
+ }
337
+ case AST_NODE_TYPES . Identifier : {
338
+ // check if the identifier is a reference of the type being checked
339
+ if ( superVar . references . some ( ref => isNodeEqual ( ref . identifier , node ) ) ) {
340
+ return true ;
341
+ }
342
+
343
+ // otherwise, follow its definition(s)
344
+ const refVar = ASTUtils . findVariable ( superVar . scope , node . name ) ;
345
+
346
+ if ( refVar ) {
347
+ return refVar . defs . some ( def =>
348
+ isDeeplyReferencingType ( def . node , superVar , visited ) ,
349
+ ) ;
350
+ }
351
+ }
352
+ }
353
+
354
+ return false ;
355
+ }
0 commit comments