第 14 篇:给个人项目加上团队级质量门禁:ESLint、Prettier、TypeScript 与 Husky

4 阅读9分钟

项目地址

说明:当前在线版本部署在 Vercel,国内网络访问可能不稳定。如果打不开,可以直接查看 GitHub 源码并在本地启动。


前言

上一篇我们完成了服务端工程化重构,把原本臃肿的 server/index.ts 拆成了更清晰的结构:

server/
  config/
    env.ts
  routes/
    chat.ts
  services/
    dify.ts
  types/
    dify.ts
  utils/
    errors.ts
  index.ts

到这里,项目已经不再只是一个能跑的 AI Demo。

它已经具备了比较完整的功能和结构:

Dify RAG 知识库
DeepSeek 模型
Vite React 前端
Express BFF
SSE 流式输出
Markdown 渲染
引用来源展示
多会话管理
停止生成
前端 Hook 拆分
服务端分层

但一个项目想继续长期维护,还需要一件很重要的事:

质量门禁。

也就是在开发、提交、构建之前,自动帮我们发现一些低级问题。

这一篇我们就给项目加上:

ESLint
Prettier
TypeScript strict
Husky
lint-staged

目标是让个人项目也具备团队项目的基础规范。


为什么个人项目也需要质量门禁?

很多人觉得个人项目没必要配这些东西。

一开始确实可以不配。

但当项目逐渐变复杂后,没有质量门禁会出现很多问题:

1. 代码风格不统一
2. 有些文件单引号,有些文件双引号
3. 有些地方加分号,有些地方不加
4. 未使用变量没人发现
5. 类型错误到构建时才暴露
6. any 越写越多
7. 提交到 GitHub 后才发现 lint 不通过
8. README 里写的启动方式和实际不一致

尤其是这个项目已经有:

前端 React 代码
服务端 Express 代码
TypeScript 类型
环境变量
流式请求
localStorage 状态

代码面越来越广,越早加质量门禁,后面越省心。


本篇目标

这一篇完成后,项目会支持:

1. Prettier 统一代码格式
2. ESLint 检查 TypeScript / React 常见问题
3. TypeScript 开启严格检查
4. npm run check 一键检查项目质量
5. Git commit 前自动格式化和检查
6. README 增加质量保障说明

最终我们希望常用命令变成:

npm run lint
npm run format
npm run typecheck
npm run check

并且提交前自动执行:

lint-staged
TypeScript typecheck

第一步:安装依赖

在项目根目录执行:

npm install -D prettier eslint-config-prettier lint-staged husky

如果你的 Vite 项目本身已经带 ESLint,这一步不用重复安装 ESLint。

如果没有,再补:

npm install -D eslint typescript-eslint eslint-plugin-react-hooks eslint-plugin-react-refresh globals

几个依赖的作用:

prettier:代码格式化
eslint:代码质量检查
eslint-config-prettier:关闭和 Prettier 冲突的 ESLint 规则
lint-staged:只检查本次提交暂存区文件
husky:Git hooks 工具
typescript-eslint:让 ESLint 支持 TypeScript
eslint-plugin-react-hooks:检查 React Hooks 使用规则
eslint-plugin-react-refresh:Vite React 常用规则
globals:提供 browser / node 全局变量定义

第二步:配置 Prettier

在项目根目录新建:

.prettierrc

写入:

{
  "semi": false,
  "singleQuote": true,
  "printWidth": 80,
  "trailingComma": "es5"
}

这里我选择:

不加分号
使用单引号
每行 80 字符
尾随逗号使用 es5 兼容风格

这不是唯一标准,团队可以按自己的习惯调整。

关键是:

项目里要有统一标准,而不是每个人按自己的喜好写。

再新建:

.prettierignore

写入:

dist
node_modules
coverage
.env

这些文件不需要格式化。


第三步:配置 ESLint

Vite React TS 新项目通常会生成:

eslint.config.js

我们可以调整成支持前端和服务端两套环境。

因为当前项目里既有:

src/**/*.ts / src/**/*.tsx

也有:

server/**/*.ts

前端需要 browser globals,服务端需要 node globals。

参考配置:

