Repository: TypeScript
Issue: Ability to parametrize generic function type
Labels: Suggestion,Awaiting More Feedback

# Suggestion

<!--
  Please fill in each section completely. Thank you!
-->

I would like to propose something along the lines of [$Call](https://flow.org/en/docs/types/utilities/#toc-call) utility type in Flow so it is possible to represents the result of calling a generic function with a concrete parameters.

Alternatively could be an utility type that hoists parameters from generic function type into type itself e.g.

```ts
HoistTypeParams<<I, O>(inn:I[], f:(p:I) => O) => O[]> // => type DerivedType<I, O> = (inn:I[], f:(p:I) => O) => O[]
```

Or yet another alternative might be to add some way to parametrize through `infer` e.g. I would have expected following to work:

```ts
type $Call<T extends (args: any[]) => any, Args extends unknown[]> =
  T extends (...args:Args) => infer O ? O : never
``` 

## 🔍 Search Terms

<!--
  💡 Did you know? TypeScript has over 2,000 open suggestions!
  🔎 Please search thoroughly before logging new feature requests as most common ideas already have a proposal in progress.
  The "Common Feature Requests" section of the FAQ lists many popular requests: https://github.com/Microsoft/TypeScript/wiki/FAQ#common-feature-requests

  Replace the text below:
-->

List of keywords you searched for before creating this issue. Write them down here so that others can find this suggestion more easily and help provide feedback.

## ✅ Viability Checklist

<!--
   Suggestions that don't meet all these criteria are very, very unlikely to be accepted.
   We always recommend reviewing the TypeScript design goals before investing time writing
   a proposal for ideas outside the scope of the project.
-->
My suggestion meets these guidelines:

* [x] This wouldn't be a breaking change in existing TypeScript/JavaScript code
* [x] This wouldn't change the runtime behavior of existing JavaScript code
* [x] This could be implemented without emitting different JS based on the types of the expressions
* [x] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
* [x] This feature would agree with the rest of [TypeScript's Design Goals](https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals).


## ⭐ Suggestion

<!-- A summary of what you'd like to see added or changed -->

## 📃 Motivating Example

<!--
  If you were announcing this feature in a blog post, what's a short explanation that shows
  a developer why this feature improves the language?
-->

Today it seems impossible capture return type of the generic function, even when concrete type of the input is known. Both built-in `ReturnType`  and `infer` based solutions turn generics into a unknowns.

Here is concrete example:

```ts
type Map = <I, O> (list:I[], f:(i:I) => O) => O[]

ReturnType<Map> // unknown[]
Map extends (...args:infer Args) => infer O ? O : never // unknown[]
Call<Map, [number[], (i:number) => string]> // unknown[]

type Call<F, Args extends unknown[]> = F extends (...args:Args) => infer O ? O : never
```

As per https://github.com/microsoft/TypeScript/issues/33185#issuecomment-526969274 it is possible to accomplish this at the implementation level, however I do not believe it is possible to do it at the type level.
 
## 💻 Use Cases

<!--
  What do you want to use this for?
  What shortcomings exist with current approaches?
  What workarounds are you using in the meantime?
-->

Libraries that generate more complex interfaces from a simple ones, something like RPC client for a service with generics becomes impossible here to express:

```ts
type Service<T, Path extends PropertyKey[]=[]> = {
  [K in keyof T]: T[K] extends (...args:infer I) => infer O ? Method<I, O, [...Path, K]> : Service<T[K], [...Path, K]>
}

interface Method<I extends unknown[], O, Path extends PropertyKey[]> {
  <Args extends I> (...args:Args): Promise<O>
  meta: {  path: Path }
}


declare function service <T>(impl:T):Service<T>
const s = service({
  map <I, O>(inn:I[], f:(inn:I) => O):O[] {
    return inn.map(f)
  }
})

s.map([1, 2, 3], (n:number):string => String(number))
//               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// Argument of type '(n: number) => string' is not assignable to parameter of type '(inn: unknown) => unknown'.
//  Types of parameters 'n' and 'inn' are incompatible.
//    Type 'unknown' is not assignable to type 'number'.(2345)
```