Repository navigation
Typescript does not implicitly infers types when providing function as arguments? #17520
Description
Activity
This is an effect of TypeScript's type argument inference algorithm:
- First, we process (from left to right) all arguments that are deemed context insensitive, which effectively means all arguments that don't contain function expressions with un-annotated parameters.
- Then, we then separately process the context sensitive arguments (again from left to right), and for the un-annotated parameters in the contained function expressions, we fix inferences made for type parameters referenced in the corresponding contextual type and use the inferences we have made so far to compute and assign a type. Once a type parameter is fixed in this manner, we make no further inferences for that type parameter.
This differs from the unification based type inference implemented by some functional programming languages, but it has the distinct advantage of being able to make partial inferences in incomplete code which is hugely beneficial to statement completion in IDEs. For example, see #15680 (comment) and the thread in #17237.
Now, in your example we get it right when there is an arrow function argument because we classify that as contextually sensitive and process it after first making inferences from other arguments. But we can't handle the situation where the arguments don't appear context sensitive (which the simple identifier
identitydoesn't) and where we actually need to process the arguments in reverse order in order to succeed.- addedDesign LimitationConstraints of the existing architecture prevent this from being fixedConstraints of the existing architecture prevent this from being fixed
on Jul 30, 2017 Anders Hejlsberg (@ahejlsberg) Wouldn't lifting generic parameters in argument types work?
map(identity :: <T>(x: T) => T, [1, 2, 3] :: number[]) :: <T, U>(f: (x: T) => U, arr: T[]) => U[]map(identity :: (x: T_from_identity) => T_from_identity, [1, 2, 3] :: number[]) :: <T_from_identity>(f: (x: T_from_identity) => T_from_identity, arr: T_from_identity) => T_from_identity[]map(identity :: (x: number) => number, [1, 2, 3] :: number) :: (f: (x: number) => number, arr: number[]) => number[]
This would solve lots of other cases, like
compose(), when they are given generic argument types, too. This is something that really hurts libraries likeramdaandrecompose.Reacted by Jakub KorzeniowskiSimon Buchan (@simonbuchan) Yes, but this type of unification gets exponentially more complicated when types aren't the simple naked type parameters in your example, but more complex constructs such as union and intersection types. For example, see #15016 (comment).
Following up this chain, I apologise for my "smart" suggestion raising old wounds :)
Looks like #16072 gets us nearly there, thanks for that!
Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.
- locked and limited conversation to collaborators
on Jun 14, 2018
When providing
identityfunction tomap, it's return type is incorrect(seex2), while providing anonymous function as a callback leads to a correct output type(seex1).TypeScript Version: 2.4.0 / nightly (2.5.0-dev.201xxxxx)
Code
Expected behavior:
Return type of
mapfunction, when providingidentityfunction should benumber[]Actual behavior:
Return type is
{}[]Also, I've realized that

mapfunction in 2nd example does not infers evenTtype. It's{}currently, while it's obvious should be anumbertype, as we are providing array of numbers:The second interesting thing is, that type inferring works fine, if
mapfunction arguments will be reversed: