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
Generic inference to unknown after using function arguments #43371
Hello 👋🏻
I'm facing some strange issue with Generics. Until I specify input arguments in function all generics are inferred correctly. But as soon as I mention argument in function the inference is broken.
Thanks in advance for any help 🙏🏻
🔎 Search Terms
generic is lost after specifying input arguments
typescript generic unknown after specifying arguments (found this stackoverflow but not sure if it's the same case)
🕗 Version & Regression Information
typescript: 4.2.3
Also tried nightly build but the issue remains
This is the behavior in every version I tried, and I reviewed the FAQ for entries about Generics_____
Our inference algorithm is based on doing a fixed number of passes to collect inference candidates (see also #30134 which proposes that we use an unbounded number of passes). In this case, the parameterless function is non-context-sensitive, meaning that its return type can be known ahead-of-time to not depend on the ultimate result of inference, so we're able to use its return type to collect an inference candidate.
The parameterful version is context-sensitive, because it has an unannotated parameter. From the outside we can see that this ultimately won't matter, but the inference process is not far enough along at this point to use that information, and thus delays collecting the return type as an inference candidate until later on at which point AccessCode has already been fixed to unknown due to a lack of covariant candidates.
The workaround in this case is to write (inp: { code: string }) =>.
Thanks for the great explanation Ryan Cavanaugh (@RyanCavanaugh), very helpful! Ran into this exact issue today (playground link), and it was especially insidious because it silently introduced anys into the codebase without causing any compiler errors. Leaving a slightly simplified example here just in case it's useful for discussions/example use cases that could be improved. It'd be much less of a priority for me if it just introduced unknowns instead of anys, because that would be caught easily in other places at compile time:
typeInputData={someNum: number;}typeOptions<T>={selector: (data: InputData)=>T;equalityFn?: (a: T,b: T)=>boolean;}functionuseSelector<T>(options: Options<T>): T{returnoptions.selector({someNum: 20});}// GOOD: works when there's an implict type for `data` and no `equalityFn`,// `foo` is correctly of type `number`const{ foo }=useSelector({selector: (data)=>({foo: data.someNum}),});// GOOD: works when there's an expicit type for `data` and an `equalityFn`// `foo2` is correctly of type `number`const{ foo2 }=useSelector({selector: (data: InputData)=>({foo2: data.someNum}),equalityFn: (a,b)=>a===b,});// BAD: fails silently when there's an implicit type for `data` and an `equalityFn`// foo3 is `any`const{ foo3 }=useSelector({selector: (data)=>({foo3: data.someNum}),equalityFn: (a,b)=>a===b,});
Jason Killian (@jkillian) that's a bug caused by the binding pattern (aka destructuring). I think it's reported already but can't find it at the moment - can you log a new issue?
Ryan Cavanaugh (@RyanCavanaugh), thanks, agreed after looking at it again that the any vs. unknown part of my above post is a different issue than this ticket about function arguments and inference. I've made the new issue for that here: #45074. Please feel free to edit anything as necessary if I used wrong terminology anywhere (as is highly likely 😆).
Closed #46977 (duplicate) for this one. We're trying to do get around this in our React Query library and it's definitely been weirding us out 😂 . I think it's worth dropping in a quick snippet of our use-case for posterity:
functionuseQuery<TQueryKey,TData>(_options: {queryKey: TQueryKey;queryFn: (context?: {queryKey: TQueryKey})=>TData;onSuccess: (data: TData)=>void;}){}constqueryKey=["test",1,2,{3: true}]// no context no cryuseQuery({
queryKey,queryFn: (): number=>1,onSuccess: (data)=>data.toFixed(),});// as soon as I use ctx, data is no longer of type number for onSuccessuseQuery({
queryKey,queryFn: (ctx): number=>1,// Why is `data` of type `any`?onSuccess: (data)=>data.toFixed(),});
For now, we've dropped the proposal to adopt this syntax as the primary syntax in our API, but we would really love to make it happen in the future. Where should we go from here to help that happen? cc Dominik Dorfmeister 🔮 (@TkDodo)
Reacted by Michael Haglund, Dominik Dorfmeister 🔮, Patrick Kerschbaum, Clifford Fajardo , Artur Baybulatov, Julian Meinking, Damian and German Jablonski
Yes, this is an existing limitation. Note that it isn't about properties but certainly, it's easier to run into this with object properties. Let's take a look at this example with positional arguments (TS playground):
declarefunctioncreateModule1<RequestData,FormValues>(values: FormValues,mapValues: (values: FormValues)=>RequestData,onCreate: (data: RequestData)=>void,): void;// this workscreateModule1({name: 2,},(values)=>({title: values.name,}),(data)=>data.title.toFixed(),);declarefunctioncreateModule2<RequestData,FormValues>(values: FormValues,onCreate: (data: RequestData)=>void,mapValues: (values: FormValues)=>RequestData,): void;// this doesn'tcreateModule2({name: 2,},(data)=>data.title.toFixed(),// 'data' is of type 'unknown'.(18046)(values)=>({title: values.name,}),);
As we can see, when context-sensitive functions are involved it always has been important to have the "producer" before the "consumer".
Activity
misha-erm commentedon Mar 25, 2021
Workbench Repro
ilogico commentedon Mar 25, 2021
This issue might be related: 38264.
RyanCavanaugh commentedon Mar 25, 2021
Our inference algorithm is based on doing a fixed number of passes to collect inference candidates (see also #30134 which proposes that we use an unbounded number of passes). In this case, the parameterless function is non-context-sensitive, meaning that its return type can be known ahead-of-time to not depend on the ultimate result of inference, so we're able to use its return type to collect an inference candidate.
The parameterful version is context-sensitive, because it has an unannotated parameter. From the outside we can see that this ultimately won't matter, but the inference process is not far enough along at this point to use that information, and thus delays collecting the return type as an inference candidate until later on at which point
AccessCodehas already been fixed tounknowndue to a lack of covariant candidates.The workaround in this case is to write
(inp: { code: string }) =>.misha-erm commentedon Mar 25, 2021
wow, thanks for the detailed answer and the workaround
jkillian commentedon Jul 16, 2021
Thanks for the great explanation Ryan Cavanaugh (@RyanCavanaugh), very helpful! Ran into this exact issue today (playground link), and it was especially insidious because it silently introduced
anys into the codebase without causing any compiler errors. Leaving a slightly simplified example here just in case it's useful for discussions/example use cases that could be improved. It'd be much less of a priority for me if it just introducedunknowns instead ofanys, because that would be caught easily in other places at compile time:RyanCavanaugh commentedon Jul 16, 2021
Jason Killian (@jkillian) that's a bug caused by the binding pattern (aka destructuring). I think it's reported already but can't find it at the moment - can you log a new issue?
jkillian commentedon Jul 16, 2021
Ryan Cavanaugh (@RyanCavanaugh), thanks, agreed after looking at it again that the
anyvs.unknownpart of my above post is a different issue than this ticket about function arguments and inference. I've made the new issue for that here: #45074. Please feel free to edit anything as necessary if I used wrong terminology anywhere (as is highly likely 😆).tannerlinsley commentedon Dec 1, 2021
Closed #46977 (duplicate) for this one. We're trying to do get around this in our React Query library and it's definitely been weirding us out 😂 . I think it's worth dropping in a quick snippet of our use-case for posterity:
For now, we've dropped the proposal to adopt this syntax as the primary syntax in our API, but we would really love to make it happen in the future. Where should we go from here to help that happen? cc Dominik Dorfmeister 🔮 (@TkDodo)
jkillian commentedon Apr 25, 2022
I believe this is related to #47599 and is now fixed by #48538?
Andarist commentedon Sep 22, 2022
Ryan Cavanaugh (@RyanCavanaugh) it seems that this got fixed by intra-inference improvements in 4.7, all reported playgrounds work:
The issue can be closed.
layerok commentedon Sep 26, 2024
So the order of properties is important when inferring
For example this works
But this doesn't
Andarist commentedon Sep 26, 2024
Yes, this is an existing limitation. Note that it isn't about properties but certainly, it's easier to run into this with object properties. Let's take a look at this example with positional arguments (TS playground):
As we can see, when context-sensitive functions are involved it always has been important to have the "producer" before the "consumer".