@@ -55,6 +55,12 @@ import { NodeUtils } from "../utils/node.utils";
5555import path from "path" ;
5656import { FileUtils } from "../utils/file.utils" ;
5757
58+ let MAX_TYPE_RESOLUTION_DEPTH = 10 ;
59+
60+ export function setMaxTypeResolutionDepth ( maxDepth : number ) {
61+ MAX_TYPE_RESOLUTION_DEPTH = maxDepth ;
62+ }
63+
5864/**
5965 * Returns the type for a given class property (with a non-computed name)
6066 * @param esProperty property name node provided in ESTree
@@ -301,7 +307,13 @@ export function parseESNodeType(processingContext: ProcessingContext, esNode: ES
301307 return result ;
302308}
303309
304- function parseType ( processingContext : ProcessingContext , type : Type , node : Node , excludedFQN ?: string , ignoreDependencies = false ) : LCEType {
310+ function parseType ( processingContext : ProcessingContext , type : Type , node : Node , excludedFQN ?: string , ignoreDependencies = false , typeResolutionDepth = 0 ) : LCEType {
311+
312+ // cut off type resolution at a certain depth to prevent graph clutter and potential infinite recursion
313+ if ( typeResolutionDepth > MAX_TYPE_RESOLUTION_DEPTH ) {
314+ return LCETypeNotIdentified . RESOLUTION_LIMIT ;
315+ }
316+
305317 const globalContext = processingContext . globalContext ;
306318 const tc = globalContext . typeChecker ;
307319 try {
@@ -341,7 +353,7 @@ function parseType(processingContext: ProcessingContext, type: Type, node: Node,
341353 }
342354
343355 // anonymous type
344- return parseAnonymousType ( processingContext , type , node , symbol , excludedFQN , ignoreDependencies ) ;
356+ return parseAnonymousType ( processingContext , type , node , symbol , excludedFQN , ignoreDependencies , typeResolutionDepth ) ;
345357 }
346358
347359 if ( type . isTypeParameter ( ) ) {
@@ -413,17 +425,17 @@ function parseType(processingContext: ProcessingContext, type: Type, node: Node,
413425
414426 if ( normalizedFqn . globalFqn === excludedFQN ) {
415427 // if declared type would reference excluded fqn (e.g. variable name), treat as anonymous type
416- return parseAnonymousType ( processingContext , type , node , symbol , excludedFQN , ignoreDependencies ) ;
428+ return parseAnonymousType ( processingContext , type , node , symbol , excludedFQN , ignoreDependencies , typeResolutionDepth ) ;
417429 }
418430
419431 const typeArguments : LCEType [ ] = [ ] ;
420432 for ( let i = 0 ; i < tc . getTypeArguments ( type as TypeReference ) . length ; i ++ ) {
421433 const ta = tc . getTypeArguments ( type as TypeReference ) [ i ] ;
422434 if ( "typeArguments" in node && node . typeArguments ) {
423435 // if type argument child node is available, pass it on
424- typeArguments . push ( parseType ( processingContext , ta , ( node . typeArguments as Node [ ] ) . at ( i ) ?? node , excludedFQN , ignoreDependencies ) )
436+ typeArguments . push ( parseType ( processingContext , ta , ( node . typeArguments as Node [ ] ) . at ( i ) ?? node , excludedFQN , ignoreDependencies , typeResolutionDepth + 1 ) )
425437 } else {
426- typeArguments . push ( parseType ( processingContext , ta , node , excludedFQN , ignoreDependencies ) ) ;
438+ typeArguments . push ( parseType ( processingContext , ta , node , excludedFQN , ignoreDependencies , typeResolutionDepth + 1 ) ) ;
427439 }
428440 }
429441
@@ -451,23 +463,24 @@ function parseAnonymousType(
451463 node : Node ,
452464 symbol ?: Symbol ,
453465 excludedFQN ?: string ,
454- ignoreDependencies = false
466+ ignoreDependencies = false ,
467+ typeResolutionDepth = 0
455468) : LCEType {
456469 const globalContext = processingContext . globalContext ;
457470 const tc = globalContext . typeChecker ;
458471
459472 // complex anonymous type
460473 if ( type . isUnion ( ) ) {
461474 // union type
462- return new LCETypeUnion ( type . types . map ( ( t ) => parseType ( processingContext , t , node , excludedFQN , ignoreDependencies ) ) ) ;
475+ return new LCETypeUnion ( type . types . map ( ( t ) => parseType ( processingContext , t , node , excludedFQN , ignoreDependencies , typeResolutionDepth + 1 ) ) ) ;
463476 } else if ( type . isIntersection ( ) ) {
464477 // intersection type
465- return new LCETypeIntersection ( type . types . map ( ( t ) => parseType ( processingContext , t , node , excludedFQN , ignoreDependencies ) ) ) ;
478+ return new LCETypeIntersection ( type . types . map ( ( t ) => parseType ( processingContext , t , node , excludedFQN , ignoreDependencies , typeResolutionDepth + 1 ) ) ) ;
466479 } else if ( type . getCallSignatures ( ) . length > 0 ) {
467480 if ( type . getCallSignatures ( ) . length > 1 ) return new LCETypeNotIdentified ( tc . typeToString ( type ) ) ;
468481 // function type
469482 const signature = type . getCallSignatures ( ) [ 0 ] ;
470- const returnType = parseType ( processingContext , tc . getReturnTypeOfSignature ( signature ) , node , excludedFQN , ignoreDependencies ) ;
483+ const returnType = parseType ( processingContext , tc . getReturnTypeOfSignature ( signature ) , node , excludedFQN , ignoreDependencies , typeResolutionDepth + 1 ) ;
471484 const parameters : LCETypeFunctionParameter [ ] = [ ] ;
472485 const paramSyms = signature . getParameters ( ) ;
473486 for ( let i = 0 ; i < paramSyms . length ; i ++ ) {
@@ -480,7 +493,7 @@ function parseAnonymousType(
480493 i ,
481494 parameterSym . name ,
482495 optional ,
483- parseType ( processingContext , paramType , node , excludedFQN , ignoreDependencies )
496+ parseType ( processingContext , paramType , node , excludedFQN , ignoreDependencies , typeResolutionDepth + 1 )
484497 )
485498 ) ;
486499 }
@@ -499,7 +512,7 @@ function parseAnonymousType(
499512 const propType = tc . getTypeOfSymbolAtLocation ( prop , node ) ;
500513 members . push ( new LCETypeObjectMember (
501514 prop . name ,
502- parseType ( processingContext , propType , node , undefined , ignoreDependencies ) ,
515+ parseType ( processingContext , propType , node , undefined , ignoreDependencies , typeResolutionDepth + 1 ) ,
503516 optional ,
504517 readonly
505518 ) ) ;
@@ -523,7 +536,7 @@ function parseAnonymousType(
523536 const typeArgs = tc . getTypeArguments ( type as TypeReference ) ;
524537 const types : LCEType [ ] = [ ] ;
525538 for ( const typeArg of typeArgs ) {
526- types . push ( parseType ( processingContext , typeArg , node , excludedFQN , ignoreDependencies ) ) ;
539+ types . push ( parseType ( processingContext , typeArg , node , excludedFQN , ignoreDependencies , typeResolutionDepth + 1 ) ) ;
527540 }
528541 return new LCETypeTuple ( types ) ;
529542 }
0 commit comments