返回

TypeScript 使用 rot.js:无打包方案详解

javascript

使用 TypeScript 和外部加载的 rot.js:无打包方案

在没有打包工具的情况下,使用外部加载的 rot.js 库并结合 TypeScript 类型检查是一个常见但容易让人感到困惑的场景。这主要是由于 TypeScript 的模块解析方式,以及其类型声明的导入方式与实际值的导入方式有所不同。本篇文章会深入探讨这些问题,并提供几种切实可行的解决方案。

问题剖析

通常的 TypeScript 项目,会通过模块系统(如 CommonJS 或 ES 模块)管理依赖关系。 但对于某些需要在 HTML 中直接引用的外部库,我们不希望在 TypeScript 的编译输出中生成额外的 importrequire 语句。我们需要在保持类型检查的同时,允许 JavaScript 文件在浏览器环境中执行。 使用 rot.js 时,由于其自身包含了 TypeScript 定义文件(.d.ts),开发者期望 TypeScript 能够识别并使用这些类型声明,从而进行有效的类型检查,但这并不是理所当然的。

TypeScript 的模块解析

tsconfig.json 中,moduleResolution 设置项扮演着关键角色。 如果将其设置为 nodenodenext,TypeScript 会像 Node.js 一样查找模块,期望找到对应的 node_modules 依赖,并在编译输出中生成 requireimport 语句。 然而,对于外部加载的库,我们并不需要这些,这会导致运行错误。 此外,编辑器的类型推断通常依赖于模块解析策略。如果配置错误,可能出现编辑器提示正确,而编译报错的情况。

类型声明和值导入

TypeScript 的类型声明(.d.ts)文件用于提供类型信息,而不是运行时值。使用 import { Something } from "module"; 导入模块时,期望得到的是可以在代码中实例化和调用的实际对象或函数,但这无法直接从 .d.ts 文件获得。 import type { Something } from "module" 用于导入类型声明,这只是类型层面上的操作,并不对应运行时值。当两者使用不当的时候,TS编译器会发出各种告警。

解决方案

针对上述问题,存在以下几种可行的方案:

方案一:全局变量声明

原理: 通过在 d.ts 文件中声明全局变量,直接暴露 rot.js 中的模块,绕过 TypeScript 的模块系统,让 TypeScript 类型检查器知道rot-js 在运行时可用。

操作步骤:

  1. 创建一个名为 rot-js.d.ts 的文件(或者扩展你项目中已有的 *.d.ts 文件)。

  2. 添加以下代码,声明全局 ROT 变量,同时把 rot-js 中对应的类型加上:

    import * as ROT from 'rot-js';
    declare global {
       const ROT : typeof ROT
    }
    

    此步骤非常重要,没有此步骤就没法用 ROT.Display

  3. 在你的 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 的模块解析和库的实际加载机制协调一致。

操作步骤:

  1. 确保你的页面中包含加载器的库 (例如 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 的模块解析机制, 以及类型声明的用途。 根据实际项目的需求选择合适方案,保持项目结构的简洁和清晰。