@@ -393,6 +393,19 @@ namespace ts.codefix {
393
393
394
394
function inferTypeFromReferences ( program : Program , references : ReadonlyArray < Identifier > , cancellationToken : CancellationToken ) {
395
395
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
+
396
409
return {
397
410
single,
398
411
parameters,
@@ -777,7 +790,7 @@ namespace ts.codefix {
777
790
good = good . filter ( i => ! ( checker . getObjectFlags ( i ) & ObjectFlags . Anonymous ) ) ;
778
791
good . push ( unifyAnonymousTypes ( anons ) ) ;
779
792
}
780
- return checker . getWidenedType ( checker . getUnionType ( good , UnionReduction . Subtype ) ) ;
793
+ return checker . getWidenedType ( checker . getUnionType ( good . map ( checker . getBaseTypeOfLiteralType ) , UnionReduction . Subtype ) ) ;
781
794
}
782
795
783
796
function unifyAnonymousTypes ( anons : AnonymousType [ ] ) {
@@ -928,28 +941,9 @@ namespace ts.codefix {
928
941
929
942
function findBuiltinTypes ( usage : Usage ) : Type [ ] {
930
943
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
939
944
const matches = builtins . filter ( t => matchesAllPropertiesOf ( t , usage ) ) ;
940
945
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 ) ) ;
953
947
}
954
948
return [ ] ;
955
949
}
@@ -974,26 +968,84 @@ namespace ts.codefix {
974
968
return result ;
975
969
}
976
970
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 ;
977
979
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 ) ) ;
980
991
}
981
992
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
+ }
992
1012
}
993
1013
}
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 ) ) ;
994
1040
}
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 ) ;
997
1049
}
998
1050
999
1051
function getSignatureFromCalls ( calls : CallUsage [ ] , fallbackReturn : Type ) : Signature {
@@ -1023,9 +1075,5 @@ namespace ts.codefix {
1023
1075
( usage . candidateThisTypes || ( usage . candidateThisTypes = [ ] ) ) . push ( type ) ;
1024
1076
}
1025
1077
}
1026
-
1027
- function hasCalls ( usage : Usage | undefined ) : boolean {
1028
- return ! ! usage && ! ! usage . calls ;
1029
- }
1030
1078
}
1031
1079
}
0 commit comments