Issue
class A<T>
{
some: { [K in keyof T]: (x: T[K]) => T[K] }
}
interface IB {
b: number
}
class B<T extends IB> extends A<T>
{
constructor()
{
super()
/**
* Type '{ b: (x: T["b"]) => number; }'
* is not assignable to type '{ [K in keyof T]: (x: T[K]) => T[K]; }'.
*/
this.some = {
b: x => 2*x
}
}
}
interface IC {
b: number
c: boolean
}
class C<T extends IC> extends B<T>
{
constructor()
{
super()
/**
* Type '{ b: (x: T["b"]) => number; c: (x: T["c"]) => boolean; }'
* is not assignable to type '{ [K in keyof T]: (x: T[K]) => T[K]; }'
*/
this.some = {
b: x => 4*x,
c: x => !x
}
}
}
Hello. I try to set generic constraint in base class "A" with the aim to automatically infer types of "some" properties in derived classes. Unfortunately I can't understand why I get TS errors like I have mentioned above. Everything seems ok from my point of view.
Thank you!
Solution
What should happen if I do this?
const b = new B<{ b: 3, z: string }>();
As you can see, I've passed in the type { b: 3, z: string }
, which is acceptable because it extends { b: number }
. So that means b.some.b
should be of type (x: 3) => 3
. And it also means b.some.z
should be of type (x: string) => string
. Are either of those true of the implementation of B
? No; b.some.b
is actually of type (x: 3) => number
, and b.some.z
is undefined. So it makes sense that the compiler is warning you.
First, let's take care of the z: string
issue. Perhaps in A
you want the properties of some
to be optional, like this:
class A<T>
{
some: {[K in keyof T]?: (x: T[K]) => T[K]}
}
This would allow your B
and C
constructor to initialize some
without having to know about extra properties.
Now, about b: 3
. If you want to allow for someone to extend number
, then the only safe thing you can use is the identity function:
this.some = {};
this.some.b = x => x; // okay
But probably you don't want anyone to pass in anything more specific than number
for the type of b
. Unfortunately there's no great way to prevent it. So, fine, just document that users should only pass in types where b
can be any number
. In this case you need to just tell the compiler not to worry, by asserting that this
is of type B<IB>
:
this.some = {};
(this as B<IB>).some.b = x => 2 * x; // okay
Similar fixes can be done for your C
class. Hope that helps; good luck!
Answered By - jcalz
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.