简体   繁体   English

从打字稿中的基类通用类推断子类属性类型

[英]Infer subclass property type from base class generic in typescript



    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. 我试图在基类“ A”中设置通用约束,以自动推断派生类中“某些”属性的类型。 Unfortunately I can't understand why I get TS errors like I have mentioned above. 不幸的是,我不明白为什么会出现如上所述的TS错误。 Everything seems ok from my point of view. 从我的角度来看,一切似乎都还可以。

Thank you! 谢谢!

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 } . 如您所见,我传入了{ b: 3, z: string } ,这是可以接受的,因为它扩展了{ b: number } So that means b.some.b should be of type (x: 3) => 3 . 所以这意味着b.some.b应该是(x: 3) => 3 And it also means b.some.z should be of type (x: string) => string . 这也意味着b.some.z应该是(x: string) => string Are either of those true of the implementation of B ? B的实现是否是真的? No; 没有; b.some.b is actually of type (x: 3) => number , and b.some.z is undefined. b.some.b实际上是(x: 3) => number ,而b.some.z是未定义的。 So it makes sense that the compiler is warning you. 因此,编译器警告您是有道理的。

First, let's take care of the z: string issue. 首先,让我们处理z: string问题。 Perhaps in A you want the properties of some to be optional, like this: 也许在A您希望其中some的属性是可选的,如下所示:

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. 这将使您的BC构造函数可以初始化some而不必了解额外的属性。

Now, about b: 3 . 现在,关于b: 3 If you want to allow for someone to extend number , then the only safe thing you can use is the identity function: 如果您想允许某人扩展number ,那么唯一可以使用的安全方法就是身份验证功能:

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 . 但可能您不希望任何人传递比b类型更具体的number 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 . 因此,很好地证明,用户只能传递b可以为任何number类型。 In this case you need to just tell the compiler not to worry, by asserting that this is of type B<IB> : 在这种情况下,你只是需要告诉编译器不要担心,通过声称this是类型B<IB>

this.some = {};
(this as B<IB>).some.b = x => 2 * x; // okay

Similar fixes can be done for your C class. 可以为您的C类进行类似的修复。 Hope that helps; 希望能有所帮助; good luck! 祝好运!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM