TypeScript 如何使用命名空间(超详细) 这篇文章中详细介绍了TypeScript的命名空间的使用,并且查看使用命名空间时生成的 JavaScript 代码
在这里,我们将介绍命名空间有用的场景之一:为外部库创建模块声明。 为此,我们将在 TypeScript 项目中编写一个新文件来声明类型,然后更改 tsconfig.json 文件以使 TypeScript 编译器识别类型。
%> 注意 :要执行后续步骤,需要一个可以访问文件系统的 TypeScript 环境。 如果您使用的是 TypeScript Playground,则可以通过单击顶部菜单中的导出,然后在 CodeSandbox 中打开,将现有代码导出到 CodeSandbox 项目。 这将允许我们创建新文件并编辑 tsconfig.json 文件。
并非 npm 注册表中的每个可用包都绑定了自己的 TypeScript 模块声明。这意味着在项目中安装包时,可能会遇到与包缺少类型声明相关的编译错误,或者必须使用所有类型都设置为 any 的库。根据您使用 TypeScript 的严格程度,这可能是不希望的结果。
希望这个包将有一个由 DefinetelyTyped 社区创建的 @types
包,允许安装包并获得该库的工作类型。但是,情况并非总是如此,有时我们必须处理一个不绑定其自己的类型模块声明的库。在这种情况下,如果你想保持你的代码完全类型安全,你必须自己创建模块声明。
例如,假设正在使用一个名为 example-vector3 的向量库,它使用单个方法 add 导出单个类 Vector3。此方法用于将两个 Vector3 向量相加。
库中的代码可能类似于以下内容:
export class Vector3 {
super(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
}
add(vec) {
let x = this.x + vector.x;
let y = this.y + vector.y;
let z = this.z + vector.z;
let newVector = new Vector3(x, y, z);
return newVector
}
}
这导出了一个类,该类创建具有 x、y 和 z 属性的向量,用于表示向量的坐标分量。
接下来,看一下使用假设库的示例代码:
index.ts
import { Vector3 } from "example-vector3";
const v1 = new Vector3(1, 2, 3);
const v2 = new Vector3(1, 2, 3);
const v3 = v1.add(v2);
example-vector3 库没有与它自己的类型声明绑定在一起,因此 TypeScript 编译器将给出错误 2307:
Cannot find module 'example-vector3' or its corresponding type declarations. ts(2307)
为了解决这个问题,我们现在将为这个包创建一个类型声明文件。 首先,创建一个名为 types/example-vector3/index.d.ts 的新文件,然后在自己喜欢的编辑器中打开它。 在此文件中写入以下代码:
types/example-vector3/index.d.ts
declare module "example-vector3" {
export = vector3;
namespace vector3 {
}
}
在此代码中,我们正在为 example-vector3 模块创建类型声明。 代码的第一部分是声明 module 块本身。 TypeScript 编译器将解析这个块并解释其中的所有内容,就好像它是模块本身的类型表示一样。 这意味着我们在此处声明的任何内容,TypeScript 都将用于推断模块的类型。 现在,说这个模块导出了一个名为 vector3 的命名空间,该命名空间目前是空的。
保存并退出此文件。
TypeScript 编译器当前不知道我们的声明文件,因此必须将其包含在 tsconfig.json 中。 为此,通过将 types 属性添加到 compilerOptions 选项来编辑项目 tsconfig.json:
tsconfig.json
{
"compilerOptions": {
...
"types": ["./types/example-vector3/index.d.ts"]
}
}
现在,如果返回原始代码,我们将看到错误已经变了。 TypeScript 编译器现在给出错误 2305:
Module '"example-vector3"' has no exported member 'Vector3'. ts(2305)
当为 example-vector3 创建模块声明时,导出当前设置为空命名空间。 没有从该命名空间中导出 Vector3 类。
重新打开 types/example-vector3/index.d.ts 并编写以下代码:
types/example-vector3/index.d.ts
declare module "example-vector3" {
export = vector3;
namespace vector3 {
export class Vector3 {
constructor(x: number, y: number, z: number);
add(vec: Vector3): Vector3;
}
}
}
在此代码中,请注意现在如何在 vector3 命名空间内导出一个类。模块声明的主要目标是提供由库公开的值的类型信息。这样,我们可以以类型安全的方式使用它。
在这种情况下,我们知道 example-vector3 库提供了一个名为 Vector3 的类,该类在构造函数中接受三个数字,并且具有用于将两个 Vector3 实例相加的 add 方法,并返回一个新实例作为结果。无需在此处提供实现,只需提供类型信息本身。不提供实现的声明在 TypeScript 中称为环境声明,通常在 .d.ts
文件中创建这些声明。
此代码现在将正确编译并具有 Vector3 类的正确类型。
使用命名空间,可以将库导出的内容隔离到单个类型单元中,在本例中为 vector3 命名空间。这使得自定义模块声明变得更加容易,甚至可以通过将类型声明提交到 DefinetelyTyped 仓库来使所有开发人员都可以使用它。