为什么在 TypeScript 中实现接口时需要重新定义属性?

Why the need to redefine properties when implementing an interface in TypeScript?

我正在研究 classes 和界面。有一件事让我很恼火,这是我第一次处理这类事情,所以请多多包涵..

假设我得到了这个界面:

// IFoo.d.ts
export default interface IFoo {
  foo: string;
  bar: number;
}

当我在 class 中实现它时,我执行以下操作:

// FooModel.ts
import IFoo from './IFoo';

export default class FooModel implements IFoo {
  foo: string;
  bar: number;

  constructor({ foo, bar }: IFoo = { foo: 'hello', bar: 1 }) {
    this.foo = foo;
  }
}

为什么我必须重新实现相同的属性?

这与复制粘贴基本相同,但有严格的约定。另外,我必须每次键入 foobar 总共 6 次才能正确分配默认值 optional 值,基于接口。

还有更有效的方法吗?

编辑;我正在努力实现以下目标:

一个带有属性的class,从中属性可以用于打字稿的检查,像这样: 界面

export default interface FooDTO {
  foo: string;
  bar: number;
}

型号

export interface IFoo {
  foo: string;
  bar: number;
}

export default class FooModel implements IFoo {
  foo: string;
  bar: number;

  constructor({ foo, bar }: IFoo = { foo: 'hello', bar: 1 }) {
    this.foo = foo;
  }
}

控制器

export default class FooController {
   public static async readAll(): Array<FooDTO> {
      // some model stuff which maps query to dto
      return Array<FooDTO>result;
   }
}

您可以使用 abstract class 而不是接口。与接口相同的好处,但可以扩展它:

export abstract class IFoo {
  foo: string;
  bar: number;
}

export default class FooModel extends IFoo {
  constructor({ foo, bar }: IFoo = { foo: 'hello', bar: 1 }) {
    this.foo = foo;
  }
}

如果您定义一个仅包含属性的接口,您基本上定义了一个数据结构类型,然后您可以使用它来键入对象。

例如,您现在可以这样做:

const fooObj: IFoo = { foo: "test", bar: 12 };

在其他编程语言中(例如Java)在接口上定义属性是非法的。接口 "normally" 定义了一种可以与对象交互的方式(意思是可以在其上调用的方法)。但 TypeScript 是个例外。

我认为 "why do I have to implement the same properties all over again" 的规范答案在(越来越过时的)TypeScript spec:

Note that because TypeScript has a structural type system, a class doesn't need to explicitly state that it implements an interface—it suffices for the class to simply contain the appropriate set of instance members. The implements clause of a class provides a mechanism to assert and validate that the class contains the appropriate sets of instance members, but otherwise it has no effect on the class type.

我在上面添加了强调:implements 子句根本不影响 class 类型;它不会向其中添加成员或更改成员的类型。它所做的只是告诉编译器在 class 不符合接口时发出警告。


您可能对 TS 团队负责人 GitHub 问题 microsoft/TypeScript#22815, which suggests that members of implemented interfaces should be copied down into the implementing classes. (The title of the issue is about abstract classes, but the ensuing discussion is not limited to that.) It looks like the sticking point is what to do about optional members (see this comment 感兴趣)。这个问题是一个较旧的问题,但它仍然以 "needs proposal" 标签打开,所以如果你很关心它,你可能想去那里,给它一个 ,甚至可能提供更多关于行为应该是什么的细节处于边缘情况,这样它就不会是一个破坏性的变化。

在该问题中建议 workaround using interface merging 告诉编译器 class 实例接口继承属性:

interface FooModel extends IFoo { } // merge into FooModel
class FooModel {
    constructor({ foo, bar }: IFoo = { foo: 'hello', bar: 1 }) {
        this.foo = foo;
        this.bar = bar;
    }
}

这不那么多余,但可能比仅从接口重新声明属性更令人困惑。


总之,希望对您有所帮助;祝你好运!

Playground link to code