Dave Cousineau Dave Cousineau - 12 days ago 112
TypeScript Question

Using both "static" and "concrete" generic types

So, I would like to pass both a "type" to work on, as well as a function that operates on a concrete value of that type.

I'm hoping there's a less verbose way to do this, but I'm not sure.

I've figured out that you can say

typeof MyClass
to indicate the "static" type, whereas just
is the concrete type.

Here is an example of the function I would like to call:

function SomeFunction<TStatic, TConcrete, TKey>(
type: TStatic,
keyGetter: (value: TConcrete) => TKey
): (key: TKey) => TConcrete

Calling it looks like:

Module.SomeFunction<typeof SomeType, SomeType, string>(SomeType, value => value.SomeText);

Without the long type list, it can't infer that value is
, but if I specify it explicitly, I can simplify it to this and still get the right types:

Module.SomeFunction(SomeType, (value: SomeType) => value.SomeText);

There is a version of the function that is meant to be simpler when I know that I have a

function SomeFunction2<TStatic, TConcrete extends { Text: string }>(
type: TStatic
): (key: string) => TConcrete {
return Module.SomeFunction<TStatic, TConcrete, string>(type, value => value.Text);

But using it, it can't infer the type of
, so I end up with this as the simplest version if I want the types to be correct:

Module.SomeFunction2<typeof SomeType, SomeType>(SomeType);

What I would really like to say is just this, which would probably work fine in C#:

Module.SomeFunction(SomeType, value => value.Text);

Can I somehow get rid of this distinction between
or do anything else that could let me simplify this further?

Answer Source

From what I understand, TStatic and TConcrete are not independent. What you are passing when you pass the type name is actually the constructor for the class. We can specify the signature for the constructor instead of TStatic this will help the compiler infer the types better.

function SomeFunction<TConcrete, TKey>(
    type: new (...params: any[]) => TConcrete,
    keyGetter: (value: TConcrete) => TKey
): (key: TKey) => TConcrete {
    // ...

SomeFunction(SomeType, (value) => value.SomeText); // Works fine 

Note: I tested on the latest typescript, I see the question is tagged with 1.8, it should work the same, but let me know in the comments if it works for you.