@@ -393,6 +393,19 @@ namespace ts.codefix {
393393
394394 function inferTypeFromReferences ( program : Program , references : ReadonlyArray < Identifier > , cancellationToken : CancellationToken ) {
395395 const checker = program . getTypeChecker ( ) ;
396+ const builtinConstructors : { [ s : string ] : ( t : Type ) => Type } = {
397+ string : ( ) => checker . getStringType ( ) ,
398+ number : ( ) => checker . getNumberType ( ) ,
399+ Array : t => checker . createArrayType ( t ) ,
400+ Promise : t => checker . createPromiseType ( t ) ,
401+ } ;
402+ const builtins = [
403+ checker . getStringType ( ) ,
404+ checker . getNumberType ( ) ,
405+ checker . createArrayType ( checker . getAnyType ( ) ) ,
406+ checker . createPromiseType ( checker . getAnyType ( ) ) ,
407+ ] ;
408+
396409 return {
397410 single,
398411 parameters,
@@ -777,7 +790,7 @@ namespace ts.codefix {
777790 good = good . filter ( i => ! ( checker . getObjectFlags ( i ) & ObjectFlags . Anonymous ) ) ;
778791 good . push ( unifyAnonymousTypes ( anons ) ) ;
779792 }
780- return checker . getWidenedType ( checker . getUnionType ( good , UnionReduction . Subtype ) ) ;
793+ return checker . getWidenedType ( checker . getUnionType ( good . map ( checker . getBaseTypeOfLiteralType ) , UnionReduction . Subtype ) ) ;
781794 }
782795
783796 function unifyAnonymousTypes ( anons : AnonymousType [ ] ) {
@@ -928,28 +941,9 @@ namespace ts.codefix {
928941
929942 function findBuiltinTypes ( usage : Usage ) : Type [ ] {
930943 if ( ! usage . properties || ! usage . properties . size ) return [ ] ;
931- const builtins = [
932- checker . getStringType ( ) ,
933- checker . getNumberType ( ) ,
934- checker . createArrayType ( checker . getAnyType ( ) ) ,
935- checker . createPromiseType ( checker . getAnyType ( ) ) ,
936- // checker.getFunctionType() // TODO: not sure what this was supposed to be good for.
937- ] ;
938- // TODO: Still need to infer type parameters
939944 const matches = builtins . filter ( t => matchesAllPropertiesOf ( t , usage ) ) ;
940945 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- } ) ;
946+ return matches . map ( m => inferTypeParameterFromUsage ( m , usage ) ) ;
953947 }
954948 return [ ] ;
955949 }
@@ -974,26 +968,84 @@ namespace ts.codefix {
974968 return result ;
975969 }
976970
971+ // inference is limited to
972+ // 1. generic types with a single parameter
973+ // 2. inference to/from calls with a single signature
974+ function inferTypeParameterFromUsage ( type : Type , usage : Usage ) {
975+ if ( ! usage . properties || ! ( getObjectFlags ( type ) & ObjectFlags . Reference ) ) return type ;
976+ const generic = ( type as TypeReference ) . target ;
977+ const singleTypeParameter = singleOrUndefined ( generic . typeParameters ) ;
978+ if ( ! singleTypeParameter ) return type ;
977979
978- function getFunctionFromCalls ( calls : CallUsage [ ] ) {
979- return checker . createAnonymousType ( undefined ! , createSymbolTable ( ) , [ getSignatureFromCalls ( calls , checker . getAnyType ( ) ) ] , emptyArray , undefined , undefined ) ;
980+ const types : Type [ ] = [ ] ;
981+ usage . properties . forEach ( ( propUsage , name ) => {
982+ const source = checker . getTypeOfPropertyOfType ( generic , name as string ) ;
983+ if ( ! source ) {
984+ return Debug . fail ( "generic should have all the properties of its reference." ) ;
985+ }
986+ if ( ! propUsage . calls ) return ;
987+
988+ types . push ( ...infer ( source , getFunctionFromCalls ( propUsage . calls ) , singleTypeParameter ) ) ;
989+ } ) ;
990+ return builtinConstructors [ type . symbol . escapedName as string ] ( unifyTypes ( types ) ) ;
980991 }
981992
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)
984- let types : Type [ ] = [ ] ;
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 ] ) ) ;
993+ // TODO: Source and target are bad names. Should be builtinType and usageType...or something
994+ // and search is a bad name
995+ function infer ( source : Type , target : Type , search : Type ) : readonly Type [ ] {
996+ if ( source === search ) {
997+ return [ target ] ;
998+ }
999+ else if ( source . flags & TypeFlags . UnionOrIntersection ) {
1000+ return flatMap ( ( source as UnionOrIntersectionType ) . types , t => infer ( t , target , search ) ) ;
1001+ }
1002+ else if ( getObjectFlags ( source ) & ObjectFlags . Reference && getObjectFlags ( target ) & ObjectFlags . Reference ) {
1003+ // this is wrong because we need a reference to the targetType to, so we can check that it's also a reference
1004+ const sourceArgs = ( source as TypeReference ) . typeArguments ;
1005+ const targetArgs = ( target as TypeReference ) . typeArguments ;
1006+ const types = [ ] ;
1007+ if ( sourceArgs && targetArgs ) {
1008+ for ( let i = 0 ; i < sourceArgs . length ; i ++ ) {
1009+ if ( targetArgs [ i ] ) {
1010+ types . push ( ...infer ( sourceArgs [ i ] , targetArgs [ i ] , search ) ) ;
1011+ }
9921012 }
9931013 }
1014+ return types ;
1015+ }
1016+ const sourceSigs = checker . getSignaturesOfType ( source , SignatureKind . Call ) ;
1017+ const targetSigs = checker . getSignaturesOfType ( target , SignatureKind . Call ) ;
1018+ if ( sourceSigs . length === 1 && targetSigs . length === 1 ) {
1019+ return inferFromSignatures ( sourceSigs [ 0 ] , targetSigs [ 0 ] , search ) ;
1020+ }
1021+ return [ ] ;
1022+ }
1023+
1024+ function inferFromSignatures ( sourceSig : Signature , targetSig : Signature , search : Type ) {
1025+ const types = [ ] ;
1026+ for ( let i = 0 ; i < sourceSig . parameters . length ; i ++ ) {
1027+ const sourceParam = sourceSig . parameters [ i ] ;
1028+ const targetParam = targetSig . parameters [ i ] ;
1029+ const isRest = sourceSig . declaration && isRestParameter ( sourceSig . declaration . parameters [ i ] ) ;
1030+ if ( ! targetParam ) {
1031+ break ;
1032+ }
1033+ let sourceType = checker . getTypeOfSymbolAtLocation ( sourceParam , sourceParam . valueDeclaration ) ;
1034+ let elementType = isRest && checker . getElementTypeOfArrayType ( sourceType ) ;
1035+ if ( elementType ) {
1036+ sourceType = elementType ;
1037+ }
1038+ const targetType = ( targetParam as SymbolLinks ) . type || checker . getTypeOfSymbolAtLocation ( targetParam , targetParam . valueDeclaration ) ;
1039+ types . push ( ...infer ( sourceType , targetType , search ) ) ;
9941040 }
995- const type = unifyTypes ( types ) ;
996- return isRestParameter ? checker . createArrayType ( type ) : type ;
1041+ const sourceReturn = checker . getReturnTypeOfSignature ( sourceSig ) ;
1042+ const targetReturn = checker . getReturnTypeOfSignature ( targetSig ) ;
1043+ types . push ( ...infer ( sourceReturn , targetReturn , search ) ) ;
1044+ return types ;
1045+ }
1046+
1047+ function getFunctionFromCalls ( calls : CallUsage [ ] ) {
1048+ return checker . createAnonymousType ( undefined ! , createSymbolTable ( ) , [ getSignatureFromCalls ( calls , checker . getAnyType ( ) ) ] , emptyArray , undefined , undefined ) ;
9971049 }
9981050
9991051 function getSignatureFromCalls ( calls : CallUsage [ ] , fallbackReturn : Type ) : Signature {
@@ -1023,9 +1075,5 @@ namespace ts.codefix {
10231075 ( usage . candidateThisTypes || ( usage . candidateThisTypes = [ ] ) ) . push ( type ) ;
10241076 }
10251077 }
1026-
1027- function hasCalls ( usage : Usage | undefined ) : boolean {
1028- return ! ! usage && ! ! usage . calls ;
1029- }
10301078 }
10311079}
0 commit comments