【问题标题】:How to avoid `Subsequent property declarations must have the same type` with Interfaces如何避免`后续的属性声明必须具有与接口相同的类型`
【发布时间】:2021-02-22 03:42:45
【问题描述】:

我有一些接口,它有一个枚举字段。还想对此接口进行重载,这将在某些特定枚举值的情况下采用更多属性。

举个例子:

enum CellValueType {
    DATE,
    STRING,
    NUMBER
}


export interface TableCell {
    type: CellValueType;
    value: string; // ...
}

export interface TableCell {
    type: CellValueType.DATE;
    format: string; // Should be exist only on the DATE type
    value: string; // ...
}

但是,问题出在使用界面时出现错误:

后续的属性声明必须具有相同的类型。属性“type”必须是“CellValueType”类型,但这里有“CellValueType.DATE”类型。

错误是可以理解的,没有问题。

主要问题是如何以合法的方式避免它。

我知道,我可以使用 type 而不是 interface 并得到这个构造:

export type TableCell = {
    type: CellValueType;
    value: string;
} | {
    type: CellValueType.DATE;
    format: string; 
    value: string;
}

效果很好,但问题是类不能实现联合类型。


未按预期工作,DATE 仍与第一个签名兼容。 要正常工作,需要将type: CellValueType 更改为type: Exclude<CellValueType, CellValueType.DATE>

另外,我可以仅为DATE 枚举类型创建子接口,但也希望避免为每种情况创建接口。

【问题讨论】:

  • 我知道我真的很迂腐,但“类不支持类型作为要实现的东西”并不完全正确。类可以实现一些类型。这里的问题是他们无法实现联合类型。您收到错误“一个类只能实现一个对象类型或具有静态已知成员的对象类型的交集。”

标签: typescript overloading


【解决方案1】:

当您以相同名称定义两个接口时,将执行declaration merge

export interface TableCell {
    type: CellValueType;
    value: string; // ...
}

export interface TableCell {
    type: CellValueType.DATE;
    format: string; // Should be exist only on the DATE type
    value: string; // ...
}

这是您在尝试将 CellValueTypeCellValueType.DATE 合并时遇到的设计行为。引用the handbook:

如果两个接口都声明了同名但类型不同的非函数成员,编译器会报错

现在,当您尝试使用联合来创建所需类型时,您遇到了另一种设计行为 - 因为 CellValueType.DATECellValueType 的成员,所以选择了更广泛的类型(想想看:@ 987654329@和A | B一样):

type isInType = CellValueType.DATE extends CellValueType ? true : false; //true;
type isNotInType = CellValueType extends CellValueType.DATE ? true : false; //false;
type cellType = TableCell["type"]; //CellValueType

除此之外,您为什么不想创建扩展基本类型的接口呢?这是一个完全有效的抽象策略。看看下面的例子(注意as const 断言,否则CellValueType.DATE 的类型将扩大到CellValueType):

export enum CellValueType {
    DATE,
    STRING,
    NUMBER
}

interface TableCell {
    value: string;
}

export interface DateCell extends TableCell {
    type: CellValueType.DATE;
    format: string;
}

export interface NumberCell extends TableCell {
    //number-specific props here
}

export class DateCell implements DateCell {
    format = "yyyy-MM-dd";
    type = CellValueType.DATE as const;
    value = "2021-02-22";
}

Playground

【讨论】:

    猜你喜欢
    • 2022-09-30
    • 2017-11-15
    • 2018-04-18
    • 1970-01-01
    • 1970-01-01
    • 2017-07-14
    • 2019-02-06
    • 2016-11-07
    • 1970-01-01
    相关资源
    最近更新 更多