@@ -421,32 +421,43 @@ export class TypeTable {
421421 return id ;
422422 }
423423
424- // type -> [id (not unfolded), id (unfolded)]
425- private idCache = new WeakMap < ts . Type , [ number | undefined , number | undefined ] > ( ) ;
424+ /**
425+ * Caches the result of `getId`: `type -> [id (not unfolded), id (unfolded)]`.
426+ *
427+ * A value of `undefined` means the value is not yet computed,
428+ * and `number | null` corresponds to the return value of `getId`.
429+ */
430+ private idCache = new WeakMap < ts . Type , [ number | null | undefined , number | null | undefined ] > ( ) ;
426431
427432 /**
428433 * Gets the canonical ID for the given type, generating a fresh ID if necessary.
429434 *
430435 * Returns `null` if we do not support extraction of this type.
431436 */
432437 public getId ( type : ts . Type , unfoldAlias : boolean ) : number | null {
433- const cached = this . idCache . get ( type ) ;
434- if ( typeof cached !== "undefined" ) {
435- let value = cached [ unfoldAlias ? 1 : 0 ] ;
436- if ( typeof value !== "undefined" ) return value ;
437- }
438- const setAndReturn = ( v : number | null ) => {
439- let cached = this . idCache . get ( type ) || [ undefined , undefined ] ;
440- cached [ unfoldAlias ? 1 : 0 ] = v ;
441- this . idCache . set ( type , cached ) ;
442- return v ;
443- }
444-
438+ let cached = this . idCache . get ( type ) ?? [ undefined , undefined ] ;
439+ let cachedValue = cached [ unfoldAlias ? 1 : 0 ] ;
440+ if ( cachedValue !== undefined ) return cachedValue ;
441+
442+ let result = this . getIdRaw ( type , unfoldAlias ) ;
443+ cached [ unfoldAlias ? 1 : 0 ] = result ;
444+ return result ;
445+ }
446+
447+ /**
448+ * Gets the canonical ID for the given type, generating a fresh ID if necessary.
449+ *
450+ * Returns `null` if we do not support extraction of this type.
451+ */
452+ public getIdRaw ( type : ts . Type , unfoldAlias : boolean ) : number | null {
445453 if ( this . typeRecursionDepth > 100 ) {
446454 // Ignore infinitely nested anonymous types, such as `{x: {x: {x: ... }}}`.
447455 // Such a type can't be written directly with TypeScript syntax (as it would need to be named),
448456 // but it can occur rarely as a result of type inference.
449- return setAndReturn ( null ) ;
457+
458+ // Caching this value is technically incorrect, as a type might be seen at depth 101 and then we cache the fact that it can't be extracted.
459+ // Then later the type is seen at a lower depth and could be extracted, but then we immediately give up because of the cached failure.
460+ return null ;
450461 }
451462 // Replace very long string literal types with `string`.
452463 if ( ( type . flags & ts . TypeFlags . StringLiteral ) && ( ( type as ts . LiteralType ) . value as string ) . length > 30 ) {
@@ -455,12 +466,12 @@ export class TypeTable {
455466 ++ this . typeRecursionDepth ;
456467 let content = this . getTypeString ( type , unfoldAlias ) ;
457468 -- this . typeRecursionDepth ;
458- if ( content == null ) return setAndReturn ( null ) ; // Type not supported.
469+ if ( content == null ) return null ; // Type not supported.
459470 let id = this . typeIds . get ( content ) ;
460471 if ( id == null ) {
461472 let stringValue = this . stringifyType ( type , unfoldAlias ) ;
462473 if ( stringValue == null ) {
463- return setAndReturn ( null ) ; // Type not supported.
474+ return null ; // Type not supported.
464475 }
465476 id = this . typeIds . size ;
466477 this . typeIds . set ( content , id ) ;
@@ -485,7 +496,7 @@ export class TypeTable {
485496 this . buildTypeWorklist . push ( [ type , id , unfoldAlias ] ) ;
486497 }
487498 }
488- return setAndReturn ( id ) ;
499+ return id ;
489500 }
490501
491502 private stringifyType ( type : ts . Type , unfoldAlias : boolean ) : string {
0 commit comments