1

Is there any way to convert between these two kinds of generic function:

type Foo=<S>(a: S) => S
type FooReturnType = ReturnType<Foo>          // unknown

type Bar<S> = {
  (a: S): S
}
type BarReturnType = ReturnType<Bar<string>>  // string

I.e. convert from type Foo to type Bar, and Bar to Foo?

When one defines a function with generics is takes the form of type Foo. However when passing the return type type around, it is much more useful if it takes the form of Bar.

One solution is to define the return type separately, using generics, and then to use that in one's function. However this splits one's code, and makes it messier.

4
  • I am not sure I understand what do you mean Commented Feb 10, 2023 at 9:14
  • @captain-yossarian when one defines a generic function e.g. function<S>(a:S){ return S} it always takes the form <S>(a: S) => S and if one wants to use the generic return type, there is no simply way to access it via e.g. Foo<string> Commented Feb 10, 2023 at 10:28
  • 1
    This isn't directly possible in TypeScript at the type level; to get close to this you need to drop down to the value level and use instantiation expressions or higher order inference from generic functions, as shown in this playground link. Does that fully address your question? If so I could write up an answer; if not, what am I missing? Commented Feb 10, 2023 at 16:05
  • @jcalz, thank you - that's the solution! I didn't know one could do typeof foo<S>. Thank you again. Commented Feb 12, 2023 at 1:07

1 Answer 1

1

There is currently no way purely at the type level to convert between generic types and generic call signatures. The scope of the generic type parameters are different, and although there is a logical relationship between them, this relationship cannot be properly expressed in the type system. For that to happen we'd probably need true higher kinded types as requested in microsoft/TypeScript#1213, or possibly generic values as requested in microsoft/TypeScript#17574, or possibly existentially quantified generics as requested in microsoft/TypeScript#14466. And we don't have direct support for any of those (as of TS5.0).


The language does have some limited ability to represent this sort of thing, but it all involves dropping down to the value level. That is, you need to have a value of the relevant type, and then you can perform some operation on that value to compute something that has the type you want. Depending on your use cases, this sort of value-level code might be sufficient.

For example, instantiation expressions let you convert a specific type representing a generic call signature (like Foo) into a generic type representing a specific call signature (like Bar), but you need a value of type Foo (or you need to at least pretend to have such a value):

type Foo = <S>(a: S) => S
type Bar<S> = (a: S) => S 

declare const foo: Foo;
type BarFromFoo<S> = typeof foo<S>; // instantiation expression
// type BarFromFoo<S> = (a: S) => S

And you can use higher order type inference from generic types to take a generic type like Bar<S> and convert it to Foo, but you need to have (or pretend to have) a value of a type related to Bar:

declare const bar: <S>() => Bar<S>;

And you need to have a function that shuttles around arguments and return types:

declare const funWithFunctions: <A extends any[], B extends any[], R>(
    f: (...a: A) => (...b: B) => R
) => (...ab: [...A, ...B]) => R // higher order generic function inference

And then operate on the former with the latter and get its type:

const fooFromBar = funWithFunctions(bar)
type FooFromBar = typeof fooFromBar;
// type FooFromBar = <S>(a: S) => S

Playground link to code

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.