Defining GenericOf<T> to extract generic M used by another generic T

Viewed 50

I am attempting to define a function that uses generics in the following way:

public fetch<M extends Model, T extends Service<M> = Service<M>>(service: string): T {
  return this.services[service]<M, T>();
}

where Service requires a generic M extends Model. Now, T is an extension of Service that has a generic that extends Model, for instance UserService<UserModel>.

The above works but question is, is there a way to define the above function fetch so that I do not have to specify M explicitly, for instance:

// This is obviously bogus but I think it gets my point across.
public fetch<T extends Service<GenericOf<T>>>(service: string): T {
  return this.services[service]<GenericOf<T>, T>();
}

I have looked around and imagined it would be a Utility Type of sort that would allow me to get the generic used in a type. So would I be able to define an operator that extracts the generic of the type provided to it type M = GenericOf<UserService>?

1 Answers

You cannot have a fully-general GenericOf type like how you want; consider this example:

type Foo<T> = {foo: T, bar: number}
type Bar<T> = {foo: string, bar: T}

type FooTest = GenericOf<Foo<string>>
type BarTest = GenericOf<Bar<number>>

Here, FooTest would have to be string and BarTest would have to be number; but both are defined as GenericOf<...> of the same type {foo: string, bar: number}. Clearly any GenericOf helper type cannot give two different results for the same input.

That said, you can define something like GenericOf if you only need to use it with a specific generic type:

type FooGenericOf<T extends Foo<any>> = T extends Foo<infer U> ? U : never
type BarGenericOf<T extends Bar<any>> = T extends Bar<infer U> ? U : never

Then FooGenericOf<Foo<string>> will be string and BarGenericOf<Bar<number>> will be number.

Related