🙋 Why CheckJS?
- 让 JavaScript 项目也能享受到 TS 的类型推导等诸多好处。* 和直迁 TypeScript 相比,大大降低成本和风险,例如:🚥 使用方法
安装依赖、追加配置
# 为你的项目安装 TypeScript
npm install typescript
# 可选的:类型定义文件,按照自己的项目需要酌情添加
npm install @types/react -D
根目录新建 tsconfig.json,复制以下内容,特别注意黄色加粗的内容:
{"compilerOptions": {"outDir": "./FAKE_DIR","target": "ESNext","module": "CommonJS","esModuleInterop": true,"allowJs": true,"checkJs": true,"strictNullChecks": true,"jsx": "react"},"include": [// 根据项目目录结构自行配置"./src/**/*"],"exclude": [// 根据项目特性按需添加]
}
编写类型定义
编写类型定义的时候最好有一定的约束,可以防止类型定义冲突、提高代码结构可读性,我个人推荐下面这些方案。
目录结构
一般来说,类型定义都是针对一个页面或者组件的(我们可以拿 CSS 样式文件的地位来做类比),即和组件(页面)所在文件在同一目录下,命名为 typings.d.ts:
└── src└── pages├── detail│ ├── components│ │ └── Alert│ │ ├── index.jsx│ │ ├── style.scss│ │ └── typings.d.ts│ ├── index.jsx│ ├── style.scss│ └── typings.d.ts├── home└── profile
书写细节
d.ts 文件和 ts 文件一个最大的不同就是前者无需导入,可以理解为全局变量,这就很容易导致同名类型定义的冲突干扰,为了尽可能地解决该问题,推荐使用 namespace (命名空间)语法来分组局部类型定义,还是以上面一小节的目录结构为例,Alert 组件的 typings.d.ts 可以这样写:
declare namespace Page.Detail.Components.Alert { interface AlertProps {/** * 警告内容 */message: string;/** * 当关闭时做些什么 */onClose: () => void;}
}
原则是根据其所属的页面、组件等层级关系划分,层级之间可以用点号隔开,以防止过多的嵌套影响美观。
其他的书写细节和 TypeScript 的写法别无二致,平时 ts 怎么写的,这里如法炮制即可。
消费方式
核心思路是通过编写 jsdoc 进行消费,下面提供几个不错的实践:
案例 1:React 函数式组件
下面的案例中使用了 React.FC 工具函数来实现。(提示:TypeScript 的各种内置 Utility Types 也是可以利用起来的)
/**
* Alert 组件,用来展示警告信息,如:网络错误、服务器错误等
*
* @type {React.FC<Page.Detail.Components.Alert.AlertProps>}
*/
export const Alert = (props) => {const { message, onClose } = props;return <div>hello world!</div>;
};
案例 2:普通函数
对于一般函数,使用 @param 或者 @returns 注释描述该函数的入参和返回值。
/**
* 获取用户信息描述
*
* @param {Page.Detail.Components.Alert.UserInfo} userInfo 用户信息
* @return {string} 用户信息描述
*/
const getUserDescription = (userInfo) => {const {name, age} = userInfo;return `我是 ${name}, 今年 ${age} 岁`;
}
案例 3:一般变量
对于一般变量,可以使用 @types 注释在该变量顶部描述:
/** @type {Page.Detail.Components.Alert.UserInfo} */
const userInfo = {age: 18,name: '张三',
}
// 或者也可以这样写
const user = /** @type {Page.Detail.Components.Alert.UserInfo} */ {age: 18,name: '张三',
};
// 在 return 语句中
return /** @type {Page.Detail.Components.Alert.UserInfo} */ {age: 18,name: '张三',
};
最后,配合 VSCode、WebStorm 等现代 IDE,你就可以享受到类型提示了!
🚨 使用时的陷阱
无法识别 d.ts 中定义的 namespace
先引入一个问题:
已知某项目是一个巨大的 monorepo,请看它的 tsconfig.json,它有什么潜在的问题?
{"compilerOptions": {"outDir": "./FAKE_DIR","target": "ESNext","module": "CommonJS","esModuleInterop": true,"allowJs": true,"checkJs": true,"jsx": "react"},"include": ["./packages/**/*","./typings"]
}
- 大部分情况下,monorepo 仓库下的构建产物(大部分是特别长的 JavaScript 文件)都会被打包到各自 package 下面,很容易被 include 字段匹配。* 一旦开启 allowJs = true,TypeScript Server 就会去检查这些不必要的 JavaScript 文件,从而触发性能阈值,失去某些 feature,例如我们上文的 JavaScript Check 能力。
那么如何去看是什么文件造成了此问题?这里提供一个非常优雅的解法:
- 首先打开 TS Sever Log:
- 在大概第三十行左右的位置就可以看到 TypeScript Checker log 了所有被解析的 build 产物,这些都应该被排除在外:
在上面这个 Case 中,就可以通过 exclude 字段排除掉所有的 build 目录来提高性能:
{"compilerOptions": { // 略去},"include": ["./packages/**/*","./typings"],"exclude": ["**/build/**"]
}
最后
为大家准备了一个前端资料包。包含54本,2.57G的前端相关电子书,《前端面试宝典(附答案和解析)》,难点、重点知识视频教程(全套)。
有需要的小伙伴,可以点击下方卡片领取,无偿分享