在 TypeScript 中声明关键字的目的
Posted
技术标签:
【中文标题】在 TypeScript 中声明关键字的目的【英文标题】:Purpose of declare keyword in TypeScript 【发布时间】:2017-09-06 06:03:34 【问题描述】:declare
关键字的用途是什么?
type Callback = (err: Error | String, data: Array<CalledBackData>) => void;
对比
declare type Callback = (err: Error | String, data:Array<CalledBackData>) => void;
找不到解释 TS 中 declare
关键字用途的文档。
【问题讨论】:
【参考方案1】:TL;DR
declare
用于告诉编译器“这个东西(通常是变量)已经存在,因此可以被其他代码引用,也不需要将这个语句编译成任何javascript”。
常见用例:
您将对网页的引用添加到编译器一无所知的 JavaScript 文件中。也许它是来自其他域(如“foo.com”)的脚本。评估时,脚本将创建一个具有一些有用 API 方法的对象,并将其分配给全局范围内的标识符“fooSdk”。
您希望您的 TypeScript 代码能够调用 fooSdk.doSomething()
,但由于您的编译器不知道 fooSdk
变量存在,您将收到编译错误。
然后您使用declare
关键字作为告诉编译器“相信我,这个变量存在并且具有这种类型”的一种方式。编译器将使用此语句静态检查其他代码,但不会将其转编译为输出中的任何 JavaScript。
declare const fooSdk = doSomething: () => boolean
同样,您可以将 declare 关键字添加到类属性中,以告诉编译器不要发出任何会创建此属性的代码,假设您有自己的代码可以创建编译器不知道的代码或不明白。
您的具体示例有所不同,因为您声明的是类型,而不是变量,类型已经不编译成任何 JavaScript。我不知道是否有任何理由声明一个类型。
【讨论】:
【参考方案2】:考虑这种情况:您有一个 Typescript 项目。然后你决定用 JavaScript 编写一个模块,并在你的 Typescript 代码中使用它。
// getMyName.js
module.exports = function getMyName(name)
return name;
然后你将它导入到你的打字稿代码中
// myCode.ts
import getMyName from 'getMyName'; //getMyName is a JS module not typescript
当导入那个 JS 模块时(allowJs
编译器选项应该设置为false
否则,JS 文件将在没有任何类型定义的情况下被解析),Typescript 的编译器抱怨那个模块根本不是一个模块!
Could not find a declaration file for module '/absolute/path/to/getMyName.ts'
这是因为 Typescript 不能自行解析为 JS 代码。为了避免这个问题,我们应该为我们的 JS 模块提供类型定义(filename.d.ts
)。为此,您可以使用declare
和export
。
// getMyName.d.ts
declare function getMyName(name: string): string;
export default getMyName;
之后,当我们导入我们的 JS 模块时,typescript 不会抱怨任何事情,因为它现在可以访问类型。
【讨论】:
import getMyName from 'getMyName';
是default
导出,也许你的意思是import getMyName from 'getMyName'
;
@AlexanderMills 是的,你是对的。已编辑。【参考方案3】:
来自 Typescript 文档:
Typescript - Working with Other JavaScript Libraries
要描述不是用 TypeScript 编写的库的形状,我们需要声明库公开的 API。因为大多数 JavaScript 库只公开少数***对象,所以命名空间是表示它们的好方法。
我们将未定义实现的声明称为“环境”。 通常这些声明在 .d.ts 文件中定义。如果您熟悉 C/C++,您可以将这些视为 .h 文件。让我们看几个例子。
环境命名空间
流行的库 D3 在名为 d3 的全局对象中定义其功能。因为这个库是通过标签(而不是模块加载器)加载的,所以它的声明使用命名空间来定义它的形状。为了让 TypeScript 编译器看到这个形状,我们使用环境命名空间声明。例如,我们可以这样写:
D3.d.ts(简化摘录)
declare namespace D3 export interface Selectors select: (selector: string): Selection; (element: EventTarget): Selection; ; // (...)
【讨论】:
【参考方案4】:您可以在编写任何实现代码之前使用 declare 告诉编译器类型,TypeScript 会很高兴。
declare function foo(name: string): string
【讨论】:
【参考方案5】:这是一个真实的例子。
我有一个使用 Webpack Hot Middleware 的 TypeScript React 应用程序。 Webpack Hot Middleware 不是用 TypeScript 编写的,而是用老式的 JavaScript 编写的。所以它没有 TypeScript 编译器可以检查的类型声明。
当我运行我的代码时,来自 Webpack Hot Middleware 的对象 module
存在,尽管它是隐藏在我花哨的新 TypeScript React 应用程序中的老式 JavaScript,但我可以对其进行控制台记录。
module
对象也有键,比如module.hot
,键可以有值。但是 TypeScript 设计时编译器(至少在 VSCode 中)在它下面画了一个红色波浪线,上面写着property 'hot' does not exist
。但它确实存在!
为了让 TypeScript 编译器同意,像这样声明它:
declare let module: any
现有的 module
对象,现在有一个 any
类型,这让 TypeScript 编译器现在很高兴,红色波浪消失了,现在我可以继续编译和编写我的其他代码。
如果你去掉关键字declare
,只写let module: any
,它不会编译,而是说'module' already exists
。这就是接受的答案中的“环境”的含义。
【讨论】:
declare
为已经存在的变量指定一个类型,而不是声明一个新变量。
这是否像 C 语言的 extern 关键字,用于声明我们源代码之外的内容并且编译器无法访问它?只有链接器稍后会链接它们。因此 extern 将启用否则会产生错误的代码编译【参考方案6】:
导入一些没有声明类型文件的库时使用的delcare关键字,例如*.d.ts
之后 vs eslint 不检查语法和上下文,会让你通过,如果你默认使用 tsc 编译器,那是声明删除语法错误的好理由
【讨论】:
这是不正确的; declare 关键字不是为了那个目的,而是在那个场景中使用。以上是关于在 TypeScript 中声明关键字的目的的主要内容,如果未能解决你的问题,请参考以下文章