打字稿 - 泛型?或者是其他东西?

时间:2017-08-07 16:08:58

标签: typescript generics interface polymorphism

我遇到了在控制台上产生这些错误的问题。

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];

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

也许另一个想法,没有泛型的话?我不知道......

2 个答案:

答案 0 :(得分:2)

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

isUpdatable的实施是一个问题,因为它假设IsUpdatable上有this.field字段。您可以使用合适的防护装置解决此问题。不要在@ 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];

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

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

答案 1 :(得分:1)

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

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

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