@@ -405,18 +405,19 @@ namespace ts.codefix {
405
405
}
406
406
407
407
interface Usage {
408
- isNumber ? : boolean ;
409
- isString ? : boolean ;
408
+ isNumber : boolean | undefined ;
409
+ isString : boolean | undefined ;
410
410
/** 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 ;
420
421
}
421
422
422
423
function single ( ) : Type {
@@ -428,7 +429,7 @@ namespace ts.codefix {
428
429
return undefined ;
429
430
}
430
431
431
- const usage : Usage = { } ;
432
+ const usage = createEmptyUsage ( ) ;
432
433
for ( const reference of references ) {
433
434
cancellationToken . throwIfCancellationRequested ( ) ;
434
435
calculateUsageOfNode ( reference , usage ) ;
@@ -466,7 +467,7 @@ namespace ts.codefix {
466
467
}
467
468
468
469
function thisParameter ( ) {
469
- const usage : Usage = { } ;
470
+ const usage = createEmptyUsage ( ) ;
470
471
for ( const reference of references ) {
471
472
cancellationToken . throwIfCancellationRequested ( ) ;
472
473
calculateUsageOfNode ( reference , usage ) ;
@@ -476,7 +477,7 @@ namespace ts.codefix {
476
477
}
477
478
478
479
function inferTypesFromReferencesSingle ( references : readonly Identifier [ ] ) : Type [ ] {
479
- const usage : Usage = { } ;
480
+ const usage : Usage = createEmptyUsage ( ) ;
480
481
for ( const reference of references ) {
481
482
cancellationToken . throwIfCancellationRequested ( ) ;
482
483
calculateUsageOfNode ( reference , usage ) ;
@@ -671,7 +672,7 @@ namespace ts.codefix {
671
672
function inferTypeFromCallExpression ( parent : CallExpression | NewExpression , usage : Usage ) : void {
672
673
const call : CallUsage = {
673
674
argumentTypes : [ ] ,
674
- return_ : { }
675
+ return_ : createEmptyUsage ( )
675
676
} ;
676
677
677
678
if ( parent . arguments ) {
@@ -695,7 +696,7 @@ namespace ts.codefix {
695
696
if ( ! usage . properties ) {
696
697
usage . properties = createUnderscoreEscapedMap < Usage > ( ) ;
697
698
}
698
- const propertyUsage = usage . properties . get ( name ) || { } ;
699
+ const propertyUsage = usage . properties . get ( name ) || createEmptyUsage ( ) ;
699
700
calculateUsageOfNode ( parent , propertyUsage ) ;
700
701
usage . properties . set ( name , propertyUsage ) ;
701
702
}
@@ -707,7 +708,7 @@ namespace ts.codefix {
707
708
}
708
709
else {
709
710
const indexType = checker . getTypeAtLocation ( parent . argumentExpression ) ;
710
- const indexUsage = { } ;
711
+ const indexUsage = createEmptyUsage ( ) ;
711
712
calculateUsageOfNode ( parent , indexUsage ) ;
712
713
if ( indexType . flags & TypeFlags . NumberLike ) {
713
714
usage . numberIndex = indexUsage ;
@@ -845,7 +846,10 @@ namespace ts.codefix {
845
846
if ( usage . numberIndex ) {
846
847
types . push ( checker . createArrayType ( recur ( usage . numberIndex ) ) ) ;
847
848
}
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 ) {
849
853
const members = createUnderscoreEscapedMap < Symbol > ( ) ;
850
854
const callSignatures : Signature [ ] = [ ] ;
851
855
const constructSignatures : Signature [ ] = [ ] ;
@@ -873,25 +877,57 @@ namespace ts.codefix {
873
877
874
878
types . push ( checker . createAnonymousType ( /*symbol*/ undefined ! , members , callSignatures , constructSignatures , stringIndexInfo , /*numberIndexInfo*/ undefined ) ) ; // TODO: GH#18217
875
879
}
876
- return types ;
880
+ return types ; // TODO: Should cache this since I HOPE it doesn't change
877
881
878
882
function recur ( innerUsage : Usage ) : Type {
879
883
return unifyFromUsage ( inferFromUsage ( innerUsage ) ) ;
880
884
}
881
885
}
882
886
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
+
883
903
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
+ } ) ;
884
919
return {
885
920
isNumber : usages . some ( u => u . isNumber ) ,
886
921
isString : usages . some ( u => u . isString ) ,
887
922
isNumberOrString : usages . some ( u => u . isNumberOrString ) ,
888
923
candidateTypes : flatMap ( usages , u => u . candidateTypes ) as Type [ ] ,
889
- properties : undefined , // TODO
924
+ properties,
890
925
calls : flatMap ( usages , u => u . calls ) as CallUsage [ ] ,
891
926
constructs : flatMap ( usages , u => u . constructs ) as CallUsage [ ] ,
892
927
numberIndex : forEach ( usages , u => u . numberIndex ) ,
893
928
stringIndex : forEach ( usages , u => u . stringIndex ) ,
894
929
candidateThisTypes : flatMap ( usages , u => u . candidateThisTypes ) as Type [ ] ,
930
+ inferredTypes : undefined , // clear type cache
895
931
}
896
932
}
897
933
@@ -901,7 +937,7 @@ namespace ts.codefix {
901
937
checker . getNumberType ( ) ,
902
938
checker . createArrayType ( checker . getAnyType ( ) ) ,
903
939
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.
905
941
] ;
906
942
const matches = builtins . filter ( t => matchesAllPropertiesOf ( t , usage ) ) ;
907
943
if ( false && 0 < matches . length && matches . length < 3 ) {
0 commit comments