@@ -405,18 +405,19 @@ namespace ts.codefix {
405405 }
406406
407407 interface Usage {
408- isNumber ? : boolean ;
409- isString ? : boolean ;
408+ isNumber : boolean | undefined ;
409+ isString : boolean | undefined ;
410410 /** Used ambiguously, eg x + ___ or object[___]; results in string | number if no other evidence exists */
411- isNumberOrString ?: boolean ;
412-
413- candidateTypes ?: Type [ ] ;
414- properties ?: UnderscoreEscapedMap < Usage > ;
415- calls ?: CallUsage [ ] ;
416- constructs ?: CallUsage [ ] ;
417- numberIndex ?: Usage ;
418- stringIndex ?: Usage ;
419- candidateThisTypes ?: Type [ ] ;
411+ isNumberOrString : boolean | undefined ;
412+
413+ candidateTypes : Type [ ] | undefined ;
414+ properties : UnderscoreEscapedMap < Usage > | undefined ;
415+ calls : CallUsage [ ] | undefined ;
416+ constructs : CallUsage [ ] | undefined ;
417+ numberIndex : Usage | undefined ;
418+ stringIndex : Usage | undefined ;
419+ candidateThisTypes : Type [ ] | undefined ;
420+ inferredTypes : Type [ ] | undefined ;
420421 }
421422
422423 function single ( ) : Type {
@@ -428,7 +429,7 @@ namespace ts.codefix {
428429 return undefined ;
429430 }
430431
431- const usage : Usage = { } ;
432+ const usage = createEmptyUsage ( ) ;
432433 for ( const reference of references ) {
433434 cancellationToken . throwIfCancellationRequested ( ) ;
434435 calculateUsageOfNode ( reference , usage ) ;
@@ -466,7 +467,7 @@ namespace ts.codefix {
466467 }
467468
468469 function thisParameter ( ) {
469- const usage : Usage = { } ;
470+ const usage = createEmptyUsage ( ) ;
470471 for ( const reference of references ) {
471472 cancellationToken . throwIfCancellationRequested ( ) ;
472473 calculateUsageOfNode ( reference , usage ) ;
@@ -476,7 +477,7 @@ namespace ts.codefix {
476477 }
477478
478479 function inferTypesFromReferencesSingle ( references : readonly Identifier [ ] ) : Type [ ] {
479- const usage : Usage = { } ;
480+ const usage : Usage = createEmptyUsage ( ) ;
480481 for ( const reference of references ) {
481482 cancellationToken . throwIfCancellationRequested ( ) ;
482483 calculateUsageOfNode ( reference , usage ) ;
@@ -671,7 +672,7 @@ namespace ts.codefix {
671672 function inferTypeFromCallExpression ( parent : CallExpression | NewExpression , usage : Usage ) : void {
672673 const call : CallUsage = {
673674 argumentTypes : [ ] ,
674- return_ : { }
675+ return_ : createEmptyUsage ( )
675676 } ;
676677
677678 if ( parent . arguments ) {
@@ -695,7 +696,7 @@ namespace ts.codefix {
695696 if ( ! usage . properties ) {
696697 usage . properties = createUnderscoreEscapedMap < Usage > ( ) ;
697698 }
698- const propertyUsage = usage . properties . get ( name ) || { } ;
699+ const propertyUsage = usage . properties . get ( name ) || createEmptyUsage ( ) ;
699700 calculateUsageOfNode ( parent , propertyUsage ) ;
700701 usage . properties . set ( name , propertyUsage ) ;
701702 }
@@ -707,7 +708,7 @@ namespace ts.codefix {
707708 }
708709 else {
709710 const indexType = checker . getTypeAtLocation ( parent . argumentExpression ) ;
710- const indexUsage = { } ;
711+ const indexUsage = createEmptyUsage ( ) ;
711712 calculateUsageOfNode ( parent , indexUsage ) ;
712713 if ( indexType . flags & TypeFlags . NumberLike ) {
713714 usage . numberIndex = indexUsage ;
@@ -845,7 +846,10 @@ namespace ts.codefix {
845846 if ( usage . numberIndex ) {
846847 types . push ( checker . createArrayType ( recur ( usage . numberIndex ) ) ) ;
847848 }
848- else if ( usage . properties || usage . calls || usage . constructs || usage . stringIndex ) {
849+ else if ( usage . properties && usage . properties . size
850+ || usage . calls && usage . calls . length
851+ || usage . constructs && usage . constructs . length
852+ || usage . stringIndex ) {
849853 const members = createUnderscoreEscapedMap < Symbol > ( ) ;
850854 const callSignatures : Signature [ ] = [ ] ;
851855 const constructSignatures : Signature [ ] = [ ] ;
@@ -873,25 +877,57 @@ namespace ts.codefix {
873877
874878 types . push ( checker . createAnonymousType ( /*symbol*/ undefined ! , members , callSignatures , constructSignatures , stringIndexInfo , /*numberIndexInfo*/ undefined ) ) ; // TODO: GH#18217
875879 }
876- return types ;
880+ return types ; // TODO: Should cache this since I HOPE it doesn't change
877881
878882 function recur ( innerUsage : Usage ) : Type {
879883 return unifyFromUsage ( inferFromUsage ( innerUsage ) ) ;
880884 }
881885 }
882886
887+ function createEmptyUsage ( ) : Usage {
888+ return {
889+ isNumber : undefined ,
890+ isString : undefined ,
891+ isNumberOrString : undefined ,
892+ candidateTypes : undefined ,
893+ properties : undefined ,
894+ calls : undefined ,
895+ constructs : undefined ,
896+ numberIndex : undefined ,
897+ stringIndex : undefined ,
898+ candidateThisTypes : undefined ,
899+ inferredTypes : undefined ,
900+ }
901+ }
902+
883903 function combineUsages ( usages : Usage [ ] ) : Usage {
904+ const combinedProperties = createUnderscoreEscapedMap < Usage [ ] > ( )
905+ for ( const u of usages ) {
906+ if ( u . properties ) {
907+ u . properties . forEach ( ( p , name ) => {
908+ if ( ! combinedProperties . has ( name ) ) {
909+ combinedProperties . set ( name , [ ] ) ;
910+ }
911+ combinedProperties . get ( name ) ! . push ( p ) ;
912+ } ) ;
913+ }
914+ }
915+ const properties = createUnderscoreEscapedMap < Usage > ( )
916+ combinedProperties . forEach ( ( ps , name ) => {
917+ properties . set ( name , combineUsages ( ps ) ) ;
918+ } ) ;
884919 return {
885920 isNumber : usages . some ( u => u . isNumber ) ,
886921 isString : usages . some ( u => u . isString ) ,
887922 isNumberOrString : usages . some ( u => u . isNumberOrString ) ,
888923 candidateTypes : flatMap ( usages , u => u . candidateTypes ) as Type [ ] ,
889- properties : undefined , // TODO
924+ properties,
890925 calls : flatMap ( usages , u => u . calls ) as CallUsage [ ] ,
891926 constructs : flatMap ( usages , u => u . constructs ) as CallUsage [ ] ,
892927 numberIndex : forEach ( usages , u => u . numberIndex ) ,
893928 stringIndex : forEach ( usages , u => u . stringIndex ) ,
894929 candidateThisTypes : flatMap ( usages , u => u . candidateThisTypes ) as Type [ ] ,
930+ inferredTypes : undefined , // clear type cache
895931 }
896932 }
897933
@@ -901,7 +937,7 @@ namespace ts.codefix {
901937 checker . getNumberType ( ) ,
902938 checker . createArrayType ( checker . getAnyType ( ) ) ,
903939 checker . createPromiseType ( checker . getAnyType ( ) ) ,
904- // checker.getFunctionType() // not sure what this was supposed to be good for.
940+ // checker.getFunctionType() // TODO: not sure what this was supposed to be good for.
905941 ] ;
906942 const matches = builtins . filter ( t => matchesAllPropertiesOf ( t , usage ) ) ;
907943 if ( false && 0 < matches . length && matches . length < 3 ) {
0 commit comments