在TypeScript中,`declare`关键字何时最简单实用的示例是什么?

7
我阅读了关于TypeScript中declare关键字的文档(例如这里)以及一些博客文章,但仍然不理解它。我想要的是一个最简单用法的清晰示例。
以下是我最初想出用于演示最简单示例的推理过程(可能是不正确的):
在一个.js文件中导出一些东西(未经类型化,因为这是纯JS)。
在一个.ts文件中导入.js文件导出的任何内容。
我需要导出和导入什么(对象?函数?),才能看到TS显示可以通过使用declare解决的错误?

https://github.com/Microsoft/TypeScript/issues/13348 我猜这与编程有关。 - hackape
2
noImplicitAny: true to make ts complain, then fix the error by declare - hackape
1
“docs” 实际上在 https://www.typescriptlang.org/docs/home.html,其中有一个关于声明文件的特定部分:https://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html。非常感谢 basarat 撰写了深入探讨;它非常有帮助,但有时候查看源代码是个好主意。 - Heretic Monkey
@HereticMonkey,我在深入文档中没有看到使用declare关键字的地方。我有遗漏什么吗? - cham
请参见 https://dev59.com/ElcQ5IYBdhLWcg3wFf8-。 - cham
@cham 这就是评论的重点; 在那个时候,basarat的书中没有提到declare(该评论已经超过一年了)。但是在TypeScript自己网站的文档中提到了它。我的建议是去找源头。在这种情况下,declare被明确提到这里 - Heretic Monkey
2个回答

6

你的示例是正确的。例如,你正在使用一个用纯javascript编写的节点模块(没有可用的类型),所以tscompiler会注意到这一点(因为它搜索通常在节点模块或额外的@typings/package中的类型定义)。

但是,你可以通过在tsconfig.json中告诉tscompiler查看文件xyz.d.ts来提供这些类型定义,如下所示:

tsconfig.json
{
  "files": [
    "typings/index.d.ts"
  ]
}

index.d.ts 是汇集所有自定义类型的地方,可能看起来像这样:

index.d.ts
/// <reference path="custom-typings.d.ts" />

custom-typings.d.ts 文件中则包含了实际的类型定义。这也是 declare 关键字发挥作用的地方。

custom-typings.d.ts
declare module "the-untyped-node-module-name" {
    export default class TheNodeModuleClass { 
        static showNotification(string: any): void;
    }
}

现在,TypeScript编译器知道the-untyped-node-module-name中有一个名为TheNodeModuleClass的类,该类具有静态函数showNotification
更多信息请参见TypeScript模块
这是使用关键字declare的一个用例。当然,还有其他用途,如declare vardeclare functiondeclare class等等。

这里有一个简单的代码片段:https://gist.github.com/evian-pring/ce3aa59c01f6fb2b1cec806519fc8f89我可以假设myModule.js是一个纯JS编写的Node模块,我可以通过node_modules添加到我的项目中吗?如果TypeScript可以像那样推断类型,你能举个例子在哪里它不能推断,我必须使用declare吗? - evianpring
@evianpring 这个叫做上下文类型推断,它的工作方式是这样的。然而,它不能用于来自node_modules的外部导入。 - Murat Karagöz

3

Murat Karagöz在上面的回答中指出了正确的方向,而这个回答将提供实际代码,其中包含我们将使用declare的最小示例。

这里是一个非常简单的npm模块:只有一个index.js文件,其中导出了一个具有一个方法的对象。这里没有类型声明,因为它只是JS。

const item = {
    price: 5,
    name: 'item1',
};

export const MyModuleObject = { method: () => item };

这里是一个非常简单的TypeScript npm项目,只依赖于上面链接的JS项目。后者因此是一个没有类型定义的导入npm模块。以下是TS项目中的index.ts文件:

/// <reference path="index.d.ts"/>

import { MyModuleObject } from 'npmModule';

import { LocalModuleObject } from './module';

// Without the "reference path" line, TS complains in line 3 that it could not find a declaration file for 'npmModule'.
// In this case, the import has type any, so TS does not complain about the call below to an inexistent method.
// When we uncomment line 1, we get a TS error on line 8: property 'test' does not exist on type { method: ... }
MyModuleObject.test();


// TS complains that test does not exist on type { method: ... }
// Here we did not need to have a `declare` statement in a type definitions file for TS to know this because here TS is
// using contextual typing:
LocalModuleObject.test();

以下是index.d.ts的代码:
declare module "npmModule" {
    export const Item: {
        price: number,
        name: string
    }

    export const MyModuleObject: {
        method: () => Item
    }
}

./module的代码:

export const LocalModuleObject = { method: () => 10 };

这是为什么要使用declare的一个例子: - 我把这个放在了index.ts的注释中,但让我用更多的话来解释一下。 index.ts正在从一个外部模块(在node_modules中)导入一个对象和从一个本地模块(./module.js)导入另一个对象。两个模块都导出一个对象,上面有一个名为method的方法。 在index.ts中,我在每个对象上调用了一个不存在的方法test
TS在导入本地模块时使用上下文类型推断,因此它知道对象上不存在test。但是在导入外部模块的时候却没有这种情况:这个导入被导入为类型any。因此,TS不会对对不存在的方法test的调用进行投诉。然而,它会抱怨外部模块没有类型定义,这表明隐式的any正在被使用。
我们可以通过定义一个提供外部库类型定义的index.d.ts来解决后面的问题。这就是使用declare module的地方:它声明了模块npmModule导出的内容;npmModule是外部导入。在index.ts中,我们必须添加一行/// <reference path="index.d.ts"/>,以便TS知道在哪里查找类型。

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