Next本来就内置了ts的webpack配置、babel配置,想要引入的成本很低。
但是难点在于:我们的nextjs项目另外采用了express作为服务端服务器框架,如何将express内的Node代码也改造成使用ts呢?
还有最痛苦的问题就是不知道express怎么写ts啊啊啊,我原本的文件都是用CJS导入导出的,但是根本不支持导出type,导致我写个type到处都是报错😭
总之把一些步骤和解决措施放在下面。
react项目引入ts基础配置
参考:https://juejin.cn/post/7128929470618009608
Next typescript 文档
1. 安装新的依赖
1. 安装typescript
npm i -D typescript
安装完typescript后,其实ts内置了一个tsc
命令,tsc命令可以生成配置文件、检查ts的类型错误。
使用tsc --init
创建一个tsconfig.json文件,里面就是解析ts文件的各种配置。
一份可供参考的tsconfig文件如下:
🔗tsconfig官方文档文档链接
{// 编译选项"compilerOptions": {// 生成代码的语言版本:将我们写的 TS 代码编译成哪个版本的 JS 代码"target": "esnext",// 生成代码的模块化标准"module": "esnext",// 指定要包含在编译中的 library"lib": ["dom", "dom.iterable", "esnext"],// 允许 ts 编译器编译 js 文件"allowJs": true,// 跳过类型声明文件的类型检查"skipLibCheck": true,// es 模块 互操作,屏蔽 ESModule 和 CommonJS 之间的差异"esModuleInterop": true,// 允许通过 import x from 'y' 即使模块没有显式指定 default 导出"allowSyntheticDefaultImports": true,// 开启严格模式"strict": true,// 对文件名称强制区分大小写"forceConsistentCasingInFileNames": true,// 为 switch 语句启用错误报告"noFallthroughCasesInSwitch": true,// 模块解析(查找)策略"moduleResolution": "node",// 允许导入扩展名为.json的模块"resolveJsonModule": true,// 是否将没有 import/export 的文件视为旧(全局而非模块化)脚本文件"isolatedModules": true,// 编译时不生成任何文件(只进行类型检查)"noEmit": true,// 指定将 JSX 编译成什么形式"jsx": "react-jsx",// 如果指定,.ts文件将被输出到此目录中,否则留在源文件的目录结构中"outDir": "build/dist",// 当前的根目录"baseUrl": ".",// 不需要定义:所有可见的“node_modules@types/**”包都会被默认收集。但是如果被定义了那就不会默认收集了// "types": ["node"],},// 指定允许 ts 处理的目录"include": ["**/*.ts","**/*.tsx","next-env.d.ts"],// tsc排除的目录"exclude": ["node_modules", "build", "dist"]}
ts文件中存在的几个定义:
- .ts 或 .tsx文件
.ts ts 文件的后缀名 .tsx 是在 TS 中使用 React 组件时,需要使用该后缀- d.ts 文件
.d.ts 类型声明文件,用来指定类型,可以定义全局type。
不会生成 .js 文件,仅用于提供类型信息,在.d.ts文件中不允许出现可执行的代码,只用于提供类型
2. 引入ts的babel解析 & webpack的ts-loader(next/babel已经帮我们集成了这一步)
npm i -D @babel/preset-typescript ts-loader
然后在.babelrc中配置preset。
webpack中配置ts-loader,进行编译过程中的类型校验,如果类型错误将中断校验。
babel编译和ts-loader编译的区别与互补说明:🔗https://juejin.cn/post/7127206384797483044
next.config中提供了编译中跳过tsc的方式:
typescript: {ignoreBuildErrors: true, // 跳过build时的ts校验,交给eslint和husky拦截},
2. 引入常用的框架中的ts type
引入之后不用操作,tsc会自动找到@/types包下的文件的。
npm i -D @types/lodash @types/react @types/node @types/express
3. 完善ts的eslint配置
ts没有专门的tslint,它的创始人推荐使用eslint插件配置在eslint中。
TSLint is deprecated.
See this issue for more details: Roadmap: TSLint → ESLint. If you’re interested in helping with the TSLint/ESLint migration, please check out our OSS Fellowship program.
npm i -D @typescript-eslint/eslint-plugin @typescript-eslint/parser
同时在husky的命令中增加.ts / .tsx
eslintrc.js中配置:
overrides: [{files: ['**/*.ts', '**/*.tsx'],extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],parserOptions: {project: './tsconfig.json',},parser: '@typescript-eslint/parser',rules: {// 不允许显式声明any'@typescript-eslint/no-explicit-any': 2,},},],
4. 编写tsx文件,.d.ts文件
测试类型校验、eslint校验是否可行。
express服务端引入ts
参考:
next-express-github-demo
使用TS来编写express服务器
我这边的express服务端文件都在/server文件夹下,项目目录结构大概如下:
.
├── build 编译文件
├── server 服务端
├── src 客户端
├── tsconfig.server.json
├── tsconfig.json
├── eslintrc.js
├── next.config.js
├── ……
└── src
配置服务端的类型校验
1. 安装新的依赖(上面已经安装过了)
npm i -D @types/node @types/express
2. 生成node端专用的tsconfig.json文件。
我取名为tsconfig.server.json
{// 引入扩展"extends": "./tsconfig.json","compilerOptions": {"target": "ES6", // 指定生成的JS代码的ECMAScript版本"module": "CommonJS", // 指定生成的JavaScript模块系统"strict": true, // 开启所有严格类型检查选项"esModuleInterop": true, // 启用esModuleInterop以兼容CommonJS和ES模块"skipLibCheck": true, // 跳过定义文件的类型检查"outDir": "build/dist", // 编译输出目录"rootDir": ".", // 源码目录"resolveJsonModule": true, // 允许导入JSON模块"sourceMap": true // 生成source map文件"allowSyntheticDefaultImports":true, // babel转换时可配置:即使CJS模块本身没有default导出,仍然可以使用default导入。(兼容ECM / CJS)"moduleResolution":"Node", // 使用Nodejs方案解析},"include": ["server/**/*.ts"], // 包含的TypeScript文件"exclude": ["node_modules"] // 排除的目录
}
baseUrl
- 作用: 决定非相对模块导入时的基准目录。
- 使用场景: 在代码中使用非相对路径(例如,
import { x } from 'myModule'
)时,TypeScript会根据baseUrl
配置的路径作为起点寻找模块。- 默认值: 如果未指定,使用当前目录。
- 示例: 如果设置了
baseUrl
为"./src"
,然后在代码中使用import { x } from 'utils/helper'
,TypeScript将会在src/utils/helper.ts
或src/utils/helper/index.ts
中寻找模块。rootDir
- 作用: 用于控制编译器如何安排输出文件的目录结构。
- 使用场景: 影响编译输出的目录结构,通常与
outDir
一起使用来规范地管理编译后的代码。- 默认值: 如果未指定,则TypeScript自动推断,通常为输入文件的共同上级路径。
- 示例: 假设项目结构是
src/utils/helper.ts
,设定rootDir
为"src"
,而outDir
为"dist"
,那么编译时helper.ts
会被输出为dist/utils/helper.js
。allowSyntheticDefaultImports
可以影响模块的导入方式。启用这个选项后,即使模块本身没有default导出,仍然可以使用default导入。
在CommonJS中,模块并没有默认的default,但Babel 转译会生成一个名为
default
的导出,使得ES6可以像导入默认导出那样导入CJS对象。如果项目使用了 Babel 来转译,可能会生成允许默认导入的代码形式,启用该选项可以使 TypeScript 和 Babel 的行为保持一致。
eslint配置
eslintrc记得增加tsconfig.server.json
parserOptions: {project: ['./tsconfig.json','./tsconfig.server.json']
},
检查是否可以编写服务端ts文件
如果还是没有ts提示,看看tsconfig的include有没有配置对,需要重启vscode。
配置nodemon运行时编译ts的命令
nodemon是express服务端开发的常用工具配置。
上面的ts类型校验完善后,这时候你会发现我们的项目启动不起来,因为服务器端没有babel给它编译成ts,我们想要在node端实时编译ts,通常需要结合使用 ts-node
,它可以在运行时直接编译和执行 TypeScript 文件。
npm i -D ts-node
然后变更nodemon命令,原本是nodemon server/index.js
,现在要加上指定命令行以ts-node
的形式执行:
nodemon --exec ts-node server/index.js
配置ts文件的build命令
typescript带来的tsc命令可以把ts文件编译成js文件,所以你也可以看到有些demo的build命令直接就是一个tsc
,但是因为商业项目中涉及到的其他文件太多,我们往往使用webpack+babel的方案将ts转化成js。
在上面的客户端引入ts中我们已经下载了ts-loader
和@babel/preset-typescript
,也进行了配置,因此我们的build命令仍然正常使用webpack打包就可以了。
但是next的打包是专门提供了一个next build
命令,这个命令只适用于next的代码,对于我们自行搭建的express服务器的代码没有办法执行,因此我们额外需要对ts的服务器端配置需要专门指定tsconfig。修改如下:
next build && tsc --project tsconfig.server.json
如何在express中书写ts文件?
不知道大家有没有遇到这个问题:原本的文件都是用CJS导入导出的,但是根本不支持导出type,写个type到处都是报错😭
参考:
利用typescript + express 开发一个nodejs服务端demo
看起来,CJS的type只能写.d.ts文件?
比如:我们可以定义一个custom.d.ts文件,在Request的接口中添加我们加入的属性,这样在使用的时候就会出现提示。custom.d.ts文件会和@types/express里面的类型文件进行合并。
// costom.d.ts
declare namespace Express {interface Request {teacherName: string}
}
但是使用ES6就可以直接导出type,不用声明import type xxx
🌰
// ErrorResponse.ts
export default interface ErrorResponse {message: string;stack?: string;
}// index.js
import MessageResponse from '../interfaces/MessageResponse';router.get<{}, MessageResponse>('/', (req, res) => {res.json({message: '',});
});