You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Can't correctly infer generic interface type when it's behind a function #25092
interfaceMyInterface<T>{retrieveGeneric: (parameter: string)=>T,operateWithGeneric: (generic: T)=>string}constinferTypeFn=<T>(generic: MyInterface<T>)=>generic;// inferred type for myGeneric = MyInterface<{}>, `generic.toFixed()` marked as error (as {} doesn't have .toFixed())constmyGeneric=inferTypeFn({retrieveGeneric: parameter=>5,operateWithGeneric: generic=>generic.toFixed()});// inferred type for myGeneric = MyInterface<number>, everything OKconstmyWorkingGeneric=inferTypeFn({retrieveGeneric: (parameter: string)=>5,operateWithGeneric: generic=>generic.toFixed()});
Expected behavior: myGeneric has every type correctly inferred, parameter is a string, generic is a number.
Actual behavior:
it doesn't infer the correct type for generic parameter unless you manually specify the type of parameter (which it already had the right type)
I wonder if I got the same issue for interfaces + classes. Have a look at this playground.
If you hover over createHandler (line 15) you will see {} as inferred type instead of string
Inside the compiler, we have this concepts of something called a contextual type and a context-sensitive function. A contextual type is simply the type a position must have based on what it is being assigned to. A context-sensitive function is a function with untyped function parameters. When a context-sensitive function exists, its type must be derived from the contextual type at that location.
both functions in the object literal are context-sensitive. This means we try to solve their types during inference independently. We see parameter unannotated first, lock in {} as the inference (since we decide that parameter must have a type before we can check the body), and then that's what it's stuck with.
only operateWithGeneric is context-sensitive. This means we create an inference of T -> string before we even look at the context sensitive function (since we do context-free inferences and then context-sensitive ones) and everything's good.
We're conservative here because we assume that a parameter, if present, may affect the return type of a function. Based on what we've talked about in related issues before, it's unlikely we'll specifically improve this without moving to a full unification-based solve for the arguments. the recommendation right now is to not list parameters you don't use (or if you must list them, manually assign them types). ❤️
Reacted by csr632, Yuji Tomita and ragamufin
Reacted by Lynn, Alec Larson, maxwellium, csr632, Yuji Tomita and Andreas Krut
interfaceMyInterface<T>{retrieveGeneric: (parameter: string)=>T,operateWithGeneric: (generic: T)=>string}exportdeclarefunctionComponent<T>(props: MyInterface<T>): JSX.Elementconstelement=(<ComponentoperateWithGeneric={generic=>generic.toFixed()}// <- error here with generic being `{}`retrieveGeneric={parameter=>5}/>)
Activity
donaldpipowitch commentedon Jul 2, 2018
Is this the same issue? Return type is casted to
any, if generic is used:Tested on PlayGround.
otbe commentedon Jul 2, 2018
I wonder if I got the same issue for interfaces + classes. Have a look at this playground.
If you hover over
createHandler(line 15) you will see{}as inferred type instead ofstringYiSiWang commentedon Jul 3, 2018
Andy (Andrewkraft) (@Andy-MS) I got a similar issue and found a solution/workaround:
Hope it helps :) .
Benjamin Otto (@otbe) This works.
otbe commentedon Jul 3, 2018
14 (@YiSiWang) thanks! Never thought swapping the position would change something :)
I wonder if this is bug or works as intended?
weswigham commentedon Sep 10, 2018
Inside the compiler, we have this concepts of something called a contextual type and a context-sensitive function. A contextual type is simply the type a position must have based on what it is being assigned to. A context-sensitive function is a function with untyped function parameters. When a context-sensitive function exists, its type must be derived from the contextual type at that location.
In
both functions in the object literal are context-sensitive. This means we try to solve their types during inference independently. We see
parameterunannotated first, lock in{}as the inference (since we decide thatparametermust have a type before we can check the body), and then that's what it's stuck with.whereas
only
operateWithGenericis context-sensitive. This means we create an inference ofT -> stringbefore we even look at the context sensitive function (since we do context-free inferences and then context-sensitive ones) and everything's good.We're conservative here because we assume that a parameter, if present, may affect the return type of a function. Based on what we've talked about in related issues before, it's unlikely we'll specifically improve this without moving to a full unification-based solve for the arguments. the recommendation right now is to not list parameters you don't use (or if you must list them, manually assign them types). ❤️
sutarmin commentedon Oct 17, 2018
Wesley Wigham (@weswigham) I wonder if following piece of code is related to this issue:
We have type checking here (
(arg: string) => argwon't fit), but don't have type inference.Is it the same problem or something different?
raveclassic commentedon Oct 17, 2018
Might be related #23429, #22715 #15005
I've added a compete example of Anton Sutarmin (@sutarmin) 's problem here
ferdaber commentedon Mar 7, 2019
Wesley Wigham (@weswigham) I should add that this affects JSX as well, with generic components:
RyanCavanaugh commentedon Mar 13, 2019
Tracking at #30134
csr632 commentedon Nov 30, 2019
It will report error when you do this:
This is so wierd.