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

Posted

技术标签:

【中文标题】TypeScript 中的“声明类”和“接口”有啥区别【英文标题】:What's the difference between "declare class" and "interface" in TypeScriptTypeScript 中的“声明类”和“接口”有什么区别 【发布时间】:2012-12-30 00:13:01 【问题描述】:

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

declare class Example 
    public Method(): void; 

interface Example 
    Method(): void;

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

【问题讨论】:

见***.com/a/14323673/1704166 我觉得这两种方法都不能真正帮助描述你会使用一个而不是另一个,因为它们都可以在没有 JS 输出的情况下完成同样的事情。 简短:使用声明,您必须确保该类的实现存在于运行时,而您不必使用接口。 【参考方案1】:

TS 中declareinterface 的区别:

声明:

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

【讨论】:

【参考方案2】:

用外行的话来说,declare.ts/d.ts 文件中使用来告诉编译器我们应该期望关键字我们是declaring 存在于那个环境中,即使它没有在目前的文件。这将使我们在使用声明的对象时具有类型安全性,因为 Typescript 编译器现在知道某些其他组件可能会提供该变量。

【讨论】:

如果当前文件中没有定义,我们怎么能找到impl代码呢?【参考方案3】:

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

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

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

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

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

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

两条对你有用的规则:

类型的名称是否与运行时实际存在的构造函数(可用new 调用的东西)对齐(例如Date 是,但JQueryStatic 不是)?如果没有,你肯定想要interface 我是在处理来自另一个 TypeScript 文件的编译类,还是类似的东西?如果,请使用declare class

【讨论】:

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

你可以实现接口:

class MyClass implements Example 
    Method() 

    

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

【讨论】:

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

以上是关于TypeScript 中的“声明类”和“接口”有啥区别的主要内容,如果未能解决你的问题,请参考以下文章

TypeScript 对象定义中的 '!:' 和 '?:' 有啥区别?

TypeScript 中的 function 和 => 有啥区别? [复制]

TypeScript 中的“扩展”和“实现”有啥区别

TypeScript 中的 Import 和 require 有啥不同?

typescript 中的 export 和 public 有啥区别?

TypeScript 中的 private 关键字和 private 字段有啥区别?