@@ -401,7 +401,7 @@ namespace ts.codefix {
401
401
402
402
interface CallUsage {
403
403
argumentTypes : Type [ ] ;
404
- returnType : Usage ;
404
+ return_ : Usage ;
405
405
}
406
406
407
407
interface Usage {
@@ -671,16 +671,17 @@ namespace ts.codefix {
671
671
function inferTypeFromCallExpression ( parent : CallExpression | NewExpression , usage : Usage ) : void {
672
672
const call : CallUsage = {
673
673
argumentTypes : [ ] ,
674
- returnType : { }
674
+ return_ : { }
675
675
} ;
676
676
677
677
if ( parent . arguments ) {
678
678
for ( const argument of parent . arguments ) {
679
+ // TODO: should recursively infer a usage here, right?
679
680
call . argumentTypes . push ( checker . getTypeAtLocation ( argument ) ) ;
680
681
}
681
682
}
682
683
683
- calculateUsageOfNode ( parent , call . returnType ) ;
684
+ calculateUsageOfNode ( parent , call . return_ ) ;
684
685
if ( parent . kind === SyntaxKind . CallExpression ) {
685
686
( usage . calls || ( usage . calls = [ ] ) ) . push ( call ) ;
686
687
}
@@ -830,6 +831,7 @@ namespace ts.codefix {
830
831
}
831
832
832
833
types . push ( ...( usage . candidateTypes || [ ] ) . map ( t => checker . getBaseTypeOfLiteralType ( t ) ) ) ;
834
+ types . push ( ...findBuiltinType ( usage ) ) ;
833
835
834
836
if ( usage . properties && hasCalls ( usage . properties . get ( "then" as __String ) ) ) {
835
837
const paramType = getParameterTypeFromCalls ( 0 , usage . properties . get ( "then" as __String ) ! . calls ! , /*isRestParameter*/ false ) ! ; // TODO: GH#18217
@@ -858,15 +860,11 @@ namespace ts.codefix {
858
860
}
859
861
860
862
if ( usage . calls ) {
861
- for ( const call of usage . calls ) {
862
- callSignatures . push ( getSignatureFromCall ( call ) ) ;
863
- }
863
+ callSignatures . push ( getSignatureFromCalls ( usage . calls ) ) ;
864
864
}
865
865
866
866
if ( usage . constructs ) {
867
- for ( const construct of usage . constructs ) {
868
- constructSignatures . push ( getSignatureFromCall ( construct ) ) ;
869
- }
867
+ constructSignatures . push ( getSignatureFromCalls ( usage . constructs ) ) ;
870
868
}
871
869
872
870
if ( usage . stringIndex ) {
@@ -882,6 +880,61 @@ namespace ts.codefix {
882
880
}
883
881
}
884
882
883
+ function combineUsages ( usages : Usage [ ] ) : Usage {
884
+ return {
885
+ isNumber : usages . some ( u => u . isNumber ) ,
886
+ isString : usages . some ( u => u . isString ) ,
887
+ isNumberOrString : usages . some ( u => u . isNumberOrString ) ,
888
+ candidateTypes : flatMap ( usages , u => u . candidateTypes ) as Type [ ] ,
889
+ properties : undefined , // TODO
890
+ calls : flatMap ( usages , u => u . calls ) as CallUsage [ ] ,
891
+ constructs : flatMap ( usages , u => u . constructs ) as CallUsage [ ] ,
892
+ numberIndex : forEach ( usages , u => u . numberIndex ) ,
893
+ stringIndex : forEach ( usages , u => u . stringIndex ) ,
894
+ candidateThisTypes : flatMap ( usages , u => u . candidateThisTypes ) as Type [ ] ,
895
+ }
896
+ }
897
+
898
+ function findBuiltinType ( usage : Usage ) : Type [ ] {
899
+ const builtins = [
900
+ checker . getStringType ( ) ,
901
+ checker . getNumberType ( ) ,
902
+ checker . createArrayType ( checker . getAnyType ( ) ) ,
903
+ checker . createPromiseType ( checker . getAnyType ( ) ) ,
904
+ // checker.getFunctionType() // not sure what this was supposed to be good for.
905
+ ] ;
906
+ const matches = builtins . filter ( t => matchesAllPropertiesOf ( t , usage ) ) ;
907
+ if ( false && 0 < matches . length && matches . length < 3 ) {
908
+ return matches ;
909
+ }
910
+ return [ ] ;
911
+ }
912
+
913
+ function matchesAllPropertiesOf ( type : Type , usage : Usage ) {
914
+ if ( ! usage . properties ) return false ;
915
+ let result = true ;
916
+ usage . properties . forEach ( ( prop , name ) => {
917
+ const source = checker . getUnionType ( inferFromUsage ( prop ) ) ;
918
+ const target = checker . getTypeOfPropertyOfType ( type , name as string ) ;
919
+ if ( target && prop . calls ) {
920
+ const sigs = checker . getSignaturesOfType ( target , ts . SignatureKind . Call ) ;
921
+ result = result && ! ! sigs . length && sigs . some (
922
+ sig => checker . isTypeAssignableTo (
923
+ getFunctionFromCalls ( prop . calls ! ) ,
924
+ checker . createAnonymousType ( undefined ! , createSymbolTable ( ) , [ sig ] , emptyArray , undefined , undefined ) ) ) ;
925
+ }
926
+ else {
927
+ result = result && ! ! source && ! ! target && checker . isTypeAssignableTo ( source , target ) ;
928
+ }
929
+ } ) ;
930
+ return result ;
931
+ }
932
+
933
+
934
+ function getFunctionFromCalls ( calls : CallUsage [ ] ) {
935
+ return checker . createAnonymousType ( undefined ! , createSymbolTable ( ) , [ getSignatureFromCalls ( calls ) ] , emptyArray , undefined , undefined ) ;
936
+ }
937
+
885
938
function getParameterTypeFromCalls ( parameterIndex : number , calls : CallUsage [ ] , isRestParameter : boolean ) {
886
939
let types : Type [ ] = [ ] ;
887
940
if ( calls ) {
@@ -904,16 +957,20 @@ namespace ts.codefix {
904
957
return undefined ;
905
958
}
906
959
907
- function getSignatureFromCall ( call : CallUsage ) : Signature {
960
+ function getSignatureFromCalls ( calls : CallUsage [ ] ) : Signature {
908
961
const parameters : Symbol [ ] = [ ] ;
909
- for ( let i = 0 ; i < call . argumentTypes . length ; i ++ ) {
962
+ const length = Math . max ( ...calls . map ( c => c . argumentTypes . length ) ) ;
963
+ for ( let i = 0 ; i < length ; i ++ ) {
910
964
const symbol = checker . createSymbol ( SymbolFlags . FunctionScopedVariable , escapeLeadingUnderscores ( `arg${ i } ` ) ) ;
911
- symbol . type = checker . getWidenedType ( checker . getBaseTypeOfLiteralType ( call . argumentTypes [ i ] ) ) ;
965
+ symbol . type = unifyFromUsage ( calls . map ( call => call . argumentTypes [ i ] || checker . getUndefinedType ( ) ) ) ;
966
+ if ( calls . some ( call => call . argumentTypes [ i ] === undefined ) ) {
967
+ symbol . flags |= SymbolFlags . Optional ;
968
+ }
912
969
parameters . push ( symbol ) ;
913
970
}
914
- const returnType = unifyFromUsage ( inferFromUsage ( call . returnType ) , checker . getVoidType ( ) ) ;
971
+ const returnType = unifyFromUsage ( inferFromUsage ( combineUsages ( calls . map ( call => call . return_ ) ) ) , checker . getVoidType ( ) ) ;
915
972
// TODO: GH#18217
916
- return checker . createSignature ( /*declaration*/ undefined ! , /*typeParameters*/ undefined , /*thisParameter*/ undefined , parameters , returnType , /*typePredicate*/ undefined , call . argumentTypes . length , /*hasRestParameter*/ false , /*hasLiteralTypes*/ false ) ;
973
+ return checker . createSignature ( /*declaration*/ undefined ! , /*typeParameters*/ undefined , /*thisParameter*/ undefined , parameters , returnType , /*typePredicate*/ undefined , length , /*hasRestParameter*/ false , /*hasLiteralTypes*/ false ) ;
917
974
}
918
975
919
976
function addCandidateType ( usage : Usage , type : Type | undefined ) {
0 commit comments