@@ -421,7 +421,7 @@ namespace ts.codefix {
421421 }
422422
423423 function single ( ) : Type {
424- return unifyFromUsage ( inferTypesFromReferencesSingle ( references ) ) ;
424+ return unifyTypes ( inferTypesFromReferencesSingle ( references ) ) ;
425425 }
426426
427427 function parameters ( declaration : FunctionLike ) : ParameterInference [ ] | undefined {
@@ -457,7 +457,7 @@ namespace ts.codefix {
457457 const inferred = inferTypesFromReferencesSingle ( getReferences ( parameter . name , program , cancellationToken ) ) ;
458458 types . push ( ...( isRest ? mapDefined ( inferred , checker . getElementTypeOfArrayType ) : inferred ) ) ;
459459 }
460- const type = unifyFromUsage ( types ) ;
460+ const type = unifyTypes ( types ) ;
461461 return {
462462 type : isRest ? checker . createArrayType ( type ) : type ,
463463 isOptional : isOptional && ! isRest ,
@@ -473,7 +473,7 @@ namespace ts.codefix {
473473 calculateUsageOfNode ( reference , usage ) ;
474474 }
475475
476- return unifyFromUsage ( usage . candidateThisTypes || emptyArray ) ;
476+ return unifyTypes ( usage . candidateThisTypes || emptyArray ) ;
477477 }
478478
479479 function inferTypesFromReferencesSingle ( references : readonly Identifier [ ] ) : Type [ ] {
@@ -627,6 +627,9 @@ namespace ts.codefix {
627627 else if ( otherOperandType . flags & TypeFlags . StringLike ) {
628628 usage . isString = true ;
629629 }
630+ else if ( otherOperandType . flags & TypeFlags . Any ) {
631+ // do nothing, maybe we'll learn something elsewhere
632+ }
630633 else {
631634 usage . isNumberOrString = true ;
632635 }
@@ -748,7 +751,7 @@ namespace ts.codefix {
748751 return inferences . filter ( i => toRemove . every ( f => ! f ( i ) ) ) ;
749752 }
750753
751- function unifyFromUsage ( inferences : ReadonlyArray < Type > , fallback = checker . getAnyType ( ) ) : Type {
754+ function unifyTypes ( inferences : ReadonlyArray < Type > , fallback = checker . getAnyType ( ) ) : Type {
752755 if ( ! inferences . length ) return fallback ;
753756
754757 // 1. string or number individually override string | number
@@ -774,7 +777,7 @@ namespace ts.codefix {
774777 good = good . filter ( i => ! ( checker . getObjectFlags ( i ) & ObjectFlags . Anonymous ) ) ;
775778 good . push ( unifyAnonymousTypes ( anons ) ) ;
776779 }
777- return checker . getWidenedType ( checker . getUnionType ( good ) ) ;
780+ return checker . getWidenedType ( checker . getUnionType ( good , UnionReduction . Subtype ) ) ;
778781 }
779782
780783 function unifyAnonymousTypes ( anons : AnonymousType [ ] ) {
@@ -832,16 +835,7 @@ namespace ts.codefix {
832835 }
833836
834837 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 ) ) ;
845839
846840 if ( usage . numberIndex ) {
847841 types . push ( checker . createArrayType ( recur ( usage . numberIndex ) ) ) ;
@@ -864,11 +858,12 @@ namespace ts.codefix {
864858 }
865859
866860 if ( usage . calls ) {
867- callSignatures . push ( getSignatureFromCalls ( usage . calls ) ) ;
861+ callSignatures . push ( getSignatureFromCalls ( usage . calls , checker . getVoidType ( ) ) ) ;
868862 }
869863
870864 if ( usage . constructs ) {
871- constructSignatures . push ( getSignatureFromCalls ( usage . constructs ) ) ;
865+ // TODO: fallback return should maybe be {}?
866+ constructSignatures . push ( getSignatureFromCalls ( usage . constructs , checker . getVoidType ( ) ) ) ;
872867 }
873868
874869 if ( usage . stringIndex ) {
@@ -880,7 +875,7 @@ namespace ts.codefix {
880875 return types ; // TODO: Should cache this since I HOPE it doesn't change
881876
882877 function recur ( innerUsage : Usage ) : Type {
883- return unifyFromUsage ( inferFromUsage ( innerUsage ) ) ;
878+ return unifyTypes ( inferFromUsage ( innerUsage ) ) ;
884879 }
885880 }
886881
@@ -931,80 +926,88 @@ namespace ts.codefix {
931926 }
932927 }
933928
934- function findBuiltinType ( usage : Usage ) : Type [ ] {
929+ function findBuiltinTypes ( usage : Usage ) : Type [ ] {
930+ if ( ! usage . properties || ! usage . properties . size ) return [ ] ;
935931 const builtins = [
936932 checker . getStringType ( ) ,
937933 checker . getNumberType ( ) ,
938934 checker . createArrayType ( checker . getAnyType ( ) ) ,
939935 checker . createPromiseType ( checker . getAnyType ( ) ) ,
940936 // checker.getFunctionType() // TODO: not sure what this was supposed to be good for.
941937 ] ;
938+ // TODO: Still need to infer type parameters
942939 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+ } ) ;
945953 }
946954 return [ ] ;
947955 }
948956
949957 function matchesAllPropertiesOf ( type : Type , usage : Usage ) {
950958 if ( ! usage . properties ) return false ;
951959 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 ) ) ;
961969 }
962970 else {
963- result = result && ! ! source && ! ! target && checker . isTypeAssignableTo ( source , target ) ;
971+ result = result && checker . isTypeAssignableTo ( source , unifyTypes ( inferFromUsage ( propUsage ) ) ) ;
964972 }
965973 } ) ;
966974 return result ;
967975 }
968976
969977
970978 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 ) ;
972980 }
973981
974982 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)
975984 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 ] ) ) ;
985992 }
986993 }
987994 }
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 ;
994997 }
995998
996- function getSignatureFromCalls ( calls : CallUsage [ ] ) : Signature {
999+ function getSignatureFromCalls ( calls : CallUsage [ ] , fallbackReturn : Type ) : Signature {
9971000 const parameters : Symbol [ ] = [ ] ;
9981001 const length = Math . max ( ...calls . map ( c => c . argumentTypes . length ) ) ;
9991002 for ( let i = 0 ; i < length ; i ++ ) {
10001003 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 ( ) ) ) ;
10021005 if ( calls . some ( call => call . argumentTypes [ i ] === undefined ) ) {
10031006 symbol . flags |= SymbolFlags . Optional ;
10041007 }
10051008 parameters . push ( symbol ) ;
10061009 }
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 ) ;
10081011 // TODO: GH#18217
10091012 return checker . createSignature ( /*declaration*/ undefined ! , /*typeParameters*/ undefined , /*thisParameter*/ undefined , parameters , returnType , /*typePredicate*/ undefined , length , /*hasRestParameter*/ false , /*hasLiteralTypes*/ false ) ;
10101013 }
0 commit comments