import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
import prettier from 'eslint-config-prettier'

export default tseslint.config(
  { ignores: ['dist', 'node_modules'] },
  {
    extends: [js.configs.recommended, ...tseslint.configs.recommended],
    files: ['src/**/*.{ts,tsx}'],
    languageOptions: {
      ecmaVersion: 2020,
      globals: globals.browser,
    },
    plugins: {
      'react-hooks': reactHooks,
      'react-refresh': reactRefresh,
    },
    rules: {
      ...reactHooks.configs.recommended.rules,
      'react-refresh/only-export-components': [
        'warn',
        { allowConstantExport: true },
      ],
      '@typescript-eslint/no-explicit-any': 'warn',
    },
  },
  {
    extends: [js.configs.recommended, ...tseslint.configs.recommended],
    files: ['server/**/*.ts'],
    languageOptions: {
      ecmaVersion: 2022,
      globals: globals.node,
    },
    rules: {
      '@typescript-eslint/no-explicit-any': 'warn',
    },
  },
  prettier
)

这里有几个点:

1. src 使用 browser globals
2. server 使用 node globals
3. React Hooks 使用官方推荐规则
4. no-explicit-any 暂时设为 warn,而不是 error
5. 最后加 prettier,避免 ESLint 和 Prettier 冲突

为什么 no-explicit-any 先设为 warn?

项目早期如果直接把所有 any 都禁掉,可能会让开发体验变差。

尤其是接外部 API 时,比如 Dify 的返回结构不一定完全稳定。

所以我建议先设为:

'@typescript-eslint/no-explicit-any': 'warn'

这样它会提醒你,但不会阻断开发。

等项目类型逐渐完善后,再升级为:

'@typescript-eslint/no-explicit-any': 'error'

质量门禁也要逐步提高,不一定一开始就拉满。


第四步:修改 package.json scripts

打开 package.json,补充这些命令:

{
  "scripts": {
    "dev": "vite",
    "server": "tsx watch server/index.ts",
    "dev:all": "concurrently "npm run server" "npm run dev"",
    "build": "tsc -b && vite build",
    "start": "tsx server/index.ts",
    "preview": "vite preview",
    "lint": "eslint .",
    "format": "prettier . --write",
    "format:check": "prettier . --check",
    "typecheck": "tsc -b --noEmit",
    "check": "npm run typecheck && npm run lint && npm run format:check"
  }
}

几个命令说明:

npm run lint          运行 ESLint
npm run format        自动格式化
npm run format:check  检查格式是否正确,但不修改文件
npm run typecheck     只做 TypeScript 类型检查,不输出构建产物
npm run check         一键执行 typecheck + lint + format:check

以后每次提交前,可以先跑:

npm run check

第五步:开启 TypeScript 严格检查

打开:

tsconfig.app.json

确认有这些配置:

{
  "compilerOptions": {
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true
  }
}

如果服务端有单独的:

tsconfig.node.json

也建议加:

{
  "compilerOptions": {
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true
  }
}

开启这些后,可能会暴露一些问题,比如:

定义了变量但没用
函数参数没用
某些值可能是 undefined
类型推断不够明确

这些报错不是麻烦,而是在帮我们提前发现潜在问题。


第六步:配置 lint-staged

package.json 里增加:

{
  "lint-staged": {
    "*.{ts,tsx,js,jsx,json,css,md}": [
      "prettier --write"
    ],
    "*.{ts,tsx}": [
      "eslint --fix"
    ]
  }
}

lint-staged 只处理 Git 暂存区里的文件。

这比每次提交都全量格式化整个项目更快。

它会在提交前自动:

格式化本次提交的文件
自动修复 ESLint 能修的问题

第七步:配置 Husky

执行:

npx husky init

它会生成:

.husky/pre-commit

把里面内容改成:

npx lint-staged
npm run typecheck

这样每次执行:

git commit

之前都会自动执行:

1. lint-staged
2. TypeScript 类型检查

如果检查不通过,提交会被阻止。

这就是提交前质量门禁。


第八步:跑一次全量格式化和检查

执行:

npm run format
npm run check

第一次跑时,可能会出现一些报错。

