@@ -421,32 +421,43 @@ export class TypeTable {
421
421
return id ;
422
422
}
423
423
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 ] > ( ) ;
426
431
427
432
/**
428
433
* Gets the canonical ID for the given type, generating a fresh ID if necessary.
429
434
*
430
435
* Returns `null` if we do not support extraction of this type.
431
436
*/
432
437
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 {
445
453
if ( this . typeRecursionDepth > 100 ) {
446
454
// Ignore infinitely nested anonymous types, such as `{x: {x: {x: ... }}}`.
447
455
// Such a type can't be written directly with TypeScript syntax (as it would need to be named),
448
456
// 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 ;
450
461
}
451
462
// Replace very long string literal types with `string`.
452
463
if ( ( type . flags & ts . TypeFlags . StringLiteral ) && ( ( type as ts . LiteralType ) . value as string ) . length > 30 ) {
@@ -455,12 +466,12 @@ export class TypeTable {
455
466
++ this . typeRecursionDepth ;
456
467
let content = this . getTypeString ( type , unfoldAlias ) ;
457
468
-- this . typeRecursionDepth ;
458
- if ( content == null ) return setAndReturn ( null ) ; // Type not supported.
469
+ if ( content == null ) return null ; // Type not supported.
459
470
let id = this . typeIds . get ( content ) ;
460
471
if ( id == null ) {
461
472
let stringValue = this . stringifyType ( type , unfoldAlias ) ;
462
473
if ( stringValue == null ) {
463
- return setAndReturn ( null ) ; // Type not supported.
474
+ return null ; // Type not supported.
464
475
}
465
476
id = this . typeIds . size ;
466
477
this . typeIds . set ( content , id ) ;
@@ -485,7 +496,7 @@ export class TypeTable {
485
496
this . buildTypeWorklist . push ( [ type , id , unfoldAlias ] ) ;
486
497
}
487
498
}
488
- return setAndReturn ( id ) ;
499
+ return id ;
489
500
}
490
501
491
502
private stringifyType ( type : ts . Type , unfoldAlias : boolean ) : string {
0 commit comments