TypeScript 使用 rot.js:无打包方案详解
2025-01-05 03:08:02
使用 TypeScript 和外部加载的 rot.js:无打包方案
在没有打包工具的情况下,使用外部加载的 rot.js 库并结合 TypeScript 类型检查是一个常见但容易让人感到困惑的场景。这主要是由于 TypeScript 的模块解析方式,以及其类型声明的导入方式与实际值的导入方式有所不同。本篇文章会深入探讨这些问题,并提供几种切实可行的解决方案。
问题剖析
通常的 TypeScript 项目,会通过模块系统(如 CommonJS 或 ES 模块)管理依赖关系。 但对于某些需要在 HTML 中直接引用的外部库,我们不希望在 TypeScript 的编译输出中生成额外的 import
或 require
语句。我们需要在保持类型检查的同时,允许 JavaScript 文件在浏览器环境中执行。 使用 rot.js 时,由于其自身包含了 TypeScript 定义文件(.d.ts
),开发者期望 TypeScript 能够识别并使用这些类型声明,从而进行有效的类型检查,但这并不是理所当然的。
TypeScript 的模块解析
在 tsconfig.json
中,moduleResolution
设置项扮演着关键角色。 如果将其设置为 node
或 nodenext
,TypeScript 会像 Node.js 一样查找模块,期望找到对应的 node_modules
依赖,并在编译输出中生成 require
或 import
语句。 然而,对于外部加载的库,我们并不需要这些,这会导致运行错误。 此外,编辑器的类型推断通常依赖于模块解析策略。如果配置错误,可能出现编辑器提示正确,而编译报错的情况。
类型声明和值导入
TypeScript 的类型声明(.d.ts)文件用于提供类型信息,而不是运行时值。使用 import { Something } from "module";
导入模块时,期望得到的是可以在代码中实例化和调用的实际对象或函数,但这无法直接从 .d.ts
文件获得。 import type { Something } from "module"
用于导入类型声明,这只是类型层面上的操作,并不对应运行时值。当两者使用不当的时候,TS编译器会发出各种告警。
解决方案
针对上述问题,存在以下几种可行的方案:
方案一:全局变量声明
原理: 通过在 d.ts
文件中声明全局变量,直接暴露 rot.js 中的模块,绕过 TypeScript 的模块系统,让 TypeScript 类型检查器知道rot-js
在运行时可用。
操作步骤:
-
创建一个名为
rot-js.d.ts
的文件(或者扩展你项目中已有的 *.d.ts 文件)。 -
添加以下代码,声明全局
ROT
变量,同时把rot-js
中对应的类型加上:import * as ROT from 'rot-js'; declare global { const ROT : typeof ROT }
此步骤非常重要,没有此步骤就没法用
ROT.Display
-
在你的 HTML 文件中,在引入你的 TypeScript 代码之前,先引入
rot-js.js
,如:<script src="rot-js/dist/rot.js"></script> <script src="my-script.js"></script>
代码示例:
TypeScript代码
const display = new ROT.Display();
效果:
这种方式直接跳过了 TypeScript 模块加载,并且保持了代码的整洁,也满足了外部导入库的需求。TypeScript 编辑器和编译期都知道存在 ROT
全局对象及其对应的类型信息。同时编译出来的 JS 不会有任何和 import
或者 require
相关的代码。
方案二: 使用 AMD/UMD 模块方式 (如 SystemJS 或 RequireJS)
原理: 一些库如 rot.js 可能提供 AMD 或者 UMD 的模块版本。这种模块版本旨在处理不同的加载环境。通过配置加载器,你可以让 TypeScript 的模块解析和库的实际加载机制协调一致。
操作步骤:
- 确保你的页面中包含加载器的库 (例如 SystemJS)。
```html
同时在 rot-js 文件之前加载你的自定义JS代码,`System.import('rot-js.js')` 需要改为正确的文件路径。在 `rot-js.js` 加载完以后,我们会将模块加载的值赋给全局变量 `window.ROT`,后续的代码可以直接使用它了。
2. 调整 tsconfig.json 里面的 `module` 设置为 `"amd"` 或 `"umd"`
**代码示例:**
```typescript
const display = new ROT.Display();
```
**效果:**
此方案通过使用加载器协调了代码和类型加载。编译出的代码中,依旧不会包含`import` or `require`
#### 注意事项:
* 在任何情况下都请避免直接引用 `node_modules` 下面的代码,如 `import * as ROT from "./node_modules/rot-js/dist/rot.js";` ,这种方式非常不灵活。
* 第一种方法由于使用了全局变量,可能带来命名冲突等问题,所以请保持谨慎。 确保全局变量名具备良好的命名规范。
### 总结
上述解决方案展示了在不使用打包工具的情况下,如何利用 TypeScript 和外部加载库。关键在于理解 TypeScript 的模块解析机制, 以及类型声明的用途。 根据实际项目的需求选择合适方案,保持项目结构的简洁和清晰。