类与接口在打字稿中的类型

时间:2019-12-17 01:24:12

标签: typescript oop typescript-typings

我最近遇到了这个问题,我不确定是使用接口还是使用类来定义特定类型。

  

注意:这个问题不是 ,而是询问classinterface

之间的区别

例如,给定此类和接口

interface IMyClass {
  foo: string;
  bar: () => string;
}

class MyClass implements IMyClass {
  foo = 'foo';
  bar() {
    return 'bar';
  }
}

我将使用类或接口作为函数参数中的类型。

选项A-使用 class 作为类型

function identityByClass(value: MyClass): MyClass {
  return value;
}

选项B-使用界面作为类型

function identityByInterface(value: IMyClass): IMyClass {
  return value;
}

从我的角度来看,我认为两者都不错,但我更喜欢使用该类来避免同步接口上的所有方法/属性。我认为界面只是类必须遵守的模板/合同。

但是,就我而言,大多数情况下,该类通常会添加更多不在接口定义上的方法/属性。

在这种情况下,下面是此类。

class MyClassPlus implements IMyClass {
  foo = 'foo';
  bar() {
    return 'bar';
  }
  doMore() {
    console.log('more stuff here!');
  }
}

在这种情况下,我不能再将两者互换使用。

任何指向最佳做法的链接也将很不错。谢谢。

2 个答案:

答案 0 :(得分:1)

使用接口定义类型所获得的一个好处是,编译后就消除了接口本身。这样可以减少应用程序的整体膨胀,这是在这种情况下使用接口的有力依据。

您可能会发现这很有用:https://jameshenry.blog/typescript-classes-vs-interfaces/

答案 1 :(得分:1)

  

我不确定是否要使用接口或类来定义特定类型。

两个类和接口都创建一个类型,因此原则上它们可以互换使用。正如您所指出的,接口就像公共合同,而类则实现此合同。

但请考虑以下情况:

  1. 如果您需要更改MyClass,怎么办?
  2. MyClass通过揭示这是一个类类型来公开更多实现细节。

重构的MyClass的新类型签名可能不再有效,不能用作函数identityByClass中的函数参数。因此,您需要重构所有使用者-嗯...使用separate type interface,这种情况不太可能发生。

第2点的示例:客户可以想到获取MyClass的静态属性的想法(接口通过隐藏实现细节而使其不明显):

class MyClass {
  static baz(){}
  foo = 'foo';
}

function identityByClass(value: MyClass) {
  console.log((value.constructor as any).baz) // function baz()
  // there we go. do something with static baz() method of MyClass
}

顺便说一句:它不仅涉及接口和类类型。 Type aliases提供强大的功能,例如映射或条件类型,并集等,并且大多数时候都可用于支持接口。

  

但是,就我而言,大多数情况下,该类通常会添加更多不在接口定义上的方法/属性。

您不需要同步类和接口类型。为了保持DRY,我想到了两个选择:

  1. Interfaces can extend class type definitions,以消除多余的声明。
  2. 仅选择使用者需要的那些类属性。

与第二个观点相比,我更倾向于第二点,因为它遵循Information HidingLoose coupling之类的设计原则。示例:

class MyClass {
  foo = 'foo';
  tooMuchDetail = "hide me"
}

// just pick, what we need (foo here)
type OnlyNeededProps = Pick<MyClass, "foo">

function doSomethingWithFoo(a: OnlyNeededProps) {
  a.foo
  a.tooMuchDetail // error, unknown (OK)
}

doSomethingWithFoo(new MyClass())