【问题标题】:From generic to specific type从通用类型到特定类型
【发布时间】:2018-01-10 08:45:39
【问题描述】:

我想扩展一个类并将其动态类型折叠为特定类型(从TPerson)。

我为此示例创建了虚拟类:

class Person {
    constructor(public name: string){}
}

class Base<T> {
    reference?: T
    name: string

    constructor(name: string) {
        //  Assign properties from T to this
        this.name = name
    }

    static fromObject<T extends { name: string }>(object: T){
        const base = new Base(object.name)
        base.reference = object
        return base
    }
}

class Master<T> extends Base<T> {

    static fromBase<T>(base: Base<T>){
        const master = new Master(base.name)
        master.reference = base.reference
        return master
    }

    static fromObject<T extends { name: string }>(object: T){
        return Master.fromBase(Base.fromObject(object))
    }
}

class PersonMaster extends Master<Person>  {

    constructor(person: Person){
        super(person.name)
        this.reference = person
    }

    static fromBase(base: Base<Person>){
        return new PersonMaster(base)
    }
}

编译器返回此错误:

类静态端 typeof PersonMaster 错误地扩展了基类静态端 typeof MasterfromBase 的属性类型不兼容。类型 (base: Base&lt;Person&gt;) =&gt; PersonMaster 不可分配给类型 &lt;T&gt;(base: Base&lt;T&gt;) =&gt; Master&lt;{}&gt;basebase 的参数类型不兼容。类型 Base&lt;T&gt; 不可分配给 Base&lt;Person&gt;。类型 T 不能分配给类型 Person

【问题讨论】:

    标签: typescript generics


    【解决方案1】:

    MasterfromBase 的声明是通用的:

    static fromBase<T>(base: Base<T>)
    

    这是通用量化:它说fromBase 应该适用于所有T。因此,当您尝试将Base&lt;Person&gt; 的参数特化为PersonMaster 时,类型检查器正确地抱怨fromBase 的此实现不适用于所有Ts,仅适用于Person。覆盖方法的类型参数必须与声明方法的类型参数匹配。

    换句话说,fromBase 中声明的T 与封闭范围中的T 完全不同。它恰好掩盖了这个名字。如果两个类型参数有不同的名称,可能会更容易理解:

    class Master<T> extends Base<T> {
        static fromBase<U>(base: Base<U>) { /* ... */ }
    }
    

    在这种情况下,我怀疑您打算使用来自Master 的(刚性T,而不是新的(更高级别T .可悲的是,正如您在评论中指出的那样,您不能使用静态方法来执行此操作,因为 TypeScript 不支持它。 (我还没有找到任何关于语言设计者为什么做出这个决定的文档。我个人想不出任何好的理由,尽管我确信有一个。)所以你必须将你的方法转移到其他对象的实例方法:

    interface MasterFactory<T> {
        fromBase(base: Base<T>): Master<T>
    }
    class PersonMasterFactory implements MasterFactory<Person> {
        fromBase(base: Base<Person>): Master<Person> { /* ... */ }
    }
    let personMasterFactory = new PersonMasterFactory();
    

    采用这种设计,本应转到PersonMaster.fromBase 的呼叫将转到personMasterFactory.fromBase

    首先,重写静态方法是一件很奇怪的事情。方法覆盖是关于根据接收者的类型动态调度调用,但是(至少在概念上)静态方法没有接收者。线索就在名称中:动态调度静态方法没有意义!

    【讨论】:

    • 谢谢,下次使用时我会考虑到这一点,但如果我不在函数定义中指定T,它会给我Static members cannot reference class type parameters. 错误
    • 我不知道静态成员。似乎是 TypeScript 设计师的疏忽,但我确信这是有充分理由的。无论如何,我认为你有一个设计问题:覆盖静态成员是一件很奇怪的事情。覆盖是一种固有的动态行为,因此如果需要覆盖某些内容,您可能应该将其设为实例属性 - 换句话说,提取 MasterFactory
    • 我应该在 typescript GitHub 上提出问题吗?为什么重写静态方法很奇怪,为什么要使用工厂呢?并编辑您的答案,因为不幸的是它不正确。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-19
    • 2021-04-02
    • 1970-01-01
    相关资源
    最近更新 更多