TypeScript中的“声明类”和“接口”有什么区别


116

在TypeScript中,创建.d.ts源声明文件时,这是更可取的,为什么?

declare class Example {
    public Method(): void; 
}

要么

interface Example {
    Method(): void;
}

我可以说的区别是接口不能具有静态方法,因此您必须为此使用类。两者都不产生任何JS输出,所以也许没关系吗?



我觉得这两种方法都不能真正描述您会使用另一种方法,因为它们都可以在没有JS输出的情况下完成相同的任务。
克里斯(Chris

1
简而言之:使用声明,必须确保该类的实现在运行时存在,而无需使用接口。
哈基姆

Answers:


163

interface当您只想描述对象的形状时。接口从来没有代码生成-它们只是类型系统中的工件。您会发现类的代码生成没有区别,这取决于它是否具有implements子句。

declare class用于当您想要描述将在外部存在的现有类(通常是TypeScript类,但并非总是如此)时(例如,您有两个.ts文件编译为两个.js文件,并且都通过script标记包括了在网页中)。如果您从classusing 继承extends(不管基本类型是a declare class还是Regular class),则编译器将生成所有代码以连接原型链并转发构造函数,而不会。

如果您尝试继承 declare class应该是接口的则将出现运行时错误,因为生成的代码将引用没有运行时表现形式的对象。

相反,如果您只是implement一个本应为的接口,则必须declare class自己重新实现所有成员,并且不会利用可能的基类和函数中的任何代码重用在运行时检查原型链的对象将拒绝您的对象,因为它实际上不是基类的实例。

为了使您真正感到书呆子,如果您具有C ++背景,则可以粗略地想到 interface as typedefdeclare classasextern一个构造函数的声明缺乏严格在此编译单元的定义。

从纯粹的消费方面(编写命令性代码,不添加新类型),interface和之间的唯一区别declare class是您不能new使用接口。但是,如果您打算extend/implement在新的类型中选择一种class,则绝对必须在interface和之间正确选择declare class。其中只有一个会工作。

有两个规则可以很好地为您服务:

  • 该类型的名称是否与new运行时实际存在的构造函数(某些东西可以调用)对齐(例如DateJQueryStatic是否存在)?如果没有,您肯定要interface
  • 我是在处理另一个TypeScript文件中的已编译类,还是足够类似的东西?如果,请使用declare class

您实际上可以在Typescript中新建一个接口。唯一的限制是继承。
Oleg Mihailik 2013年

3
您不能new在接口类型上调用运算符。但是,接口可以具有构造签名,这意味着您可以new在接口类型的值上调用运算符。这与class工作方式大不相同,后者的构造签名位于类型名称本身而不是该类型的表达式上。
Ryan Cavanaugh

如果您选择向接口添加构造函数的途径,则该类应该是该接口的唯一成员,但该类的“静态变量”除外。不要将构造函数的接口与构造的对象的接口结合使用。如果这样做,类型系统将允许使用诸如:new(new x())之类的愚蠢行为,其中x:接口。
杰里米·贝尔

24

您可以实现接口:

class MyClass implements Example {
    Method() {

    }
}

尽管declare class语法真的打算用于添加类型定义为未在打字稿写外部代码-这样的实施是“别处”。


因此,您建议使用声明类来描述不是用TypeScript编写的代码吗?我以为是这种情况,但是在检入的jquery.d.ts文件中,JQueryStatic是通过以下方式实现的接口:声明var $:JQueryStatic我将要声明为类$ {public static ...}
Chris

我能想到的唯一原因是,如果您不希望人们扩展类-使用接口意味着您将必须提供整个实现。
Fenton

说得通。也许那是原因。
克里斯(Chris

那好吧,所以我试图指向另一个JS库中的声明,所以我绝对需要声明。代码在某些库函数(类)上使用了静态方法-到目前为止,我还没有看到在声明文件中表达的静态属性或方法。哦,我应该使用名称空间还是模块?
jenson-button-event

13

通俗地说,declare在使用.ts/ d.ts文件告诉编译器,我们应该期待我们的关键字declaring在那样的环境中存在,即使它不是本文件中定义。然后,这将使我们在使用声明的对象时具有类型安全性,因为Typescript编译器现在知道某些其他组件可以提供该变量。


6

declare和之间的区别interface

宣布:

declare class Example {
    public Method(): void; 
}

在上面的代码中declare,TS编译器知道该类Example已声明。这并不意味着该类已神奇地包含在内。作为程序员,您有责任在声明类(带有declare关键字)时使类可用。

接口:

interface Example {
    Method(): void;
}

一个 interface是一个虚拟构造,仅存在于打字稿中。打字稿编译器仅将其用于类型检查。当代码被编译为javascript时,整个结构将被剥离。打字稿编译器使用接口来检查对象是否具有正确的结构。

例如,当我们具有以下界面时:

interface test {
  foo: number,
  bar: string,
}

我们定义的具有此接口类型的对象需要与该接口完全匹配:

// perfect match has all the properties with the right types, TS compiler will not complain.
  const obj1: test = {   
    foo: 5,
    bar: 'hey',
  }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.