@@ -401,7 +401,7 @@ namespace ts.codefix {
401401
402402 interface CallUsage {
403403 argumentTypes : Type [ ] ;
404- returnType : Usage ;
404+ return_ : Usage ;
405405 }
406406
407407 interface Usage {
@@ -671,16 +671,17 @@ namespace ts.codefix {
671671 function inferTypeFromCallExpression ( parent : CallExpression | NewExpression , usage : Usage ) : void {
672672 const call : CallUsage = {
673673 argumentTypes : [ ] ,
674- returnType : { }
674+ return_ : { }
675675 } ;
676676
677677 if ( parent . arguments ) {
678678 for ( const argument of parent . arguments ) {
679+ // TODO: should recursively infer a usage here, right?
679680 call . argumentTypes . push ( checker . getTypeAtLocation ( argument ) ) ;
680681 }
681682 }
682683
683- calculateUsageOfNode ( parent , call . returnType ) ;
684+ calculateUsageOfNode ( parent , call . return_ ) ;
684685 if ( parent . kind === SyntaxKind . CallExpression ) {
685686 ( usage . calls || ( usage . calls = [ ] ) ) . push ( call ) ;
686687 }
@@ -830,6 +831,7 @@ namespace ts.codefix {
830831 }
831832
832833 types . push ( ...( usage . candidateTypes || [ ] ) . map ( t => checker . getBaseTypeOfLiteralType ( t ) ) ) ;
834+ types . push ( ...findBuiltinType ( usage ) ) ;
833835
834836 if ( usage . properties && hasCalls ( usage . properties . get ( "then" as __String ) ) ) {
835837 const paramType = getParameterTypeFromCalls ( 0 , usage . properties . get ( "then" as __String ) ! . calls ! , /*isRestParameter*/ false ) ! ; // TODO: GH#18217
@@ -858,15 +860,11 @@ namespace ts.codefix {
858860 }
859861
860862 if ( usage . calls ) {
861- for ( const call of usage . calls ) {
862- callSignatures . push ( getSignatureFromCall ( call ) ) ;
863- }
863+ callSignatures . push ( getSignatureFromCalls ( usage . calls ) ) ;
864864 }
865865
866866 if ( usage . constructs ) {
867- for ( const construct of usage . constructs ) {
868- constructSignatures . push ( getSignatureFromCall ( construct ) ) ;
869- }
867+ constructSignatures . push ( getSignatureFromCalls ( usage . constructs ) ) ;
870868 }
871869
872870 if ( usage . stringIndex ) {
@@ -882,6 +880,61 @@ namespace ts.codefix {
882880 }
883881 }
884882
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+
885938 function getParameterTypeFromCalls ( parameterIndex : number , calls : CallUsage [ ] , isRestParameter : boolean ) {
886939 let types : Type [ ] = [ ] ;
887940 if ( calls ) {
@@ -904,16 +957,20 @@ namespace ts.codefix {
904957 return undefined ;
905958 }
906959
907- function getSignatureFromCall ( call : CallUsage ) : Signature {
960+ function getSignatureFromCalls ( calls : CallUsage [ ] ) : Signature {
908961 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 ++ ) {
910964 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+ }
912969 parameters . push ( symbol ) ;
913970 }
914- const returnType = unifyFromUsage ( inferFromUsage ( call . returnType ) , checker . getVoidType ( ) ) ;
971+ const returnType = unifyFromUsage ( inferFromUsage ( combineUsages ( calls . map ( call => call . return_ ) ) ) , checker . getVoidType ( ) ) ;
915972 // 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 ) ;
917974 }
918975
919976 function addCandidateType ( usage : Usage , type : Type | undefined ) {
0 commit comments