I have several types that I am working with that I would like to have inferred at the method call level.
class AbstractModal<TInput, TOutput> {
...
}
type Constructor<T> = new(...args: any[]) => T;
function openModal<TConstructor extends Constructor<AbstractModal<?, ?>>>(
ctor: TConstructor,
initialState?: TInput // TInput type for Constructor of Abstract Modal
): TOutput {
...
}
What I would like to be able to do is have a class defined as:
class TestModalType extends AbstractModal<string /*or another type*/, number /*or another type*/> {
...
}
and be able to call openModal with TestModalType as the generic parameter with the compiler getting upset at me for improper inputs on the initial call.
I have tried reading into these questions but have not found any help:
Typescript strict typing and inferred generic types
Inferring generic types in typescript
The specific problem that I am finding is that a lot of these answers are written for generics with only one generic parameter, where my situation requires a much more in depth example.
You're probably better off drilling into the type you need from
TConstructor, but in order to do that you need to know howTInputandTOutputare used.So let's you start with this:
Now you know that
TInputis used as the first parameter toopen()andTOutputis the return type ofopen().Now you can write
openModallike this:TConstructor extends Constructor<AbstractModal<unknown, unknown>>constrainsTConstructorto aConstructorthat returnsAbstractModal<unknown, unknown>. Theseunknowns will be made more specific by theextendswhen this is called.Parameters<InstanceType<TConstructor>['open']>[0]gets theopenmethod form the instance type that the constructor return, and uses the type of the first parameter.ReturnType<InstanceType<TConstructor>['open']>gets theopenmethod from the instance type that the constructor returns, and uses the return type.And now this should work:
Alternatively, if you want to
inferthe generics directly, you can do that, but it's quite a bit more verbose and cryptic. To do that you have to use a conditional type like:That might look like this:
See Typescript Playground