Skip to main content

代码规范

Elftia 使用严格的 ESLint 配置(typescript-eslint/strict),采用渐进式执行策略。


工具链

工具用途配置文件
ESLint代码质量检查(strict 模式)eslint.config.js
Prettier代码格式化.prettierrc
HuskyGit Hooks 管理.husky/pre-commit
lint-staged暂存文件检查package.json

常用命令

# 运行 ESLint 检查
npm run lint:eslint

# 检查 Prettier 格式
npm run lint:prettier

# 自动格式化代码
npm run format

# 完整 lint 检查 (ESLint + TypeScript)
npm run lint

# 检查单个文件
npx eslint path/to/file.ts

# 自动修复单个文件
npx eslint path/to/file.ts --fix

提交前自动检查

提交代码时,Husky 自动运行 lint-staged,对暂存的 .ts/.tsx 文件执行 eslint --fix

  • error 级别:阻止提交,必须修复
  • warn 级别:显示警告,允许提交
  • auto-fix:自动修复 import 排序等可修复问题

ESLint 规则速查

严重等级

等级含义提交时行为
error必须修复阻止提交
warn建议修复允许提交
off已禁用-

Error 级别规则(阻止提交)

{/* simple-import-sort/imports — 可自动修复 */}
{/* Import 必须按规定顺序排列,运行 eslint --fix 自动修复 */}

{/* prefer-const */}
let value = 1; // 从未重新赋值,应使用 const

{/* no-var */}
var x = 1; // 禁止使用 var

{/* eqeqeq */}
if (a == b) { } // 使用 == 而非 ===(null 检查除外)

{/* react-hooks/rules-of-hooks */}
if (condition) { useState(); } // Hook 只能在顶层调用

Warn 级别规则(允许提交)

TypeScript 规则

规则说明修复方式
@typescript-eslint/no-unused-vars未使用的变量删除,或用 _ 前缀标记
@typescript-eslint/no-explicit-any使用 any 类型改为具体类型或 unknown
@typescript-eslint/consistent-type-imports非 type-only 导入改为 import { type Foo }
@typescript-eslint/no-non-null-assertion非空断言 !改为可选链 ?. 或类型守卫

React 规则

规则说明修复方式
react-hooks/exhaustive-depsHook 依赖不完整补全依赖数组
react/jsx-no-leaked-render渲染泄漏(count && <X />改为 count > 0 && <X />
react/self-closing-comp空标签未自闭合<Comp></Comp><Comp />
react/jsx-curly-brace-presence不必要的大括号prop={"val"}prop="val"
react/jsx-boolean-value多余的布尔值disabled={true}disabled
react/hook-use-state状态名非小驼峰[Value, setV][value, setV]

其他规则

规则说明修复方式
no-console渲染进程使用 console.log改为 console.warn/error/info/debug
jsx-a11y/*无障碍问题参见 设计系统

Import 排序规则

Import 必须按以下顺序排列(eslint --fix 可自动修复):

{/* 1. Node.js 内置模块 */}
import path from 'node:path';
import fs from 'node:fs';

{/* 2. 外部包 */}
import React, { useState } from 'react';
import { useQuery } from '@tanstack/react-query';

{/* 3. 内部别名 @/ */}
import { Button } from '@/components/ui/button';
import { useAppState } from '@/shared/hooks/useAppState';

{/* 4. @shared/ 别名 */}
import type { Message } from '@shared/contracts';

{/* 5. @main/ 别名 */}
import { AppPaths } from '@main/services/infra/paths/paths';

{/* 6. 父目录导入 */}
import { utils } from '../utils';

{/* 7. 同级目录导入 */}
import { helper } from './helper';

{/* 8. 样式导入 */}
import './styles.css';

Prettier 配置

选项说明
semitrue使用分号
singleQuotetrue使用单引号
tabWidth2缩进宽度
trailingComma'es5'尾逗号
printWidth100行宽限制

文件大小限制

黄金法则:单个文件不应超过 600 行代码。

文件类型推荐上限警戒线禁止超过
React 组件400 行600 行800 行
自定义 Hook300 行400 行600 行
工具/辅助函数150 行200 行300 行
类型定义100 行150 行200 行
服务/API300 行400 行600 行

