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 howTInput
andTOutput
are used.So let's you start with this:
Now you know that
TInput
is used as the first parameter toopen()
andTOutput
is the return type ofopen()
.Now you can write
openModal
like this:TConstructor extends Constructor<AbstractModal<unknown, unknown>>
constrainsTConstructor
to aConstructor
that returnsAbstractModal<unknown, unknown>
. Theseunknown
s will be made more specific by theextends
when this is called.Parameters<InstanceType<TConstructor>['open']>[0]
gets theopen
method form the instance type that the constructor return, and uses the type of the first parameter.ReturnType<InstanceType<TConstructor>['open']>
gets theopen
method from the instance type that the constructor returns, and uses the return type.And now this should work:
Alternatively, if you want to
infer
the 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