Using both "static" and "concrete" generic types

71 Views Asked by At

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 MyClass 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 SomeType, 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 Text property:

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 TConcrete, 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);
Module.SomeFunction2(SomeType);

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

1

There are 1 best solutions below

0
On BEST ANSWER

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.