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
Return type of partially applied bound generic functionΒ #47899
functiona<TextendsArray<any>>(anArray: T): T{returnanArray;}letb=a.bind(undefined,[1]);// b has type () => any[]letc=()=>a([1]);// c has type () => number[]// Further simplifiedfunctionidentity<Type>(arg: Type): Type{returnarg;}letd=identity.bind(undefined,"test");// d has type () => unknownlete=()=>identity("test");// e has type () => string
π Actual behavior
The types of b and c are different, and the types of d and e are different.
π Expected behavior
I'd expect them to both have the same type (b that of c, () => number[]), d that of e () => string), as the expressions are doing the same thing (arrow function vs bind for partial application of a function).
This limitation is noted in a comment to the "Strict bind, call, and apply methods on functions" PR, but I couldn't find an actual issue referencing this behaviour.
That comment is also mentioned as a known limitation in a similar issue which was closed in #28920, but unfortunately that pr only fixes the issue when binding only this, and not other parameters.
For the case where you exhaust all the arguments, there's really no reason to use bind over a function call.
For the case where you don't exhaust all the arguments, we don't have a type that can represent that - there's not a type annotation on b that you could write that would make it behave the same way as if its bind argument(s) were present at the subsequent call site.
functiona<TextendsArray<any>>(anArray: T,s: T): T{returnanArray;}// b: ??letb=a.bind(undefined,[1]);// b has type () => any[]
For the case where you don't exhaust all the arguments, we don't have a type that can represent that - there's not a type annotation on b that you could write that would make it behave the same way as if its bind argument(s) were present at the subsequent call site.
Can you explain why? I'm currently trying to map an object with functions that conform to a specific (albeit generic) interface to function types that retain their generic-ness, and I think it's related to this issue.
In other words, say I have this the following (a super-reduced version of my use-case):
/* example 1 */typeFunctions=Record<string,((data: any,errorMessage?: string)=>any)>constfns=(()=>{functionnumber(data: string,errorMessage?: string): numberfunctionnumber(data: any,errorMessage?: string): {output: 0}functionnumber<T>(data: T,errorMessage?: string): number|{output: 0}{// Do something with errorMessage ...if(typeofdata==='string'){returnNumber(data)}return{output: 0}}return{ number }})()satisfiesFunctionsletfoo1=fns.number({})// { output: 0 }letbar1=fns.number('hi')// number
Now say I want to support this pattern:
/* example 2 */letdoWith=<T,FextendsFunctions>(value: T,fns: F)=>{constreturnVal={}as{[KinkeyofF]: (errorMessage?: Parameters<F[K]>[1])=>ReturnType<F[K]>// ??}for(const[key,value]ofObject.entries(fns)){returnVal[keyaskeyofF]=value.bind(value,value)}returnreturnVal}letfoo2=doWith({},fns).number()// { output: 0 }letbar2=doWith('hi',fns).number()// { output: 0 } but should be number
IMO there's enough information here for TypeScript to properly infer types, there's just no syntax for it. In other words, the problem is here:
constreturnVal={}as{[KinkeyofF]: Bind<F[K],[T,T]>// Β―\_(γ)_/Β― - basically some way to pass the type to the function type}
TypeScript knows what each function produces in terms of type based on an input type, and that type is known by likewise knowing the input type of the parent function. None of these types are dynamic and they are statically analyzable.
Ryan Cavanaugh (@RyanCavanaugh) - re-thinking it, maybe what you are saying is that there isn't currently a type annotation that can do this, in which case this would be a feature request instead of a bug report? In which case, what about a top-level Bind type?
In other words, a bind statement should be considered by TypeScript to be something like this:
functionsomething<constT>(data: T,message?: string): T{console.log(message)returndata}constfoo=something.bind(undefined,['bar'])// Type of `foo` should be (message?: string) => readonly ['bar']
I don't understand any (technical) reason why that can't be inferred? The information for inference is all there.
Activity
RyanCavanaugh commentedon Feb 18, 2022
For the case where you exhaust all the arguments, there's really no reason to use
bindover a function call.For the case where you don't exhaust all the arguments, we don't have a type that can represent that - there's not a type annotation on
bthat you could write that would make it behave the same way as if itsbindargument(s) were present at the subsequent call site.matthew-dean commentedon Jan 19, 2024
Ryan Cavanaugh (@RyanCavanaugh)
Can you explain why? I'm currently trying to map an object with functions that conform to a specific (albeit generic) interface to function types that retain their generic-ness, and I think it's related to this issue.
In other words, say I have this the following (a super-reduced version of my use-case):
Now say I want to support this pattern:
IMO there's enough information here for TypeScript to properly infer types, there's just no syntax for it. In other words, the problem is here:
TypeScript knows what each function produces in terms of type based on an input type, and that type is known by likewise knowing the input type of the parent function. None of these types are dynamic and they are statically analyzable.
Ryan Cavanaugh (@RyanCavanaugh) - re-thinking it, maybe what you are saying is that there isn't currently a type annotation that can do this, in which case this would be a feature request instead of a bug report? In which case, what about a top-level
Bindtype?matthew-dean commentedon Jan 19, 2024
In other words, a bind statement should be considered by TypeScript to be something like this:
I don't understand any (technical) reason why that can't be inferred? The information for inference is all there.
natew commentedon Aug 30, 2025
This would be really nice to have