@@ -92,6 +92,20 @@ function isTypeofCandidateSymbol(symbol: ts.Symbol) {
9292
9393const signatureKinds = [ ts . SignatureKind . Call , ts . SignatureKind . Construct ] ;
9494
95+ /**
96+ * Bitmask of flags set on a signature, but not exposed in the public API.
97+ */
98+ const enum InternalSignatureFlags {
99+ HasRestParameter = 1
100+ }
101+
102+ /**
103+ * Signature interface with some internal properties exposed.
104+ */
105+ interface AugmentedSignature extends ts . Signature {
106+ flags ?: InternalSignatureFlags ;
107+ }
108+
95109/**
96110 * Encodes property lookup tuples `(baseType, name, property)` as three
97111 * staggered arrays.
@@ -102,6 +116,16 @@ interface PropertyLookupTable {
102116 propertyTypes : number [ ] ;
103117}
104118
119+ /**
120+ * Encodes `(aliasType, underlyingType)` tuples as two staggered arrays.
121+ *
122+ * Such a tuple denotes that `aliasType` is an alias for `underlyingType`.
123+ */
124+ interface TypeAliasTable {
125+ aliasTypes : number [ ] ;
126+ underlyingTypes : number [ ] ;
127+ }
128+
105129/**
106130 * Encodes type signature tuples `(baseType, kind, index, signature)` as four
107131 * staggered arrays.
@@ -287,6 +311,11 @@ export class TypeTable {
287311 propertyTypes : [ ] ,
288312 } ;
289313
314+ private typeAliases : TypeAliasTable = {
315+ aliasTypes : [ ] ,
316+ underlyingTypes : [ ] ,
317+ } ;
318+
290319 private signatureMappings : SignatureTable = {
291320 baseTypes : [ ] ,
292321 kinds : [ ] ,
@@ -304,7 +333,7 @@ export class TypeTable {
304333 propertyTypes : [ ] ,
305334 } ;
306335
307- private buildTypeWorklist : [ ts . Type , number ] [ ] = [ ] ;
336+ private buildTypeWorklist : [ ts . Type , number , boolean ] [ ] = [ ] ;
308337
309338 private expansiveTypes : Map < number , boolean > = new Map ( ) ;
310339
@@ -372,9 +401,9 @@ export class TypeTable {
372401 /**
373402 * Gets the canonical ID for the given type, generating a fresh ID if necessary.
374403 */
375- public buildType ( type : ts . Type ) : number | null {
404+ public buildType ( type : ts . Type , unfoldAlias : boolean ) : number | null {
376405 this . isInShallowTypeContext = false ;
377- let id = this . getId ( type ) ;
406+ let id = this . getId ( type , unfoldAlias ) ;
378407 this . iterateBuildTypeWorklist ( ) ;
379408 if ( id == null ) return null ;
380409 return id ;
@@ -385,7 +414,7 @@ export class TypeTable {
385414 *
386415 * Returns `null` if we do not support extraction of this type.
387416 */
388- public getId ( type : ts . Type ) : number | null {
417+ public getId ( type : ts . Type , unfoldAlias : boolean ) : number | null {
389418 if ( this . typeRecursionDepth > 100 ) {
390419 // Ignore infinitely nested anonymous types, such as `{x: {x: {x: ... }}}`.
391420 // Such a type can't be written directly with TypeScript syntax (as it would need to be named),
@@ -397,19 +426,19 @@ export class TypeTable {
397426 type = this . typeChecker . getBaseTypeOfLiteralType ( type ) ;
398427 }
399428 ++ this . typeRecursionDepth ;
400- let content = this . getTypeString ( type ) ;
429+ let content = this . getTypeString ( type , unfoldAlias ) ;
401430 -- this . typeRecursionDepth ;
402431 if ( content == null ) return null ; // Type not supported.
403432 let id = this . typeIds . get ( content ) ;
404433 if ( id == null ) {
405- let stringValue = this . stringifyType ( type ) ;
434+ let stringValue = this . stringifyType ( type , unfoldAlias ) ;
406435 if ( stringValue == null ) {
407436 return null ; // Type not supported.
408437 }
409438 id = this . typeIds . size ;
410439 this . typeIds . set ( content , id ) ;
411440 this . typeToStringValues . push ( stringValue ) ;
412- this . buildTypeWorklist . push ( [ type , id ] ) ;
441+ this . buildTypeWorklist . push ( [ type , id , unfoldAlias ] ) ;
413442 this . typeExtractionState . push (
414443 this . isInShallowTypeContext ? TypeExtractionState . PendingShallow : TypeExtractionState . PendingFull ) ;
415444 // If the type is the self-type for a named type (not a generic instantiation of it),
@@ -426,20 +455,20 @@ export class TypeTable {
426455 this . typeExtractionState [ id ] = TypeExtractionState . PendingFull ;
427456 } else if ( state === TypeExtractionState . DoneShallow ) {
428457 this . typeExtractionState [ id ] = TypeExtractionState . PendingFull ;
429- this . buildTypeWorklist . push ( [ type , id ] ) ;
458+ this . buildTypeWorklist . push ( [ type , id , unfoldAlias ] ) ;
430459 }
431460 }
432461 return id ;
433462 }
434463
435- private stringifyType ( type : ts . Type ) : string {
464+ private stringifyType ( type : ts . Type , unfoldAlias : boolean ) : string {
465+ let formatFlags = unfoldAlias
466+ ? ts . TypeFormatFlags . InTypeAlias
467+ : ts . TypeFormatFlags . UseAliasDefinedOutsideCurrentScope ;
436468 let toStringValue : string ;
437469 // Some types can't be stringified. Just discard the type if we can't stringify it.
438470 try {
439- toStringValue = this . typeChecker . typeToString (
440- type ,
441- undefined ,
442- ts . TypeFormatFlags . UseAliasDefinedOutsideCurrentScope ) ;
471+ toStringValue = this . typeChecker . typeToString ( type , undefined , formatFlags ) ;
443472 } catch ( e ) {
444473 console . warn ( "Recovered from a compiler crash while stringifying a type. Discarding the type." ) ;
445474 console . warn ( e . stack ) ;
@@ -477,9 +506,9 @@ export class TypeTable {
477506 /**
478507 * Gets a string representing the kind and contents of the given type.
479508 */
480- private getTypeString ( type : AugmentedType ) : string | null {
509+ private getTypeString ( type : AugmentedType , unfoldAlias : boolean ) : string | null {
481510 // Reference to a type alias.
482- if ( type . aliasSymbol != null ) {
511+ if ( ! unfoldAlias && type . aliasSymbol != null ) {
483512 let tag = "reference;" + this . getSymbolId ( type . aliasSymbol ) ;
484513 return type . aliasTypeArguments == null
485514 ? tag
@@ -499,7 +528,7 @@ export class TypeTable {
499528 if ( flags & ts . TypeFlags . TypeVariable ) {
500529 let enclosingType = getEnclosingTypeOfThisType ( type ) ;
501530 if ( enclosingType != null ) {
502- return "this;" + this . getId ( enclosingType ) ;
531+ return "this;" + this . getId ( enclosingType , false ) ;
503532 } else if ( symbol . parent == null ) {
504533 // The type variable is bound on a call signature. Only extract it by name.
505534 return "lextypevar;" + symbol . name ;
@@ -730,7 +759,7 @@ export class TypeTable {
730759 private makeTypeStringVector ( tag : string , types : ReadonlyArray < ts . Type > , length = types . length ) : string | null {
731760 let hash = tag ;
732761 for ( let i = 0 ; i < length ; ++ i ) {
733- let id = this . getId ( types [ i ] ) ;
762+ let id = this . getId ( types [ i ] , false ) ;
734763 if ( id == null ) return null ;
735764 hash += ";" + id ;
736765 }
@@ -748,7 +777,7 @@ export class TypeTable {
748777 for ( let property of type . getProperties ( ) ) {
749778 let propertyType = this . typeChecker . getTypeOfSymbolAtLocation ( property , this . arbitraryAstNode ) ;
750779 if ( propertyType == null ) return null ;
751- let propertyTypeId = this . getId ( propertyType ) ;
780+ let propertyTypeId = this . getId ( propertyType , false ) ;
752781 if ( propertyTypeId == null ) return null ;
753782 hash += ";p" + this . getSymbolId ( property ) + ';' + propertyTypeId ;
754783 }
@@ -761,13 +790,13 @@ export class TypeTable {
761790 }
762791 let indexType = type . getStringIndexType ( ) ;
763792 if ( indexType != null ) {
764- let indexTypeId = this . getId ( indexType ) ;
793+ let indexTypeId = this . getId ( indexType , false ) ;
765794 if ( indexTypeId == null ) return null ;
766795 hash += ";s" + indexTypeId ;
767796 }
768797 indexType = type . getNumberIndexType ( ) ;
769798 if ( indexType != null ) {
770- let indexTypeId = this . getId ( indexType ) ;
799+ let indexTypeId = this . getId ( indexType , false ) ;
771800 if ( indexTypeId == null ) return null ;
772801 hash += ";i" + indexTypeId ;
773802 }
@@ -789,6 +818,7 @@ export class TypeTable {
789818 typeStrings : Array . from ( this . typeIds . keys ( ) ) ,
790819 typeToStringValues : this . typeToStringValues ,
791820 propertyLookups : this . propertyLookups ,
821+ typeAliases : this . typeAliases ,
792822 symbolStrings : Array . from ( this . symbolIds . keys ( ) ) ,
793823 moduleMappings : this . moduleMappings ,
794824 globalMappings : this . globalMappings ,
@@ -812,10 +842,17 @@ export class TypeTable {
812842 let worklist = this . buildTypeWorklist ;
813843 let typeExtractionState = this . typeExtractionState ;
814844 while ( worklist . length > 0 ) {
815- let [ type , id ] = worklist . pop ( ) ;
845+ let [ type , id , unfoldAlias ] = worklist . pop ( ) ;
816846 let isShallowContext = typeExtractionState [ id ] === TypeExtractionState . PendingShallow ;
817847 if ( isShallowContext && ! isTypeAlwaysSafeToExpand ( type ) ) {
818848 typeExtractionState [ id ] = TypeExtractionState . DoneShallow ;
849+ } else if ( type . aliasSymbol != null && ! unfoldAlias ) {
850+ typeExtractionState [ id ] = TypeExtractionState . DoneFull ;
851+ let underlyingTypeId = this . getId ( type , true ) ;
852+ if ( underlyingTypeId != null ) {
853+ this . typeAliases . aliasTypes . push ( id ) ;
854+ this . typeAliases . underlyingTypes . push ( underlyingTypeId ) ;
855+ }
819856 } else {
820857 typeExtractionState [ id ] = TypeExtractionState . DoneFull ;
821858 this . isInShallowTypeContext = isShallowContext || this . isExpansiveTypeReference ( type ) ;
@@ -847,7 +884,7 @@ export class TypeTable {
847884 for ( let symbol of props ) {
848885 let propertyType = this . typeChecker . getTypeOfSymbolAtLocation ( symbol , this . arbitraryAstNode ) ;
849886 if ( propertyType == null ) continue ;
850- let propertyTypeId = this . getId ( propertyType ) ;
887+ let propertyTypeId = this . getId ( propertyType , false ) ;
851888 if ( propertyTypeId == null ) continue ;
852889 this . propertyLookups . baseTypes . push ( id ) ;
853890 this . propertyLookups . names . push ( symbol . name ) ;
@@ -879,7 +916,7 @@ export class TypeTable {
879916 /**
880917 * Returns a unique string for the given call/constructor signature.
881918 */
882- private getSignatureString ( kind : ts . SignatureKind , signature : ts . Signature ) : string {
919+ private getSignatureString ( kind : ts . SignatureKind , signature : AugmentedSignature ) : string {
883920 let parameters = signature . getParameters ( ) ;
884921 let numberOfTypeParameters = signature . typeParameters == null
885922 ? 0
@@ -892,27 +929,51 @@ export class TypeTable {
892929 break ;
893930 }
894931 }
895- let returnTypeId = this . getId ( signature . getReturnType ( ) ) ;
932+ let hasRestParam = ( signature . flags & InternalSignatureFlags . HasRestParameter ) !== 0 ;
933+ let restParameterTag = '' ;
934+ if ( hasRestParam ) {
935+ if ( requiredParameters === parameters . length ) {
936+ // Do not count the rest parameter as a required parameter
937+ requiredParameters = parameters . length - 1 ;
938+ }
939+ if ( parameters . length === 0 ) return null ;
940+ let restParameter = parameters [ parameters . length - 1 ] ;
941+ let restParameterType = this . typeChecker . getTypeOfSymbolAtLocation ( restParameter , this . arbitraryAstNode ) ;
942+ if ( restParameterType == null ) return null ;
943+ let restParameterTypeId = this . getId ( restParameterType , false ) ;
944+ if ( restParameterTypeId == null ) return null ;
945+ restParameterTag = '' + restParameterTypeId ;
946+ }
947+ let returnTypeId = this . getId ( signature . getReturnType ( ) , false ) ;
896948 if ( returnTypeId == null ) {
897949 return null ;
898950 }
899- let tag = `${ kind } ;${ numberOfTypeParameters } ;${ requiredParameters } ;${ returnTypeId } ` ;
951+ let tag = `${ kind } ;${ numberOfTypeParameters } ;${ requiredParameters } ;${ restParameterTag } ; ${ returnTypeId } ` ;
900952 for ( let typeParameter of signature . typeParameters || [ ] ) {
901953 tag += ";" + typeParameter . symbol . name ;
902954 let constraint = typeParameter . getConstraint ( ) ;
903955 let constraintId : number ;
904- if ( constraint == null || ( constraintId = this . getId ( constraint ) ) == null ) {
956+ if ( constraint == null || ( constraintId = this . getId ( constraint , false ) ) == null ) {
905957 tag += ";" ;
906958 } else {
907959 tag += ";" + constraintId ;
908960 }
909961 }
910- for ( let parameter of parameters ) {
962+ for ( let paramIndex = 0 ; paramIndex < parameters . length ; ++ paramIndex ) {
963+ let parameter = parameters [ paramIndex ] ;
911964 let parameterType = this . typeChecker . getTypeOfSymbolAtLocation ( parameter , this . arbitraryAstNode ) ;
912965 if ( parameterType == null ) {
913966 return null ;
914967 }
915- let parameterTypeId = this . getId ( parameterType ) ;
968+ let isRestParameter = hasRestParam && ( paramIndex === parameters . length - 1 ) ;
969+ if ( isRestParameter ) {
970+ // The type of the rest parameter is the array type, but we wish to extract the non-array type.
971+ if ( ! isTypeReference ( parameterType ) ) return null ;
972+ let typeArguments = parameterType . typeArguments ;
973+ if ( typeArguments == null || typeArguments . length === 0 ) return null ;
974+ parameterType = typeArguments [ 0 ] ;
975+ }
976+ let parameterTypeId = this . getId ( parameterType , false ) ;
916977 if ( parameterTypeId == null ) {
917978 return null ;
918979 }
@@ -946,7 +1007,7 @@ export class TypeTable {
9461007
9471008 private extractIndexer ( baseType : number , indexType : ts . Type , table : IndexerTable ) {
9481009 if ( indexType == null ) return ;
949- let indexTypeId = this . getId ( indexType ) ;
1010+ let indexTypeId = this . getId ( indexType , false ) ;
9501011 if ( indexTypeId == null ) return ;
9511012 table . baseTypes . push ( baseType ) ;
9521013 table . propertyTypes . push ( indexTypeId ) ;
@@ -1017,7 +1078,7 @@ export class TypeTable {
10171078 let selfType = this . getSelfType ( type ) ;
10181079 if ( selfType != null ) {
10191080 this . checkExpansiveness ( selfType ) ;
1020- let id = this . getId ( selfType ) ;
1081+ let id = this . getId ( selfType , false ) ;
10211082 return this . expansiveTypes . get ( id ) ;
10221083 }
10231084 return false ;
@@ -1086,7 +1147,7 @@ export class TypeTable {
10861147 search ( type , 0 ) ;
10871148
10881149 function search ( type : ts . TypeReference , expansionDepth : number ) : number | null {
1089- let id = typeTable . getId ( type ) ;
1150+ let id = typeTable . getId ( type , false ) ;
10901151 if ( id == null ) return null ;
10911152
10921153 let index = indexTable . get ( id ) ;
0 commit comments