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
Ability to replace return type of function type #39594
I would like to be able to replace just the return type of a function with a utility type, like WithReturn<F, R>.
I'm not 100% sure this is needed, but it seems like using (...args: Parameters<F>) => R is not sufficient because any generics in F are lost.
Use Cases
My use case is a utility function that captures the arguments to a function to allow it to be called later.
Examples
exportconstdefer=<Fextends(...args: any)=>any>(f: F)=>((
...args: Parameters<F>)=>({function: f,
args,}))/* as any as F */;constmap=<T,R>(array: Array<T>,f: (i: T)=>R)=>array.map(f);constdeferredMap=defer(map);letr=deferredMap([1],(i: number)=>2);// error now: Type 'unknown' is not assignable to type 'number'.(2345)r.args;// error if defer returns F
Right now there's an error because the generics to map() are lost when using Parameters.
Very useful feature. This is the only way to implement flow function with unlimited parameters count, for example. We just need to recursively compute return type of the functions chain passed (this can be done using #38182) and then replace return type of the first function with it. There're very much cases of functions wrapping other functions and changing its return types. Ryan Cavanaugh (@RyanCavanaugh), You really should pay attention to this proposal.
This would indeed be a very useful feature! I think it might be incredibly useful to have a syntax for mapping over the entirety of a function type including type parameters and return type, similar to a mapping over object/tuple types. Does that sound useful to others in the thread?
interfaceCacheEntry<T>{value: TcachedAt: Date}// This is where I want to swap the return typeexportfunctioncreateCachedFactory<TFnextends(...args: any)=>any>(factory: TFn){constcache: Record<string,CacheEntry<ReturnType<TFn>>>={}return(...args: Parameters<TFn>)=>{constcacheKey=JSON.stringify(args)returncache[cacheKey]??={value: factory(...args),cachedAt: newDate()}}}interfaceFoo1{bar1: number}exportconstmakeFoo1=createCachedFactory((bar1: number): Foo1=>({ bar1 }))interfaceFoo2<T>{bar2: T}// This should be typed as: <T>(bar2: T) => CacheEntry<Foo2<T>>exportconstmakeFoo2=createCachedFactory(<T>(bar2: T): Foo2<T>=>({ bar2 }))
Here are some proposed solutions (in order of desirability, with most desirable at the top):
// Proposal 1: TypeScript intelligently determines that the inferred parameters// are used in the new parameters and the inferred return type is used in the// new return type, so the new function type should be generic, if the original// function type is genericdeclarefunctioncreateCachedFactory<TFnextends(...args: any)=>any>(factory: TFn):
TFnextends(...args: infer TParams)=> infer TRet
? (...args: TParams)=>CacheEntry<TRet>
: (...args: Parameters<TFn>)=>CacheEntry<ReturnType<TFn>>// Proposal 2: similar to proposal 1, but TypeScript is using the utility types// Parameters and ReturnType instead of directly inferred typesdeclarefunctioncreateCachedFactory<TFnextends(...args: any)=>any>(factory: TFn):
(...args: Parameters<TFn>)=>CacheEntry<ReturnType<TFn>>// Proposal 3: a special type is used to add handling of generic functions. I am// not sure if this is really much easier to implement than proposal 1 or 2 as// there still needs to be some guardrails to handle misusing or not// inferring/using TParams and TRetdeclarefunctioncreateCachedFactory<TFnextends(...args: any)=>any>(factory: TFn):
TFnextendsGenericFunction<infer TParams, infer TRet>
? (...args: TParams)=>CacheEntry<TRet>
: (...args: Parameters<TFn>)=>CacheEntry<ReturnType<TFn>>// Proposal 4: similar to proposal 3, but with additional special types for the// parameters and return typesdeclarefunctioncreateCachedFactory<TFnextends(...args: any)=>any>(factory: TFn):
TFnextendsGenericFunction
? (...args: GenericParameters<TFn>)=>CacheEntry<GenericReturnType<TFn>>
: (...args: Parameters<TFn>)=>CacheEntry<ReturnType<TFn>>
Activity
awerlogus commentedon Aug 17, 2020
Very useful feature. This is the only way to implement
flowfunction with unlimited parameters count, for example. We just need to recursively compute return type of the functions chain passed (this can be done using #38182) and then replace return type of the first function with it. There're very much cases of functions wrapping other functions and changing its return types. Ryan Cavanaugh (@RyanCavanaugh), You really should pay attention to this proposal.TylorS commentedon Aug 22, 2020
This would indeed be a very useful feature! I think it might be incredibly useful to have a syntax for mapping over the entirety of a function type including type parameters and return type, similar to a mapping over object/tuple types. Does that sound useful to others in the thread?
awerlogus commentedon Aug 22, 2020
Tylor Steinberger (@TylorS) We need to look at the code examples
saltman424 commentedon Nov 15, 2024
Just to add some life to this four-year-old issue. I have a use case of factories of factories. For example:
Playground
Here are some proposed solutions (in order of desirability, with most desirable at the top):
linonetwo commentedon Nov 15, 2024
Another related repo https://www.typescriptlang.org/play?jsx=1&ts=4.1.3#code/PTAEElQFwCwSwHYGtSIGYFMBOoBKGoBXLBANQEMAbQjUAIyw3JVlvIGMirRGiToAngAdaAezSgm7GKAAKAKHlRhtAIIBnAQnZw0A2VlEAPAQFYAPAHkscAOaIqB4wIB8oALygA3vNCgA2rKoCKBIGALioNZ2DpROJgC6AFxRNvYIjoYmgQmSRlAYCAAm6qAAFAB0VeRYtoQAtoVQ6gD6KeQIAgCUHm7o2HgExGRUNL5+oAD85VUVNXWNCM1tcjXkjQVY6lZpsfECOS497m5O9XDqGOb4fCPUGC7jfimEyAiiAO4IANzyAL6-eTAABUwN8wNAACFCLZUKUYNgMElwcBFOxRAh1FBQLZcR5yl4-qByKUNFodHp9hZwABhDEFfIAZWwADc4OwHl0KrYCGUAOSFNmGBCLKCkbDqOAY9R8rqAxQgsGgCGQjCUT7ExiCESgNCiHC0+kYJms9kYFHyY1CfXYxCbNAcWjgWTkWClHx+XAAUVUNIAKi1ZKo-QAJFJYmwIWy-Py0ywAOUDwbDoAjiGj-yBoPBoFULPIcEo5DolFo+fupWLokI2KwrwQ6ckCCFGNFFqtNuC9sdEDpmKgHWa3nGFwAIhgWWrREJRSk6KJRKWOjHQEIi1A9Vh6uGoJGM35BXBhaLxVspZiUl5iSkAMwAGnoKQALKAAZnLUZrVhbUtsA6Ob2Rr5HkBTFKUzqujA6gPoa-aDu6fwKtmyqgAAsh05A8qA6JwUsoDljQoCUHAYSgAABmOE5TjOTRkcSxSgPUHQCJIlCXA+6iiKAESENhHShO8HzEvONbCdWtb1o2h7Hk0wSMQWIQdEUPCFEUiI4EIhgcuopRMJKlACBU7afp2dp-j2sEMlAzJYGyAEejiBDmAA0iBqmlGEEQSJZxpQC4ZSeSkzldCkPn5P4zkJL8fxAA