@@ -421,7 +421,7 @@ namespace ts.codefix {
421
421
}
422
422
423
423
function single ( ) : Type {
424
- return unifyFromUsage ( inferTypesFromReferencesSingle ( references ) ) ;
424
+ return unifyTypes ( inferTypesFromReferencesSingle ( references ) ) ;
425
425
}
426
426
427
427
function parameters ( declaration : FunctionLike ) : ParameterInference [ ] | undefined {
@@ -457,7 +457,7 @@ namespace ts.codefix {
457
457
const inferred = inferTypesFromReferencesSingle ( getReferences ( parameter . name , program , cancellationToken ) ) ;
458
458
types . push ( ...( isRest ? mapDefined ( inferred , checker . getElementTypeOfArrayType ) : inferred ) ) ;
459
459
}
460
- const type = unifyFromUsage ( types ) ;
460
+ const type = unifyTypes ( types ) ;
461
461
return {
462
462
type : isRest ? checker . createArrayType ( type ) : type ,
463
463
isOptional : isOptional && ! isRest ,
@@ -473,7 +473,7 @@ namespace ts.codefix {
473
473
calculateUsageOfNode ( reference , usage ) ;
474
474
}
475
475
476
- return unifyFromUsage ( usage . candidateThisTypes || emptyArray ) ;
476
+ return unifyTypes ( usage . candidateThisTypes || emptyArray ) ;
477
477
}
478
478
479
479
function inferTypesFromReferencesSingle ( references : readonly Identifier [ ] ) : Type [ ] {
@@ -627,6 +627,9 @@ namespace ts.codefix {
627
627
else if ( otherOperandType . flags & TypeFlags . StringLike ) {
628
628
usage . isString = true ;
629
629
}
630
+ else if ( otherOperandType . flags & TypeFlags . Any ) {
631
+ // do nothing, maybe we'll learn something elsewhere
632
+ }
630
633
else {
631
634
usage . isNumberOrString = true ;
632
635
}
@@ -748,7 +751,7 @@ namespace ts.codefix {
748
751
return inferences . filter ( i => toRemove . every ( f => ! f ( i ) ) ) ;
749
752
}
750
753
751
- function unifyFromUsage ( inferences : ReadonlyArray < Type > , fallback = checker . getAnyType ( ) ) : Type {
754
+ function unifyTypes ( inferences : ReadonlyArray < Type > , fallback = checker . getAnyType ( ) ) : Type {
752
755
if ( ! inferences . length ) return fallback ;
753
756
754
757
// 1. string or number individually override string | number
@@ -774,7 +777,7 @@ namespace ts.codefix {
774
777
good = good . filter ( i => ! ( checker . getObjectFlags ( i ) & ObjectFlags . Anonymous ) ) ;
775
778
good . push ( unifyAnonymousTypes ( anons ) ) ;
776
779
}
777
- return checker . getWidenedType ( checker . getUnionType ( good ) ) ;
780
+ return checker . getWidenedType ( checker . getUnionType ( good , UnionReduction . Subtype ) ) ;
778
781
}
779
782
780
783
function unifyAnonymousTypes ( anons : AnonymousType [ ] ) {
@@ -832,16 +835,7 @@ namespace ts.codefix {
832
835
}
833
836
834
837
types . push ( ...( usage . candidateTypes || [ ] ) . map ( t => checker . getBaseTypeOfLiteralType ( t ) ) ) ;
835
- types . push ( ...findBuiltinType ( usage ) ) ;
836
-
837
- if ( usage . properties && hasCalls ( usage . properties . get ( "then" as __String ) ) ) {
838
- const paramType = getParameterTypeFromCalls ( 0 , usage . properties . get ( "then" as __String ) ! . calls ! , /*isRestParameter*/ false ) ! ; // TODO: GH#18217
839
- const types = paramType . getCallSignatures ( ) . map ( sig => sig . getReturnType ( ) ) ;
840
- types . push ( checker . createPromiseType ( types . length ? checker . getUnionType ( types , UnionReduction . Subtype ) : checker . getAnyType ( ) ) ) ;
841
- }
842
- else if ( usage . properties && hasCalls ( usage . properties . get ( "push" as __String ) ) ) {
843
- types . push ( checker . createArrayType ( getParameterTypeFromCalls ( 0 , usage . properties . get ( "push" as __String ) ! . calls ! , /*isRestParameter*/ false ) ! ) ) ;
844
- }
838
+ types . push ( ...findBuiltinTypes ( usage ) ) ;
845
839
846
840
if ( usage . numberIndex ) {
847
841
types . push ( checker . createArrayType ( recur ( usage . numberIndex ) ) ) ;
@@ -864,11 +858,12 @@ namespace ts.codefix {
864
858
}
865
859
866
860
if ( usage . calls ) {
867
- callSignatures . push ( getSignatureFromCalls ( usage . calls ) ) ;
861
+ callSignatures . push ( getSignatureFromCalls ( usage . calls , checker . getVoidType ( ) ) ) ;
868
862
}
869
863
870
864
if ( usage . constructs ) {
871
- constructSignatures . push ( getSignatureFromCalls ( usage . constructs ) ) ;
865
+ // TODO: fallback return should maybe be {}?
866
+ constructSignatures . push ( getSignatureFromCalls ( usage . constructs , checker . getVoidType ( ) ) ) ;
872
867
}
873
868
874
869
if ( usage . stringIndex ) {
@@ -880,7 +875,7 @@ namespace ts.codefix {
880
875
return types ; // TODO: Should cache this since I HOPE it doesn't change
881
876
882
877
function recur ( innerUsage : Usage ) : Type {
883
- return unifyFromUsage ( inferFromUsage ( innerUsage ) ) ;
878
+ return unifyTypes ( inferFromUsage ( innerUsage ) ) ;
884
879
}
885
880
}
886
881
@@ -931,80 +926,88 @@ namespace ts.codefix {
931
926
}
932
927
}
933
928
934
- function findBuiltinType ( usage : Usage ) : Type [ ] {
929
+ function findBuiltinTypes ( usage : Usage ) : Type [ ] {
930
+ if ( ! usage . properties || ! usage . properties . size ) return [ ] ;
935
931
const builtins = [
936
932
checker . getStringType ( ) ,
937
933
checker . getNumberType ( ) ,
938
934
checker . createArrayType ( checker . getAnyType ( ) ) ,
939
935
checker . createPromiseType ( checker . getAnyType ( ) ) ,
940
936
// checker.getFunctionType() // TODO: not sure what this was supposed to be good for.
941
937
] ;
938
+ // TODO: Still need to infer type parameters
942
939
const matches = builtins . filter ( t => matchesAllPropertiesOf ( t , usage ) ) ;
943
- if ( false && 0 < matches . length && matches . length < 3 ) {
944
- return matches ;
940
+ if ( 0 < matches . length && matches . length < 3 ) {
941
+ return matches . map ( m => {
942
+ // special-case array and promise for now
943
+ if ( m === builtins [ 3 ] && hasCalls ( usage . properties ! . get ( "then" as __String ) ) ) {
944
+ const paramType = getParameterTypeFromCalls ( 0 , usage . properties ! . get ( "then" as __String ) ! . calls ! , /*isRestParameter*/ false ) ! ; // TODO: GH#18217
945
+ const returns = paramType . getCallSignatures ( ) . map ( sig => sig . getReturnType ( ) ) ;
946
+ return checker . createPromiseType ( returns . length ? checker . getUnionType ( returns , UnionReduction . Subtype ) : checker . getAnyType ( ) ) ;
947
+ }
948
+ else if ( m === builtins [ 2 ] && hasCalls ( usage . properties ! . get ( "push" as __String ) ) ) {
949
+ return checker . createArrayType ( getParameterTypeFromCalls ( 0 , usage . properties ! . get ( "push" as __String ) ! . calls ! , /*isRestParameter*/ false ) ! ) ;
950
+ }
951
+ return m ;
952
+ } ) ;
945
953
}
946
954
return [ ] ;
947
955
}
948
956
949
957
function matchesAllPropertiesOf ( type : Type , usage : Usage ) {
950
958
if ( ! usage . properties ) return false ;
951
959
let result = true ;
952
- usage . properties . forEach ( ( prop , name ) => {
953
- const source = checker . getUnionType ( inferFromUsage ( prop ) ) ;
954
- const target = checker . getTypeOfPropertyOfType ( type , name as string ) ;
955
- if ( target && prop . calls ) {
956
- const sigs = checker . getSignaturesOfType ( target , ts . SignatureKind . Call ) ;
957
- result = result && ! ! sigs . length && sigs . some (
958
- sig => checker . isTypeAssignableTo (
959
- getFunctionFromCalls ( prop . calls ! ) ,
960
- checker . createAnonymousType ( undefined ! , createSymbolTable ( ) , [ sig ] , emptyArray , undefined , undefined ) ) ) ;
960
+ usage . properties . forEach ( ( propUsage , name ) => {
961
+ const source = checker . getTypeOfPropertyOfType ( type , name as string ) ;
962
+ if ( ! source ) {
963
+ result = false ;
964
+ return ;
965
+ }
966
+ if ( propUsage . calls ) {
967
+ const sigs = checker . getSignaturesOfType ( source , ts . SignatureKind . Call ) ;
968
+ result = result && ! ! sigs . length && checker . isTypeAssignableTo ( source , getFunctionFromCalls ( propUsage . calls ) ) ;
961
969
}
962
970
else {
963
- result = result && ! ! source && ! ! target && checker . isTypeAssignableTo ( source , target ) ;
971
+ result = result && checker . isTypeAssignableTo ( source , unifyTypes ( inferFromUsage ( propUsage ) ) ) ;
964
972
}
965
973
} ) ;
966
974
return result ;
967
975
}
968
976
969
977
970
978
function getFunctionFromCalls ( calls : CallUsage [ ] ) {
971
- return checker . createAnonymousType ( undefined ! , createSymbolTable ( ) , [ getSignatureFromCalls ( calls ) ] , emptyArray , undefined , undefined ) ;
979
+ return checker . createAnonymousType ( undefined ! , createSymbolTable ( ) , [ getSignatureFromCalls ( calls , checker . getAnyType ( ) ) ] , emptyArray , undefined , undefined ) ;
972
980
}
973
981
974
982
function getParameterTypeFromCalls ( parameterIndex : number , calls : CallUsage [ ] , isRestParameter : boolean ) {
983
+ // TODO: This is largely redundant with getSignatureFromCalls, I think. (though it handles rest parameters correctly, so that needs to be integrated there)
975
984
let types : Type [ ] = [ ] ;
976
- if ( calls ) {
977
- for ( const call of calls ) {
978
- if ( call . argumentTypes . length > parameterIndex ) {
979
- if ( isRestParameter ) {
980
- types = concatenate ( types , map ( call . argumentTypes . slice ( parameterIndex ) , a => checker . getBaseTypeOfLiteralType ( a ) ) ) ;
981
- }
982
- else {
983
- types . push ( checker . getBaseTypeOfLiteralType ( call . argumentTypes [ parameterIndex ] ) ) ;
984
- }
985
+ for ( const call of calls ) {
986
+ if ( call . argumentTypes . length > parameterIndex ) {
987
+ if ( isRestParameter ) {
988
+ types = concatenate ( types , map ( call . argumentTypes . slice ( parameterIndex ) , a => checker . getBaseTypeOfLiteralType ( a ) ) ) ;
989
+ }
990
+ else {
991
+ types . push ( checker . getBaseTypeOfLiteralType ( call . argumentTypes [ parameterIndex ] ) ) ;
985
992
}
986
993
}
987
994
}
988
-
989
- if ( types . length ) {
990
- const type = checker . getWidenedType ( checker . getUnionType ( types , UnionReduction . Subtype ) ) ;
991
- return isRestParameter ? checker . createArrayType ( type ) : type ;
992
- }
993
- return undefined ;
995
+ const type = unifyTypes ( types ) ;
996
+ return isRestParameter ? checker . createArrayType ( type ) : type ;
994
997
}
995
998
996
- function getSignatureFromCalls ( calls : CallUsage [ ] ) : Signature {
999
+ function getSignatureFromCalls ( calls : CallUsage [ ] , fallbackReturn : Type ) : Signature {
997
1000
const parameters : Symbol [ ] = [ ] ;
998
1001
const length = Math . max ( ...calls . map ( c => c . argumentTypes . length ) ) ;
999
1002
for ( let i = 0 ; i < length ; i ++ ) {
1000
1003
const symbol = checker . createSymbol ( SymbolFlags . FunctionScopedVariable , escapeLeadingUnderscores ( `arg${ i } ` ) ) ;
1001
- symbol . type = unifyFromUsage ( calls . map ( call => call . argumentTypes [ i ] || checker . getUndefinedType ( ) ) ) ;
1004
+ symbol . type = unifyTypes ( calls . map ( call => call . argumentTypes [ i ] || checker . getUndefinedType ( ) ) ) ;
1002
1005
if ( calls . some ( call => call . argumentTypes [ i ] === undefined ) ) {
1003
1006
symbol . flags |= SymbolFlags . Optional ;
1004
1007
}
1005
1008
parameters . push ( symbol ) ;
1006
1009
}
1007
- const returnType = unifyFromUsage ( inferFromUsage ( combineUsages ( calls . map ( call => call . return_ ) ) ) , checker . getVoidType ( ) ) ;
1010
+ const returnType = unifyTypes ( inferFromUsage ( combineUsages ( calls . map ( call => call . return_ ) ) ) , fallbackReturn ) ;
1008
1011
// TODO: GH#18217
1009
1012
return checker . createSignature ( /*declaration*/ undefined ! , /*typeParameters*/ undefined , /*thisParameter*/ undefined , parameters , returnType , /*typePredicate*/ undefined , length , /*hasRestParameter*/ false , /*hasLiteralTypes*/ false ) ;
1010
1013
}
0 commit comments