Reputation: 3867
I am trying to wrap my head around infering generics in Typescript, but I can't seem to get a hold on what am I doing wrong. My concrete example would be rather large to leave here, but I've made a small typescript playground link, which illustrates the problem I am facing completely.
Here's the link
Basically, I am trying to infer a generic parameter from a class hierarchy:
type payloadType = {
[key:string]: string
}
type specificPayloadB = {
a: string
}
type specificPayloadC = {
a: string,
b: string
}
class A<TPayload extends payloadType = any> {}
class B<TPayload extends specificPayloadB = specificPayloadB> extends A<TPayload> {}
class C extends B<specificPayloadC> {}
type InferKeys<TType> = TType extends A<infer Keys> ? Keys : never;
class extender<
TType extends A,
TKeys = InferKeys<TType>
> {}
function getClass<TClass extends A>(): extender<TClass> {
return new extender<TClass>();
}
let testExtenderB = getClass<B>();
let testExtenderC = getClass<C>();
The above example, gives no compilation error, but when inspecting the returned variable types with typescript, testExtenderB
is identified as a extender<B<specificPayloadB>, payloadType>
which is correct, but based on the information provided, it could be more correctly identified as a extender<B<specificPayloadB>, specificPayloadB>
.
The same is true in my opinion for the testExtenderC
variable, which is identified as extender<C, payloadType>
instead of it being identified as extender<C, specificPayloadC>
.
Am I doing something wrong here? Is it possible to achieve the correct identification of the variables, without explicitly providing the types to typescript?
Upvotes: 1
Views: 62
Reputation: 249466
Typescript has a structural type system. For object types this means the members of the type are important when determining type compatibility (see FAQ). This means that unused type parameters are a not very significant in the type system. Since you don't use TPayload
in A
the type parameter doesn't have any significance in the type system, so during inference it will end up with it's default value in some cases.
If you add a member, inference works as you expect it to:
class A<TPayload extends payloadType = any> { p!: TPayload }
Upvotes: 2