这是正常的。

下面列几个常见问题。


常见问题 1:未使用变量

比如:

'activeSession' is assigned a value but never used

解决方式:

没用就删掉
确实要用就补上逻辑

不要为了消除报错写一些无意义代码。


常见问题 2:catch error 是 unknown

TypeScript strict 下,catch (error) 默认是 unknown。

不要直接:

console.log(error.message)

应该写:

const message = error instanceof Error ? error.message : 'Unknown error'

这也是我们之前在服务端加 getErrorMessage 的原因。


常见问题 3:Node 类型缺失

如果服务端提示:

Cannot find name 'process'

或者:

Cannot find module 'path'

安装:

npm install -D @types/node

并确认 server 相关 tsconfig 能识别 Node 类型。


常见问题 4:格式检查不通过

如果 npm run format:check 报错,说明有文件格式不符合 Prettier。

直接执行:

npm run format

再重新:

npm run check

第九步:测试 pre-commit

随便改一个文件,然后执行:

git add .
git commit -m "chore: add quality gate"

如果配置成功,提交前会自动执行:

npx lint-staged
npm run typecheck

如果有错误,提交会失败。

修复后重新提交即可。


第十步:更新 README

在 README 中加一节:

## 代码质量

项目配置了基础质量保障:

- TypeScript strict mode
- ESLint
- Prettier
- Husky
- lint-staged
- pre-commit typecheck

常用命令:

```bash
npm run lint
npm run format
npm run typecheck
npm run check

README 不只是给别人看的,也是给未来的自己看的。

等几个月后再回到这个项目,清晰的命令说明会很有帮助。

---

## 当前质量门禁覆盖了什么?

现在项目有几层保障:

### 1. 格式层

 Prettier 负责:

```text
缩进
换行
引号
尾随逗号
代码宽度

2. 代码规范层

由 ESLint 负责:

未使用变量
React Hooks 规则
不推荐写法
部分 TypeScript 问题

3. 类型层

由 TypeScript 负责:

类型不匹配
可能 undefined
错误参数
返回值不一致

4. 提交流程层

由 Husky + lint-staged 负责:

提交前自动格式化
提交前自动修复 lint
提交前跑 typecheck

这几层组合起来,就是一个基础质量门禁。


这一步的工程价值

这一篇没有新增任何用户功能。

但是它对项目长期维护非常重要。

它的价值是:

1. 降低低级错误出现概率
2. 保证代码风格统一
3. 提交前发现类型问题
4. 让项目更像团队工程
5. 方便后续多人协作
6. 让简历项目更有工程含金量

很多简历项目只展示功能,但代码质量不一定好。

如果你能在 README 里明确写出:

TypeScript strict
ESLint
Prettier
Husky
lint-staged

这会比单纯写“实现了 AI 聊天”更能体现工程意识。


当前版本还有哪些不足?

现在项目已经有了比较完整的前后端工程结构和质量门禁。

接下来就会遇到部署问题。

我们前面尝试过几种方案:

Vercel
Render
Railway
国内轻量服务器

每种都有优缺点。

比如:

Vercel:免费方便,但国内访问不稳定,Express streaming 要改造
Render:适合 Node 服务,但可能要求绑卡
Railway:适合部署完整 Express 服务,但不是永久免费
国内轻量服务器:国内访问稳定,但需要购买和运维

下一篇我们专门写部署方案选择。

不是马上教某一种平台怎么部署,而是先分析:

这个项目到底适合部署在哪里?


本篇总结

这一篇我们给项目加上了基础质量门禁。

主要完成:

1. 安装 Prettier / ESLint / Husky / lint-staged
2. 配置 .prettierrc
3. 配置 .prettierignore
4. 配置 eslint.config.js
5. 区分前端 browser 环境和服务端 node 环境
6. 增加 lint / format / typecheck / check 命令
7. 开启 TypeScript strict 相关检查
8. 配置 lint-staged
9. 配置 Husky pre-commit
10. 更新 README 质量保障说明

到这里,项目已经具备了正式项目的基础工程质量。

下一篇我们聊部署:

Vercel、Railway、Render、国内轻量服务器怎么选?