需求背景
主管和其他同事基于公司的业务特点,开发了一套自研前端框架。技术选型是 React + JavaScript 的组合,上线后表现还不错。现在他们想把这个组件库推广到其他团队使用,所以让我琢磨一下:怎么能让使用者用得更顺手一点?尤其是能不能在写代码的时候有自动提示?
我调研了一下市面上常见的几种方案,大致有以下几类:
- 把整个项目从 JavaScript 重构为 TypeScript,这样就能通过 .ts 或 .tsx 文件自动生成 .d.ts 类型声明文件;
- 不动源码,在外面单独为每个导出的组件手动写 .d.ts 文件;
- 使用 TypeScript 编译器解析 JavaScript 文件,直接生成 .d.ts 文件;对于那些识别不全的部分,再通过 JSDoc 注释来辅助生成更准确的类型信息。
主管的意思是,希望尽可能少投入人力,因为框架已经稳定运行了,不想为了一个“非刚需”的功能去大动干戈。所以最终我们选择了第三种方案——它的最大优点就是:对源码几乎无侵入,改动小,成本低,见效快!
不过它也不是十全十美。比如 TypeScript 自动生成的 .d.ts 文件中,很多函数参数或对象属性都会被推断成 any,即使配合 JSDoc 使用,也并不是所有组件都能有完整的类型提示。有些时候你还是得手动点进 .d.ts 文件里看定义。
但总的来说,瑕不掩瑜。毕竟现在的目标是高效产出,不是追求完美主义。
项目结构说明
我们的框架是一个典型的多包项目,主要由两个核心目录组成:packages 和 components。
packages 目录下包含了四个子包:
- cli:负责创建项目的命令行工具;
- compatible:提供运行环境适配能力;
- multipage:支持多页面应用架构;
- store:实现全局状态管理。
components 目录则主要是 UI 组件和交互能力的集合。
整个项目的目录结构如下所示:- my-project/
- ├── components/
- │ ├── src/
- │ ├── build.config.mts
- │ └── package.json
- ├── packages/
- │ ├── cli/
- │ ├── compatible/
- │ │ ├── src/
- │ │ ├── build.config.mts
- │ │ └── package.json
- │ ├── multipage/
- │ └── store/
- ├── scripts/
- └── package.json
复制代码 除了 cli 外,其余子包都需要生成 .d.ts 文件。那我们的思路也很简单:在根目录安装 TypeScript,然后给每个子包加上 tsconfig.json,最后写个脚本批量处理这些子包,自动生成类型声明文件。
安装依赖
因为这些依赖项是多个子包共用的,所以我们统一安装在根目录下:- npm install --save-dev typescript jsdoc @types/react @types/react-dom
复制代码 tsconfig.json 配置
每个子包都是独立发布的,所以每个子包都要有自己的 tsconfig.json 文件。
下面是通用配置,利用了 TypeScript 的 emitDeclarationOnly 功能,只用来生成 .d.ts 文件:- {
- "compilerOptions": {
- "module": "ESNext",
- "target": "ES5",
- "moduleResolution": "node",
- "esModuleInterop": true,
- "skipLibCheck": true,
- "jsx": "preserve",
- "allowJs": true,
- "declaration": true,
- "emitDeclarationOnly": true,
- "outDir": "./types",
- "lib": ["es2017", "dom"]
- },
- "include": ["src/**/*.js", "src/**/*.jsx"],
- "exclude": ["node_modules"]
- }
复制代码 自动化脚本编写
为了让这个流程自动化,我们还需要一个脚本,遍历 packages 和 components 文件夹,找到带有 tsconfig.json 的子包,然后执行 TypeScript 命令生成 .d.ts 文件,并放在对应层级下的 types 文件夹中。
我们在 scripts 目录下新建了一个 build-dts.js 脚本,内容如下:
[code]const fs = require("fs");const path = require("path");const { execSync } = require("child_process");const rootDir = __dirname + "/../";const packagesDir = path.join(rootDir, "packages");const componentsDir = path.join(rootDir, "components");function buildDtsForPackage(pkgPath) { const tsconfigPath = path.join(pkgPath, "tsconfig.json"); const typesOutDir = path.join(pkgPath, "types"); if (!fs.existsSync(tsconfigPath)) { console.warn( `⚠️ No tsconfig.json found in ${pkgPath}, skipping .d.ts generation` ); return; } // ---- 清空 types 文件夹 ---- if (fs.existsSync(typesOutDir)) { console.log(`
来源:豆瓜网用户自行投稿发布,如果侵权,请联系站长删除 |