违反后果

  • 超过警戒线 → 必须在当前任务中拆分,不得留到以后
  • 超过禁止线 → 立即停止编写,先重构再继续

命名规范

文件命名

类型规则示例
React 组件PascalCase.tsxUserMessage.tsx
Hooksuse + PascalCase.tsuseChatActions.ts
工具函数camelCase.tsmessageHelpers.ts
类型定义camelCase.ts 或 types.tschatTypes.ts
常量UPPER_CASE.tsAPI_CONSTANTS.ts
服务PascalCase + Service.tsChatService.ts
目录kebab-casechat-messages/

组件命名

{/* 好:清晰、描述性强 */}
export function UserMessage() { }
export function EditTool() { }
export function ChatComposer() { }

{/* 不好:太泛化 */}
export function Message() { }
export function Tool() { }
export function Input() { }

Hook 命名

{/* 好:use + 功能描述 */}
export function useChatActions() { }
export function useMessageEffects() { }

{/* 不好:不符合 React Hook 命名规范 */}
export function chatActions() { }
export function messageHook() { }

目录结构规范

组件目录

components/
├── ComponentName/
│ ├── index.ts # 统一导出
│ ├── ComponentName.tsx # 主组件
│ ├── types.ts # 类型定义
│ ├── utils.ts # 工具函数
│ ├── SubComponent.tsx # 子组件
│ └── __tests__/
│ └── ComponentName.test.tsx

Hooks 目录

hooks/
├── domain/
│ ├── index.ts # 统一导出
│ ├── useDomainState.ts # 状态
│ ├── useDomainActions.ts # 动作
│ └── useDomainEffects.ts # 副作用

服务目录

services/
├── ServiceName/
│ ├── index.ts # 统一导出
│ ├── ServiceName.ts # 主服务
│ ├── types.ts # 类型定义
│ └── subdomain/ # 子领域

TypeScript 最佳实践

类型导入

{/* 使用 type-only 导入 */}
import { type SomeType } from './types';
import type { AnotherType } from '@shared/contracts';

避免 any

{/* 不好 */}
function handle(data: any) { }

{/* 好:使用具体类型 */}
function handle(data: Message) { }

{/* 好:使用泛型 */}
function handle<T extends BaseMessage>(data: T) { }

{/* 好:使用 unknown + 类型守卫 */}
function handle(data: unknown) {
if (isMessage(data)) { /* data: Message */ }
}

Barrel Export

{/* components/chat/messages/index.ts */}
export { MessageItem } from './MessageItem';
export { MessageList } from './MessageList';
export type { MessageItemProps } from './MessageItem';

代码组织顺序

React 组件结构

{/* 1. 导入 */}
{/* 2. 类型定义 */}
{/* 3. 常量 */}
{/* 4. 辅助函数 */}
{/* 5. 主组件 */}
{/* 5.1 Hooks */}
{/* 5.2 派生状态 (useMemo) */}
{/* 5.3 事件处理器 (useCallback) */}
{/* 5.4 Effects (useEffect) */}
{/* 5.5 渲染 (return) */}
{/* 6. 子组件(如果简单) */}

单一职责原则

每个文件只有一个改变的理由。当组件出现以下情况时应拆分:

  1. 超过 300 行代码
  2. 包含 3 个以上独立逻辑块
  3. 有可复用的部分
  4. 不同部分的变更频率不同
  5. 测试变得困难

拆分策略

策略适用场景示例
按功能拆分UI 区块独立工具栏 / 消息列表 / 输入框 → 独立组件
按数据流拆分状态逻辑复杂状态 / 动作 / 副作用 → 独立 Hook
按领域拆分多种同类项EditTool / WriteTool / BashTool → 独立文件

Git 工作流

提交规范

使用 Conventional Commits 格式:

feat: 新功能描述
fix: 修复描述
refactor: 重构描述
docs: 文档更新
style: 代码格式调整(不影响逻辑)
test: 测试相关
chore: 构建工具或辅助工具变动

提交前检查清单

  1. 运行 npm run lint — 确保无 error
  2. 修复所有 error 级别问题
  3. 尽量修复 warn 级别问题
  4. 运行 npm run format — 格式化代码
  5. 新文件应该 0 warning
  6. 修改现有文件时,顺便修复该文件的 warning