两个独立的接口或一个由另一个接口扩展:干或不干

Two separate interfaces or one extended by another: To DRY or not to DRY

从无类型语言 JavaScript 到类型化 TypeScript,您开始使用接口。有时关于如何构建应用程序的简单问题会变成与我的队友的激烈讨论:)我们正在构建 React+Redux+TypeScript 应用程序,我们有两个 "things": Application state for User(Redux store 中的用户表示)和 User 的 API 响应(描述 JSON API 响应),它们都有接口,截至目前,它们是相同的,但这可能会改变。映射是使用一些自定义函数完成的:

const mapUserApiToState = (user: IUserAPI): IUserState => { ... }

由于这是一个早期阶段,而且会有很多接口要编写,所以我们可以采用两三个路径:

(1) 将每个界面分离到各自的文件中。但是你必须为每个接口重复相同的属性,这违反了 DRY 原则。

// file1.ts
interface IUserState {
  id: number;
  name: string;
  kids: number;
}

// file2.ts
interface IUserAPI {
  id: number;
  name: string;
  kids: number;
}

(2) 只需将一个接口扩展到另一个。这样你就不需要重复自己,但这意味着一个接口扩展了另一个接口,而实际上它们看起来相同,但用于完全不同的目的

// file1.ts
interface IUserState {
  id: number;
  name: string;
  kids: number;
}

// file2.ts
interface IUserAPI extends IUser {}

(3) 创建第三个抽象接口。但这又创建了一个带有不应该使用的接口的文件(你会在哪里使用 IUser?)

// file1.ts
interface IUser {
  id: number;
  name: string;
  kids: number;
}

// file2.ts
interface IUserState extends IUser {}

// file3.ts
interface IUserAPI extends IUser {}

(4) 不要在琐碎的事情上浪费时间。 YAGNI.

经过多次辩论,我们决定使用 TypeScript 的 typeof 功能并决定复制变量枚举,但我们没有使用两个接口,而是使用默认应用程序状态的值作为我们类型的定义。所以它和两个接口不一样,但确实更有意义。

interface IUserAPI {
    id: number;
    name: string;
    kids: number;
}

const defaultUserState = {
    id: 0,
    name: "Unnamed",
    kids: 0
}

type IUserState = typeof defaultUserState