在'export declare class Actions'中,'declare'是什么作用?

248

为什么在语句中我们需要使用 declare 关键词?

export declare class Actions {
    ...
}

5个回答

261

找到了我正在寻找的东西:

var 创建一个新变量,declare 用于告诉 TypeScript 这个变量已经在其他地方被创建。如果使用 declare,则不会添加任何生成的 JavaScript 代码中 - 它只是编译器的提示。

例如,如果您使用定义了 var externalModule 的外部脚本,则需要使用 declare var externalModule 来提示 TypeScript 编译器已经设置好了 externalModule


1
我有一个类似的情况,其中一个外部JS定义了一个变量externalModule和其他变量。为什么在运行时externalModule未定义,但其他一些变量却不是呢? - Alejandro González

115
要理解这个,你首先要理解"declare"关键字。
这里有一个来自Gil Fink's Blog的很好的解释:

TypeScript的declare关键字用于声明可能不是来自TypeScript文件的变量。

For example, lets imagine that we have a library called myLibrary that doesn’t have a TypeScript declaration file and have a namespace called myLibrary in the global namespace. If you want to use that library in your TypeScript code, you can use the following code:

declare var myLibrary;

The type that the TypeScript runtime will give to myLibrary variable is the any type. The problem here is that you won’t have Intellisense for that variable in design time but you will be able to use the library in your code. Another option to have the same behavior without using the declare keyword is just using a variable with the any type:

var myLibrary: any;

Both of the code examples will result in the same JavaScript output but the declare example is more readable and expresses an ambient declaration.


所以在你理解了"declare"关键字之后,回到你找到以下内容的地方:
export declare class Action{
  // ...
}

这个类的真正实现可能在其他地方,也许是一个`.js`文件中。
为了突出评论@apollo的重要性,请检查以下内容:

enter image description here


6
这句话的翻译是:"这两个代码示例将产生相同的JavaScript输出",但这不是正确的。declare var myLibrary不会被转译成任何内容:http://www.typescriptlang.org/play/#code/CYUwxgNghgTiAEEQBd4DMD2GBcBnZMAlgHYDmA3AFBKoBGs8AvPAORQtWWVA - apollo
@Terrance,Gil Flink博客的链接对我来说无法使用(错误建立db连接)。我认为这可能是相同的内容:https://dzone.com/articles/quick-tip-%E2%80%93-typescript-declare - Mark Edington

59

typescript中的declare关键字:

declare 关键字在 TypeScript 中非常有用,可以告诉 TypeScript 编译器某个声明是在别处定义的(可能是写在外部的 JavaScript 文件中或者属于运行时环境的一部分)。

假设我们有一个名为 foo 的变量是在其他地方声明的。当我们尝试引用该变量时,TypeScript 编译器会抛出一个错误:

foo = 'random'; // Error: 'foo' is not defined

我们可以使用 declare 关键字来解决这个问题:

declare var foo: string;
foo = 'random';  // no error anymore

以下是其影响:

  • 当我们尝试在运行时使用变量,却没有任何地方声明foo,可能会出现运行时错误。因此,仅在您知道变量在此处可用时使用declare关键字。
  • 由于我们知道类型,我们(可能)可以访问我们的IDEIntellisense
  • 由于我们知道类型,TypeScript编译器可以在编译时检查类型,并在某些情况下警告我们是否正在使用错误的类型。

25

在这种特定情况下,declare 关键字的作用是:

export declare class Actions {
    ...
}

... 显然是没有用的,我认为 TypeScript 应该考虑将其变成错误(我不知道是否有隐藏的原因)。如果你声明一个类,你将永远不需要导入它。如果你导出一个期望别人导入的类,你不需要声明它。并且因为你正在声明这个类,根据定义,这个类应该可以在不需要导入它的情况下使用。但当你导出声明一个类时,这并不是真实的。你需要导入它才能使用。

简而言之:

export declare class Actions {
    ...
}

等同于

declare class Actions {
    ...
}

3
我认为它们不同,前者需要使用 import,而后者则不需要。 - Clocher Zhong
此外,declare 不会破坏 .d.ts 文件的全局作用域,而 export 会...(declare 不会使文件成为模块) - Daniel

15

declare可以在没有任何导入或导出关键字的情况下自动定义TypeScript需要的声明文件,这是一个有用的功能,可以为遗留模块(npm安装的没有TypeScript定义的包)添加类型。

import/export是使用模块的正确方式,需要手动导入所有内容(逻辑和定义),而这可能有些繁琐。

实际应用中,export declare允许你避免导出所有子元素,例如:

export declare namespace Redux {
    namespace Store {
        interface Definition { ... }
    }
}

可能比以下内容更易读:

export namespace Redux {
    export namespace Store {
        export interface Definition { ... }
    }
}

在这两种情况下,外部导入是相同的(例如:import { Redux } from 'definitions/redux';),我不知道这是否是一个好习惯,但我认为它很整洁!^^

重要的是要记住,向文件添加importexport将使其成为模块,因此declare范围将不再处于全局级别。

附注,有一个错误(issue 16671):如果您在声明中使用const enum(我在redux actions类型中使用它),并且指定了transpileOnly标志(create-react-app-typescript软件包会这样做,所以我知道),则该枚举将不会被内联!你可能会遇到这个问题,也可能不会,但事先了解这一点很有用!


1
模块内部的命名空间,如 export namespace,是 不太好的做法,并且会增加不必要的命名空间。关于 export declare,请看André Pena的回答。 - ford04
亲爱的 @ford04,以防万一,很抱歉,但是关于“不是一个好主意”的第一个参考似乎在2023年已经失效了。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接