【问题标题】:Typescript - Generics? Or something else?打字稿 - 泛型?或者是其他东西?
【发布时间】:2018-01-15 00:29:25
【问题描述】:

我有一个问题会在控制台上产生这些错误。

Type 'WebFieldB' does not satisfy the constraint 'WebFieldA & WebFieldB'.

服务器生成了2个类,不应该改变。

class WebFieldA {
  readonly Name: string;
  readonly IsUpdatable: boolean;
}

class WebFieldB {
  readonly Name: string;
}

这是我的代码:

interface IFieldInfo {
    name: string;
    isUpdatable: boolean;
}

class SomeField<T extends  WebFieldA & WebFieldB> implements IFieldInfo {
    field: T;

    constructor(pField: T) {
        this.field = pField;
    }

    get name(): string {
        return this.field.Name;
    };

    get isUpdatable(): boolean {
        return this.field.IsUpdatable || false;
    }
}

let field1: IFieldInfo = new SomeField<WebFieldA>({Name: 'fieldA', IsUpdatable: false});
let field2: IFieldInfo = new SomeField<WebFieldB>({Name: 'fieldB'}); **// Here for WebFieldB is error**
let myCollection: Array<IFieldInfo> = [field1, field2];

我期待什么? 我想要一个由两种类型的对象构建的IFieldInfo 集合:WebFieldAWebFieldB(WFB 没有isUpdatable 属性!)。不幸的是,我只能修改我的代码,而不是 WebFieldAWebFieldB

也许还有另一个想法,在没有泛型的情况下做到这一点?我不知道...

【问题讨论】:

    标签: typescript generics interface polymorphism


    【解决方案1】:

    我不确定这是否会实现您的最终目标,但如果您像这样更改代码,您可以编译代码。

    class SomeField<T extends (WebFieldA | WebFieldB)> implements IFieldInfo {
        // ....
        get isUpdatable(): boolean {
            return this.field instanceof WebFieldA && this.field.IsUpdatable;
        }
    }
    

    我做了一些更改。首先,我使用| 而不是&amp; 来表示T 只需要扩展其中一个类,而不需要同时扩展两者。然后,我在isUpdatable 属性getter 中添加了一个类型保护,以确保仅当TWebFieldA 或其子类时才读取该属性。

    【讨论】:

    • 您可能应该将类型保护更改为直接(结构上)测试'this.field.IsUpdatable'的东西,因为示例代码正在测试不是instanceofWebFieldA或的对象WebFieldB 类。
    • @str1ct,小心,在运行时针对(new SomeField&lt;WebFieldA&gt;({Name: 'fieldA', IsUpdatable: true})).isUpdatable 进行测试
    【解决方案2】:

    正如@recursive 建议的那样,您的输入字段将是WebFieldA WebFieldB,而不是WebFieldA WebFieldB。这意味着您应该使用WebFieldA | WebFieldB 而不是WebFieldA &amp; WebFieldB

    isUpdatable 的实现是一个问题,因为它假设this.field 上有一个IsUpdatable 字段。您可以使用适当的类型保护来解决此问题。不要像@recursive 的答案那样使用instanceof,除非您只打算传递WebFieldAWebFieldB 的实际实例,而不是具有相同属性的对象。我看到你的示例代码使用的是普通对象,所以你不应该使用instanceof

    我喜欢使用的一个类型保护是以下有用的函数(它可能应该在库中):

    function hasKey<K extends string>(key: K, obj: any): obj is {[P in K]: any} {
      return key in obj;
    }
    

    这只是确保一个对象有一个特定的键。然后你就可以实现isUpdatable 而不会出现这样的错误:

    get isUpdatable(): boolean {
        return hasKey('IsUpdatable',this.field) ? this.field.IsUpdatable : false;
    }
    

    不过,这有点多。让我们回避类型保护问题。请注意,从结构上讲,WebFieldA | WebFieldB 非常类似于像

    这样的单个接口
    interface SomeWebField {
      readonly Name: string;
      readonly IsUpdatable?: boolean; // optional
    }
    

    并注意您根本没有使用泛型。您不妨将泛型替换为SomeWebField

    class SomeField implements IFieldInfo {
        field: SomeWebField;
    
        constructor(pField: SomeWebField) {
            this.field = pField;
        }
    
        get name(): string {
            return this.field.Name;
        };
    
        get isUpdatable(): boolean {
            return this.field.IsUpdatable || false;
        }
    }
    
    let field1: IFieldInfo = new SomeField({Name: 'fieldA', IsUpdatable: false});
    let field2: IFieldInfo = new SomeField({Name: 'fieldB'}); 
    let myCollection: Array<IFieldInfo> = [field1, field2];
    

    假设您没有其他未提及的隐藏用例,以上内容应该可以满足您的需求而无需泛型。

    希望有所帮助;祝你好运!

    【讨论】:

    • 当然。这有帮助。我想知道是否有另一种方法可以做到这一点。这很容易......我认为这个话题已经筋疲力尽了。非常感谢大家:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-28
    • 1970-01-01
    • 2019-11-19
    • 2018-06-13
    相关资源
    最近更新 更多