@@ -433,6 +433,53 @@ namespace ts.codefix {
433
433
inferredTypes : Type [ ] | undefined ;
434
434
}
435
435
436
+ function createEmptyUsage ( ) : Usage {
437
+ return {
438
+ isNumber : undefined ,
439
+ isString : undefined ,
440
+ isNumberOrString : undefined ,
441
+ candidateTypes : undefined ,
442
+ properties : undefined ,
443
+ calls : undefined ,
444
+ constructs : undefined ,
445
+ numberIndex : undefined ,
446
+ stringIndex : undefined ,
447
+ candidateThisTypes : undefined ,
448
+ inferredTypes : undefined ,
449
+ } ;
450
+ }
451
+
452
+ function combineUsages ( usages : Usage [ ] ) : Usage {
453
+ const combinedProperties = createUnderscoreEscapedMap < Usage [ ] > ( ) ;
454
+ for ( const u of usages ) {
455
+ if ( u . properties ) {
456
+ u . properties . forEach ( ( p , name ) => {
457
+ if ( ! combinedProperties . has ( name ) ) {
458
+ combinedProperties . set ( name , [ ] ) ;
459
+ }
460
+ combinedProperties . get ( name ) ! . push ( p ) ;
461
+ } ) ;
462
+ }
463
+ }
464
+ const properties = createUnderscoreEscapedMap < Usage > ( ) ;
465
+ combinedProperties . forEach ( ( ps , name ) => {
466
+ properties . set ( name , combineUsages ( ps ) ) ;
467
+ } ) ;
468
+ return {
469
+ isNumber : usages . some ( u => u . isNumber ) ,
470
+ isString : usages . some ( u => u . isString ) ,
471
+ isNumberOrString : usages . some ( u => u . isNumberOrString ) ,
472
+ candidateTypes : flatMap ( usages , u => u . candidateTypes ) as Type [ ] ,
473
+ properties,
474
+ calls : flatMap ( usages , u => u . calls ) as CallUsage [ ] ,
475
+ constructs : flatMap ( usages , u => u . constructs ) as CallUsage [ ] ,
476
+ numberIndex : forEach ( usages , u => u . numberIndex ) ,
477
+ stringIndex : forEach ( usages , u => u . stringIndex ) ,
478
+ candidateThisTypes : flatMap ( usages , u => u . candidateThisTypes ) as Type [ ] ,
479
+ inferredTypes : undefined , // clear type cache
480
+ } ;
481
+ }
482
+
436
483
function single ( ) : Type {
437
484
return unifyTypes ( inferTypesFromReferencesSingle ( references ) ) ;
438
485
}
@@ -767,6 +814,10 @@ namespace ts.codefix {
767
814
return inferences . filter ( i => toRemove . every ( f => ! f ( i ) ) ) ;
768
815
}
769
816
817
+ function unifyFromUsage ( usage : Usage ) {
818
+ return unifyTypes ( inferFromUsage ( usage ) ) ;
819
+ }
820
+
770
821
function unifyTypes ( inferences : ReadonlyArray < Type > ) : Type {
771
822
if ( ! inferences . length ) return checker . getAnyType ( ) ;
772
823
@@ -837,7 +888,7 @@ namespace ts.codefix {
837
888
numberIndices . length ? checker . createIndexInfo ( checker . getUnionType ( numberIndices ) , numberIndexReadonly ) : undefined ) ;
838
889
}
839
890
840
- function inferFromUsage ( usage : Usage ) {
891
+ function inferFromUsage ( usage : Usage ) : Type [ ] {
841
892
const types = [ ] ;
842
893
843
894
if ( usage . isNumber ) {
@@ -851,12 +902,20 @@ namespace ts.codefix {
851
902
}
852
903
853
904
types . push ( ...( usage . candidateTypes || [ ] ) . map ( t => checker . getBaseTypeOfLiteralType ( t ) ) ) ;
854
- types . push ( ...findBuiltinTypes ( usage ) ) ;
905
+ types . push ( ...inferNamedTypesFromProperties ( usage ) ) ;
855
906
856
907
if ( usage . numberIndex ) {
857
- types . push ( checker . createArrayType ( recur ( usage . numberIndex ) ) ) ;
908
+ types . push ( checker . createArrayType ( unifyFromUsage ( usage . numberIndex ) ) ) ;
909
+ }
910
+ const structural = inferStructuralType ( usage ) ;
911
+ if ( structural ) {
912
+ types . push ( structural ) ;
858
913
}
859
- else if ( usage . properties && usage . properties . size
914
+ return types ;
915
+ }
916
+
917
+ function inferStructuralType ( usage : Usage ) {
918
+ if ( usage . properties && usage . properties . size
860
919
|| usage . calls && usage . calls . length
861
920
|| usage . constructs && usage . constructs . length
862
921
|| usage . stringIndex ) {
@@ -868,7 +927,7 @@ namespace ts.codefix {
868
927
if ( usage . properties ) {
869
928
usage . properties . forEach ( ( u , name ) => {
870
929
const symbol = checker . createSymbol ( SymbolFlags . Property , name ) ;
871
- symbol . type = recur ( u ) ;
930
+ symbol . type = unifyFromUsage ( u ) ;
872
931
members . set ( name , symbol ) ;
873
932
} ) ;
874
933
}
@@ -882,75 +941,23 @@ namespace ts.codefix {
882
941
}
883
942
884
943
if ( usage . stringIndex ) {
885
- stringIndexInfo = checker . createIndexInfo ( recur ( usage . stringIndex ) , /*isReadonly*/ false ) ;
944
+ stringIndexInfo = checker . createIndexInfo ( unifyFromUsage ( usage . stringIndex ) , /*isReadonly*/ false ) ;
886
945
}
887
946
888
- types . push ( checker . createAnonymousType ( /*symbol*/ undefined ! , members , callSignatures , constructSignatures , stringIndexInfo , /*numberIndexInfo*/ undefined ) ) ; // TODO: GH#18217
889
- }
890
- return types ; // TODO: Should cache this since I HOPE it doesn't change
891
-
892
- function recur ( innerUsage : Usage ) : Type {
893
- return unifyTypes ( inferFromUsage ( innerUsage ) ) ;
947
+ return checker . createAnonymousType ( /*symbol*/ undefined ! , members , callSignatures , constructSignatures , stringIndexInfo , /*numberIndexInfo*/ undefined ) ; // TODO: GH#18217
894
948
}
895
949
}
896
950
897
- function createEmptyUsage ( ) : Usage {
898
- return {
899
- isNumber : undefined ,
900
- isString : undefined ,
901
- isNumberOrString : undefined ,
902
- candidateTypes : undefined ,
903
- properties : undefined ,
904
- calls : undefined ,
905
- constructs : undefined ,
906
- numberIndex : undefined ,
907
- stringIndex : undefined ,
908
- candidateThisTypes : undefined ,
909
- inferredTypes : undefined ,
910
- }
911
- }
912
-
913
- function combineUsages ( usages : Usage [ ] ) : Usage {
914
- const combinedProperties = createUnderscoreEscapedMap < Usage [ ] > ( )
915
- for ( const u of usages ) {
916
- if ( u . properties ) {
917
- u . properties . forEach ( ( p , name ) => {
918
- if ( ! combinedProperties . has ( name ) ) {
919
- combinedProperties . set ( name , [ ] ) ;
920
- }
921
- combinedProperties . get ( name ) ! . push ( p ) ;
922
- } ) ;
923
- }
924
- }
925
- const properties = createUnderscoreEscapedMap < Usage > ( )
926
- combinedProperties . forEach ( ( ps , name ) => {
927
- properties . set ( name , combineUsages ( ps ) ) ;
928
- } ) ;
929
- return {
930
- isNumber : usages . some ( u => u . isNumber ) ,
931
- isString : usages . some ( u => u . isString ) ,
932
- isNumberOrString : usages . some ( u => u . isNumberOrString ) ,
933
- candidateTypes : flatMap ( usages , u => u . candidateTypes ) as Type [ ] ,
934
- properties,
935
- calls : flatMap ( usages , u => u . calls ) as CallUsage [ ] ,
936
- constructs : flatMap ( usages , u => u . constructs ) as CallUsage [ ] ,
937
- numberIndex : forEach ( usages , u => u . numberIndex ) ,
938
- stringIndex : forEach ( usages , u => u . stringIndex ) ,
939
- candidateThisTypes : flatMap ( usages , u => u . candidateThisTypes ) as Type [ ] ,
940
- inferredTypes : undefined , // clear type cache
941
- }
942
- }
943
-
944
- function findBuiltinTypes ( usage : Usage ) : Type [ ] {
951
+ function inferNamedTypesFromProperties ( usage : Usage ) : Type [ ] {
945
952
if ( ! usage . properties || ! usage . properties . size ) return [ ] ;
946
- const matches = builtins . filter ( t => matchesAllPropertiesOf ( t , usage ) ) ;
953
+ const matches = builtins . filter ( t => allPropertiesAreAssignableToUsage ( t , usage ) ) ;
947
954
if ( 0 < matches . length && matches . length < 3 ) {
948
- return matches . map ( m => inferTypeParameterFromUsage ( m , usage ) ) ;
955
+ return matches . map ( m => inferInstantiationFromUsage ( m , usage ) ) ;
949
956
}
950
957
return [ ] ;
951
958
}
952
959
953
- function matchesAllPropertiesOf ( type : Type , usage : Usage ) {
960
+ function allPropertiesAreAssignableToUsage ( type : Type , usage : Usage ) {
954
961
if ( ! usage . properties ) return false ;
955
962
let result = true ;
956
963
usage . properties . forEach ( ( propUsage , name ) => {
@@ -960,34 +967,34 @@ namespace ts.codefix {
960
967
return ;
961
968
}
962
969
if ( propUsage . calls ) {
963
- const sigs = checker . getSignaturesOfType ( source , ts . SignatureKind . Call ) ;
970
+ const sigs = checker . getSignaturesOfType ( source , SignatureKind . Call ) ;
964
971
result = result && ! ! sigs . length && checker . isTypeAssignableTo ( source , getFunctionFromCalls ( propUsage . calls ) ) ;
965
972
}
966
973
else {
967
- result = result && checker . isTypeAssignableTo ( source , unifyTypes ( inferFromUsage ( propUsage ) ) ) ;
974
+ result = result && checker . isTypeAssignableTo ( source , unifyFromUsage ( propUsage ) ) ;
968
975
}
969
976
} ) ;
970
977
return result ;
971
978
}
972
979
973
- // inference is limited to
974
- // 1. generic types with a single parameter
975
- // 2. inference to/from calls with a single signature
976
- function inferTypeParameterFromUsage ( type : Type , usage : Usage ) {
977
- if ( ! usage . properties || ! ( getObjectFlags ( type ) & ObjectFlags . Reference ) ) return type ;
980
+ /**
981
+ * inference is limited to
982
+ * 1. generic types with a single parameter
983
+ * 2. inference to/from calls with a single signature
984
+ */
985
+ function inferInstantiationFromUsage ( type : Type , usage : Usage ) {
986
+ if ( ! ( getObjectFlags ( type ) & ObjectFlags . Reference ) || ! usage . properties ) {
987
+ return type ;
988
+ }
978
989
const generic = ( type as TypeReference ) . target ;
979
990
const singleTypeParameter = singleOrUndefined ( generic . typeParameters ) ;
980
991
if ( ! singleTypeParameter ) return type ;
981
992
982
993
const types : Type [ ] = [ ] ;
983
994
usage . properties . forEach ( ( propUsage , name ) => {
984
- const source = checker . getTypeOfPropertyOfType ( generic , name as string ) ;
985
- if ( ! source ) {
986
- return Debug . fail ( "generic should have all the properties of its reference." ) ;
987
- }
988
- if ( ! propUsage . calls ) return ;
989
-
990
- types . push ( ...infer ( source , getFunctionFromCalls ( propUsage . calls ) , singleTypeParameter ) ) ;
995
+ const genericPropertyType = checker . getTypeOfPropertyOfType ( generic , name as string ) ;
996
+ Debug . assert ( ! ! genericPropertyType , "generic should have all the properties of its reference." ) ;
997
+ types . push ( ...infer ( genericPropertyType ! , unifyFromUsage ( propUsage ) , singleTypeParameter ) ) ;
991
998
} ) ;
992
999
return builtinConstructors [ type . symbol . escapedName as string ] ( unifyTypes ( types ) ) ;
993
1000
}
@@ -1033,7 +1040,7 @@ namespace ts.codefix {
1033
1040
break ;
1034
1041
}
1035
1042
let sourceType = checker . getTypeOfSymbolAtLocation ( sourceParam , sourceParam . valueDeclaration ) ;
1036
- let elementType = isRest && checker . getElementTypeOfArrayType ( sourceType ) ;
1043
+ const elementType = isRest && checker . getElementTypeOfArrayType ( sourceType ) ;
1037
1044
if ( elementType ) {
1038
1045
sourceType = elementType ;
1039
1046
}
@@ -1047,7 +1054,7 @@ namespace ts.codefix {
1047
1054
}
1048
1055
1049
1056
function getFunctionFromCalls ( calls : CallUsage [ ] ) {
1050
- return checker . createAnonymousType ( undefined ! , createSymbolTable ( ) , [ getSignatureFromCalls ( calls ) ] , emptyArray , undefined , undefined ) ;
1057
+ return checker . createAnonymousType ( undefined ! , createSymbolTable ( ) , [ getSignatureFromCalls ( calls ) ] , emptyArray , /*stringIndexInfo*/ undefined , /*numberIndexInfo*/ undefined ) ;
1051
1058
}
1052
1059
1053
1060
function getSignatureFromCalls ( calls : CallUsage [ ] ) : Signature {
@@ -1061,7 +1068,7 @@ namespace ts.codefix {
1061
1068
}
1062
1069
parameters . push ( symbol ) ;
1063
1070
}
1064
- const returnType = unifyTypes ( inferFromUsage ( combineUsages ( calls . map ( call => call . return_ ) ) ) ) ;
1071
+ const returnType = unifyFromUsage ( combineUsages ( calls . map ( call => call . return_ ) ) ) ;
1065
1072
// TODO: GH#18217
1066
1073
return checker . createSignature ( /*declaration*/ undefined ! , /*typeParameters*/ undefined , /*thisParameter*/ undefined , parameters , returnType , /*typePredicate*/ undefined , length , /*hasRestParameter*/ false , /*hasLiteralTypes*/ false ) ;
1067
1074
}
0 commit comments