ChatGPT解决这个技术问题 Extra ChatGPT

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

在 TypeScript 中,当创建 .d.ts 源声明文件时,哪个更可取,为什么?

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

或者

interface Example {
    Method(): void;
}

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

我认为这两种方法都不能真正帮助描述你会使用一个而不是另一个,因为它们都可以在没有 JS 输出的情况下完成同样的事情。
简而言之:使用声明,您必须确保类的实现存在于运行时,而您不必使用接口。

R
Ryan Cavanaugh

interface 适用于您只想描述对象形状的情况。从来没有为接口生成代码——它们只是类型系统中的一个工件。根据是否有 implements 子句,您将看到类的代码生成没有区别。

declare class 用于当您要描述将在外部存在的 现有 类(通常是 TypeScript 类,但并非总是如此)时(例如,您有两个 .ts 文件编译为两个 .js 文件,两者都通过网页中的 script 标记包含)。如果您使用 extendsclass 继承(无论基类型是 declare class 还是常规 class),编译器将生成所有代码以连接原型链和转发构造函数,并且什么不是。

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

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

如果你有 C++ 背景,你可以粗略地认为 interfacetypedefdeclare class 为构造函数的 extern 声明,该声明在此编译单元中严格缺乏定义。

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

两条规则会很好地为您服务:

类型的名称是否与运行时实际存在的构造函数(可以用 new 调用的东西)对齐(例如 Date 是,但 JQueryStatic 不是)?如果没有,你肯定想要接口

我是在处理来自另一个 TypeScript 文件的编译类,还是类似的东西?如果是,请使用声明类


实际上,您可以在 typescript 中新建一个界面。唯一的限制是继承。
您不能在接口类型上调用 new 运算符。但是,接口可以具有构造签名,这意味着您可以对接口类型的值调用 new 运算符。这与 class 的工作方式非常不同,其中构造签名位于类型名称本身而不是该类型的表达式。
如果您将构造函数添加到接口,它应该是接口的唯一成员,除了类的“静态”。不要将构造函数的接口与构造的对象的接口结合起来。如果你这样做了,那么类型系统就会允许一些愚蠢的东西,比如:new (new x()),其中 x:接口。
F
Fenton

您可以实现接口:

class MyClass implements Example {
    Method() {

    }
}

declare class 语法实际上旨在用于为不是用 TypeScript 编写的外部代码添加类型定义 - 所以实现是“其他地方”。


所以你建议声明类应该用于描述不是用 TypeScript 编写的代码?我会假设是这种情况,但是在签入的 jquery.d.ts 文件中,JQueryStatic 是一个由以下方式实现的接口: declare var $ : JQueryStatic 我会认为这是 declare class $ { public static ... }
我能想到的唯一原因是如果您不希望人们扩展该类 - 使用接口意味着您必须提供整个实现。
说得通。也许这就是原因。
好的,所以我试图指向另一个 JS 库中的声明,所以我肯定需要声明。该代码在一些库函数(类)上使用静态 - 到目前为止,我还没有看到在声明文件中表达的静态属性或方法。哦,我应该使用命名空间还是模块?
n
nikk wong

通俗地说,declare.ts/d.ts 文件中用于告诉编译器我们应该期望关键字 we're declaring 存在于那个环境中,即使它没有在当前文件中定义.这将允许我们在使用声明的对象时具有类型安全性,因为 Typescript 编译器现在知道其他一些组件可能会提供该变量。


如果当前文件中没有定义它,我们怎么能找到 impl 代码在哪里呢?
W
Willem van der Veen

TS中声明和接口的区别:

宣布:

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

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

界面:

interface Example {
    Method(): void;
}

interface 是仅存在于 typescript 中的虚拟构造。 typescript 编译器将它用于类型检查的唯一目的。当代码编译为 javascript 时,整个结构将被删除。 typescript 编译器使用接口来检查对象是否具有正确的结构。

例如当我们有以下接口时:

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',
  }