现代 JavaScript/TypeScript 编程规范通常借助多个代码检查工具来保证代码质量和一致性。本文基于 OxLint、ESLint、Knip 三大工具总结了一套编程规范,涵盖代码复杂度控制、类型安全、现代 API 使用、模块管理等多个方面。这些规则帮助团队编写更可维护、更健壮的代码。
工具概述
OxLint、ESLint、Knip 是三种互补的代码质量工具,各自专注于不同的检查维度。本章准确介绍每个工具的技术特点和应用场景。
OxLint
OxLint 是 Oxc 项目的高性能 linter 组件,完全用 Rust 编写。作为 ESLint 的现代替代品,它在速度上实现了 50-100 倍的性能提升。
技术特点:
- 极致性能:Rust 实现带来的零成本抽象,可在秒级完成大型代码库检查
- ESLint 兼容:实现了 ESLint、typescript-eslint、React 等插件的核心规则
- 开箱即用:无需复杂配置,内置合理的默认规则集
- 增量检查:支持只检查变更文件,适合 CI/CD 集成
检查范围:
- 代码复杂度控制(函数行数、参数数量、嵌套深度)
- 代码风格规范(大括号、严格相等、模板字符串)
- TypeScript 基础规则(禁止
any、类型注解一致性、重载签名顺序) - 模块管理(重复导入、循环依赖、CommonJS/AMD 检测)
- 现代 API 推荐(
for...of、.includes()、structuredClone()) - React/JSX 规则(属性简写、自闭合标签、Hooks 基础规则)
**适用场景:**日常开发的快速反馈、CI 流水线的门禁检查、大型单体仓库的全量扫描
ESLint
ESLint 是 JavaScript 生态系统中最成熟的代码检查工具,用 JavaScript 编写。通过强大的插件系统和规则引擎,提供深度可定制的代码质量保障。
技术特点:
- 完整的插件生态:typescript-eslint、eslint-plugin-react、testing-library 等数千个插件
- 类型感知检查:通过 TypeScript 编译器 API 提供深度类型检查(需要完整类型信息)
- 自定义规则:支持编写项目特定的 lint 规则
- 自动修复:多数规则支持
--fix自动修复
检查范围:
- 复杂类型规则:Promise 误用检测、Switch 穷尽性检查、类型安全的命名约定
- 语义分析:需要类型信息的高级规则(如
no-floating-promises、no-misused-promises) - 框架深度集成:React Hooks 依赖检查、Testing Library 最佳实践
- 团队约定:自定义规则实现项目特定的代码约定
**适用场景:**需要类型感知规则的场景、自定义规则需求、特定框架的深度检查
Knip
Knip 是一个专注于死代码检测的静态分析工具,用 TypeScript 编写。它解决了"代码是否被使用"这一维度的质量问题,与 linter 形成互补。
技术特点:
- 全局依赖分析:从入口文件开始,追踪整个项目的导入导出关系
- 构建工具集成:理解 Webpack、Vite、Next.js 等工具的入口配置
- 多维度检测:不仅检测代码文件,还分析 npm 依赖、类型定义、配置文件
- 上下文感知:区分生产代码和开发依赖,理解测试文件的特殊性
检查范围:
- 未使用的导出:导出但从未被导入的函数、类、类型
- 未使用的文件:项目中存在但未被任何入口引用的文件
- 未使用的依赖:
package.json中声明但未被代码使用的依赖包 - 未使用的类型:TypeScript 类型定义中未被引用的类型
- 未使用的枚举成员:枚举中声明但从未读取的成员
- 重复依赖:同一依赖的不同版本
**适用场景:**定期代码清理、依赖瘦身、重构后的死代码清除、bundle 体积优化前的分析
工具对比
| 工具 | 实现语言 | 检查类型 | 性能特点 | 典型应用场景 |
|---|---|---|---|---|
| OxLint | Rust | 语法、风格、最佳实践 | 极快 | 日常开发的快速反馈 |
| ESLint | JavaScript | 类型、语义、框架规则 | 较慢(需要类型信息) | 复杂的类型检查和自定义规则 |
| Knip | TypeScript | 代码使用分析 | 中等(全量分析) | 定期清理未使用代码 |
三者协同工作:
- OxLint 覆盖大部分常规检查,提供快速反馈
- ESLint 处理需要完整类型信息的复杂规则
- Knip 在代码库层面进行全局使用分析
代码复杂度限制
控制代码复杂度是保证代码可维护性的基础。过长的函数、过多的参数、过深的嵌套都会增加理解和维护成本。
函数行数限制: oxlint(max-lines-per-function)
单个函数不超过 100 行(不计空行和注释)。超过限制的函数应拆分为多个小函数。
// ❌ 错误:函数超过 100 行
function processData() {
// ... 超过 100 行代码
}
// ✅ 正确:拆分为多个小函数
function processData() {
validateData()
transformData()
saveData()
}
参数数量限制: oxlint(max-params)
函数参数不超过 5 个。参数过多时应使用对象参数。
// ❌ 错误:超过 5 个参数
function createUser(
name: string,
age: number,
email: string,
phone: string,
address: string,
country: string,
) {}
// ✅ 正确:使用对象参数
function createUser(userData: UserData) {}
回调嵌套深度: oxlint(max-nested-callbacks)
回调嵌套不超过 4 层。过深的嵌套应使用 async/await 或 Promise 链重构。
// ❌ 错误:嵌套超过 4 层
a(() => {
b(() => {
c(() => {
d(() => {
e(() => {}) // 第 5 层,过深!
})
})
})
})
// ✅ 正确:使用 async/await
async function process() {
await a()
await b()
await c()
await d()
await e()
}
条件嵌套深度: oxlint(max-depth)
if/for/while 等控制结构的嵌套深度不超过限制。过深的嵌套应使用提前返回(early return)或提取函数重构。
// ❌ 错误:嵌套过深
if (a) {
if (b) {
if (c) {
if (d) {
// 过深
}
}
}
}
// ✅ 正确:提前返回
if (!a) return
if (!b) return
if (!c) return
if (d) {
// 处理逻辑
}
代码风格规范
统一的代码风格提高代码可读性,减少理解成本。本章规则确保代码风格的一致性。
大括号: oxlint(curly)
控制语句必须使用大括号,即使只有一行代码。
// ❌ 错误:缺少大括号
if (condition) doSomething()
// ✅ 正确:始终使用大括号
if (condition) {
doSomething()
}
相等比较: oxlint(eqeqeq)
始终使用严格相等 === 和 !==,禁止使用 == 和 !=。
// ❌ 错误:使用 == 或 !=
if (value == null) {
}
if (a != b) {
}
// ✅ 正确:使用 === 或 !==
if (value === null) {
}
if (a !== b) {
}
构造函数: oxlint(new-for-builtins)
内置类型不使用 new 构造,直接使用字面量或函数调用。
// ❌ 错误:内置类型使用 new
const str = new String('hello')
const num = new Number(42)
// ✅ 正确:使用字面量
const str = 'hello'
const num = 42
空对象/接口: oxlint(no-empty-interface, no-empty-object-types)
禁止定义空接口或空对象类型。
// ❌ 错误:空接口或空对象类型
interface Props {}
type Data = {}
// ✅ 正确:使用 unknown 或添加属性
type Props = unknown
interface Data {
id: string
}
三元运算符: oxlint(no-unneeded-ternary)
避免不必要的三元运算符,直接使用布尔值。
// ❌ 错误:不必要的三元运算符
const isActive = value ? true : false
// ✅ 正确:直接使用布尔值
const isActive = Boolean(value)
模板字符串: oxlint(no-template-curly-in-string)
避免在普通字符串中使用模板字符串语法。
// ❌ 错误:在普通字符串中使用 ${}
const message = 'Error: ${error}' // 输出字面量文本
// ✅ 正确:使用模板字符串
const message = `Error: ${error}`
单独的 if: oxlint(no-lonely-if)
避免 else 块中只有一个 if,应合并为 else if。
// ❌ 错误:else 块中只有一个 if
if (a) {
doA()
} else {
if (b) {
doB()
}
}
// ✅ 正确:合并为 else if
if (a) {
doA()
} else if (b) {
doB()
}
不可读的数组解构: oxlint(no-unreadable-array-destructuring)
避免跳过过多元素的数组解构,应使用索引访问。
// ❌ 错误:过多的跳过元素
const [, , , , fifth] = array
// ✅ 正确:使用索引访问
const fifth = array[4]
无用的字符串拼接: oxlint(no-useless-concat)
避免编译时可以合并的字符串拼接。
// ❌ 错误:无意义的字符串拼接
const message = 'Hello' + 'World'
// ✅ 正确:直接使用完整字符串
const message = 'HelloWorld'
// ✅ 动态拼接是允许的
const greeting = 'Hello' + name
命名规范
统一的命名规范提高代码的可读性和一致性。
文件名命名: oxlint(filename-case)
文件名使用 kebab-case(短横线命名)。
// ❌ 错误的文件名
MyComponent.tsx
user_service.ts
API_Client.ts
// ✅ 正确:使用 kebab-case
my - component.tsx
user - service.ts
api - client.ts
命名约定: eslint(@typescript-eslint/naming-convention)
不同类型的标识符遵循不同的命名规范。
// ❌ 错误:不符合命名规范
// ✅ 正确
import { my_helper, myHelper } from './utils' // import 应该是 camelCase 或 PascalCase
const MyConstant = 'value' // 普通变量应该是 camelCase
function do_something() {} // 函数应该是 camelCase 或 PascalCase
type user_type = {} // 类型应该是 PascalCase
enum Status {
success, // 枚举成员应该是 PascalCase
error,
}
const myConstant = 'value'
const MY_CONSTANT = 'VALUE' // 常量可以是 UPPER_CASE
function doSomething() {}
function MyComponent() {} // React 组件用 PascalCase
type UserType = {}
enum Status {
Success,
Error,
}
构造函数大写: oxlint(new-cap)
构造函数必须以大写字母开头。
// ❌ 错误:构造函数应该大写开头
function user() {}
const instance = new user()
// ✅ 正确
function User() {}
const instance = new User()
// ✅ 或使用 class
class User {}
const instance = new User()
TypeScript 类型安全
TypeScript 的类型系统是保证代码质量的重要工具。本章规则确保充分利用类型系统的能力。
禁止使用 any: oxlint(no-explicit-any)
禁止显式使用 any 类型,应使用具体类型或 unknown。
// ❌ 错误
const data: any = fetchData()
// ✅ 正确:使用具体类型
interface UserData {
name: string
age: number
}
const data: UserData = fetchData()
// ✅ 如果真的是任意类型,使用 unknown
const data: unknown = fetchData()
禁止推断类型: oxlint(no-inferrable-types)
移除可以被 TypeScript 自动推断的类型注解。
// ❌ 错误:不必要的类型声明
const count: number = 10
const name: string = 'John'
// ✅ 正确:让 TypeScript 推断
const count = 10
const name = 'John'
数组类型定义: oxlint(array-type)
保持一致的数组类型定义风格,使用 T[] 形式。
// ✅ 推荐
const numbers: number[] = [1, 2, 3]
const users: User[] = []
// 💡 复杂类型可以使用 Array<T>
const callbacks: Array<(data: string) => void> = []
泛型构造函数: oxlint(consistent-generic-constructors)
使用类型推断而非显式声明泛型参数。
// ❌ 错误
const map = new Map<string, number>()
map.set('key', 1)
// ✅ 正确:类型推断
const map = new Map([['key', 1]])
索引对象类型风格: oxlint(consistent-indexed-object-style)
保持一致的索引对象类型定义。
// ✅ 使用 Record 类型
type Config = Record<string, number>
// ✅ 或使用索引签名
interface Config {
[key: string]: number
}
函数重载签名相邻: oxlint(adjacent-overload-signatures)
函数重载签名必须相邻放置。
// ❌ 错误:重载签名不相邻
function process(data: string): string
function other(): void
function process(data: number): number
// ✅ 正确:重载签名必须相邻
function process(data: string): string
function process(data: number): number
function other(): void
Switch 穷尽检查: eslint(@typescript-eslint/switch-exhaustiveness-check)
Switch 语句必须处理联合类型的所有情况。
type Status = 'pending' | 'success' | 'error'
// ❌ 错误:遗漏 case
function handleStatus(status: Status) {
switch (status) {
case 'pending':
return 'loading'
case 'success':
return 'done'
// 缺少 'error' case
}
}
// ✅ 正确:处理所有情况
function handleStatus(status: Status): string {
switch (status) {
case 'pending':
return 'loading'
case 'success':
return 'done'
case 'error':
return 'failed'
}
}
只读属性: eslint(@typescript-eslint/prefer-readonly)
类的私有属性如果不会被修改,应声明为 readonly。
// ❌ 可改进:可变的类属性
class User {
private config = {}
}
// ✅ 更好:使用 readonly
class User {
private readonly config = {}
}
禁止误用 Promise: eslint(@typescript-eslint/no-misused-promises)
防止在不适当的地方使用 Promise。
// ❌ 错误:在条件语句中使用 Promise
if (fetchData()) {
// Promise 总是真值
}
// ❌ 错误:将 async 函数作为 forEach 回调
array.forEach(async (item) => {
await process(item) // forEach 不会等待
})
// ✅ 正确
if (await fetchData()) {
// 等待 Promise 结果
}
// ✅ 正确
for (const item of array) {
await process(item)
}
namespace 禁用: oxlint(typescript/no-namespace, namespace)
禁止使用 namespace,应使用 ES 模块。
// ❌ 错误:使用 namespace
namespace Utils {
export function format() {}
}
// ✅ 正确:使用 ES 模块
export function format() {}
模块导入导出
模块系统是现代 JavaScript 项目的基础。本章规则确保模块使用的正确性和一致性。
禁止重复导入: oxlint(no-duplicate-imports, no-duplicates)
同一模块的导入应合并到一条 import 语句。
// ❌ 错误:重复导入
// ✅ 正确:合并导入
import { a, a, b, b } from './module'
禁止导出所有: eslint(no-restricted-syntax) oxlint(export)
禁止使用 export * 重导出,应使用命名导出。
// ❌ 错误:export * 重导出
export * from './module'
// ✅ 正确:使用命名导出
export { specificExport1, specificExport2 } from './module'
禁止循环依赖: oxlint(no-cycle)
模块之间不能形成循环依赖。
// ❌ 错误:循环依赖
// a.ts
// b.ts
import { a } from './a' // 循环依赖!
import { b } from './b'
// ✅ 正确:重构代码消除循环依赖
// 将共享代码提取到第三个文件
禁止自导入: oxlint(no-self-import)
文件不能导入自己。
// ❌ 错误
// utils.ts
import { helper } from './utils' // 自己导入自己
// ✅ 正确:直接引用同文件中的函数
禁止未解析的导入: oxlint(no-unresolved)
导入的模块必须存在。
// ❌ 错误:导入不存在的模块
// ✅ 正确:确保模块存在
import { existing } from './existing-module'
import { missing } from './non-existent'
禁止类型导入副作用: oxlint(no-import-type-side-effects)
类型导入应与值导入分离。
// ❌ 错误:混合类型和值导入
import { initDatabase, initDatabase, type User } from './db'
// ✅ 正确:分离类型导入和值导入
import type { User } from './db'
禁止 CommonJS: oxlint(no-commonjs)
使用 ES 模块而非 CommonJS。
// ✅ 正确:使用 ES 模块
import module from './module'
// ❌ 错误:使用 CommonJS
const module = require('./module')
module.exports = {}
export {}
禁止 AMD: oxlint(no-amd)
使用 ES 模块而非 AMD。
// ✅ 正确:使用 ES 模块
import module from './module'
// ❌ 错误:使用 AMD
define(['module'], function (module) {})
禁止可变导出: oxlint(no-mutable-exports)
导出的应是常量或函数,而非可变绑定。
// ❌ 错误:导出可变绑定
export let count = 0
export let config = {}
// ✅ 正确:导出常量或函数
export const count = 0
export function getCount() {
return count
}
禁止未使用的模块: oxlint(no-unused-modules) knip
文件导出的内容应被其他文件使用,未使用的导出应删除。
// ❌ 错误:导出了内容但从未被导入使用
// unused-file.ts
export function helper() {} // 没有其他文件导入这个函数
// ✅ 正确:导出的内容被其他文件使用
// used-file.ts
export function helper() {} // 被 app.ts 导入使用
现代 API 使用
使用现代 JavaScript API 可以提高代码的简洁性和性能。本章推荐使用现代化的 API 替代旧的写法。
数组方法
优先使用 for...of: oxlint(prefer-for-of)
// ❌ 错误:传统 for 循环
for (let i = 0; i < array.length; i++) {
console.log(array[i])
}
// ✅ 正确:使用 for...of
for (const item of array) {
console.log(item)
}
禁止 forEach: oxlint(no-array-for-each)
// ❌ 错误:使用 forEach
array.forEach((item) => console.log(item))
// ✅ 正确:使用 for...of
for (const item of array) {
console.log(item)
}
使用 .some() 替代 .find(): oxlint(prefer-array-some)
// ❌ 错误:用 find 检查存在性
const exists = array.find((x) => x.id === id) !== undefined
// ✅ 正确:使用 some
const exists = array.some((x) => x.id === id)
使用 .flat() 和 .flatMap(): oxlint(prefer-array-flat, prefer-array-flat-map)
// ❌ 错误:手动展平数组
const flattened = array.reduce((acc, val) => acc.concat(val), [])
// ✅ 正确:使用 flat
const flattened = array.flat()
使用 .includes(): oxlint(prefer-includes)
// ❌ 错误:使用 indexOf
const hasItem = array.indexOf(item) !== -1
// ✅ 正确:使用 includes
const hasItem = array.includes(item)
数组 join 分隔符: oxlint(require-array-join-separator)
// ❌ 错误:join 不指定分隔符
const str = array.join() // 默认使用逗号,不够明确
// ✅ 正确:明确指定分隔符
const str = array.join(',')
const str2 = array.join(' ')
Promise 最佳实践
catch 处理: oxlint(prefer-catch)
// ❌ 错误:使用 then 的第二个参数
promise.then(onSuccess, onError)
// ✅ 正确:使用 catch
promise.then(onSuccess).catch(onError)
避免回调中的 Promise: oxlint(no-promise-in-callback)
// ❌ 错误:回调中返回 Promise
readFile('file.txt', (err, data) => {
return fetchData()
})
// ✅ 正确:使用 async/await
async function process() {
const data = await readFileAsync('file.txt')
return fetchData()
}
必须使用 await: oxlint(require-await)
// ❌ 错误:async 函数没有 await
async function getData() {
return data
}
// ✅ 正确:有 await 或移除 async
async function getData() {
return await fetchData()
}
function getData() {
return data
}
Promise reject 传递错误: oxlint(prefer-promise-reject-errors)
// ❌ 错误:reject 传递非 Error 对象
return Promise.reject('失败')
// ✅ 正确:传递 Error 对象
return Promise.reject(new Error('失败'))
Promise 返回值处理: oxlint(no-return-wrap)
// ❌ 错误:不必要的 Promise 包装
async function getData() {
return Promise.resolve(data)
}
// ✅ 正确:直接返回值
async function getData() {
return data // async 函数自动包装为 Promise
}
DOM API
使用现代 DOM API: oxlint(prefer-modern-dom-apis, prefer-add-event-listener)
// ❌ 错误:传递不必要的第三个参数
element.addEventListener('click', handler, false)
// ✅ 正确:省略默认参数
element.addEventListener('click', handler)
使用 .remove(): oxlint(prefer-dom-node-remove)
// ❌ 错误:使用 removeChild
element.parentNode?.removeChild(element)
// ✅ 正确:使用 remove
element.remove()
使用 .textContent: oxlint(prefer-dom-node-text-content)
// ❌ 错误:使用 innerText
const text = element.innerText
// ✅ 正确:使用 textContent
const text = element.textContent
使用 dataset: oxlint(prefer-dom-node-dataset)
// ❌ 错误:使用 getAttribute/setAttribute
element.setAttribute('data-id', '123')
const id = element.getAttribute('data-id')
// ✅ 正确:使用 dataset
element.dataset.id = '123'
const id = element.dataset.id
其他现代 API
使用 Set.has(): oxlint(prefer-set-has)
// ❌ 错误:对大数组使用 includes(性能差)
const exists = array.includes(item)
// ✅ 正确:使用 Set
const set = new Set(array)
const exists = set.has(item)
使用 structuredClone(): oxlint(prefer-structured-clone)
// ❌ 错误:使用 JSON 深拷贝
const copy = JSON.parse(JSON.stringify(obj))
// ✅ 正确:使用 structuredClone
const copy = structuredClone(obj)
使用负索引: oxlint(prefer-negative-index)
// ❌ 错误:计算数组最后一个元素
const last = array[array.length - 1]
// ✅ 正确:使用 at
const last = array.at(-1)
使用对象展开: oxlint(prefer-object-spread)
// ❌ 错误:使用 Object.assign
const merged = Object.assign({}, defaults, options)
// ✅ 正确:使用对象展开
const merged = { ...defaults, ...options }
使用剩余参数: oxlint(prefer-rest-params)
// ❌ 错误:使用 arguments
function sum() {
const args = Array.from(arguments)
return args.reduce((a, b) => a + b)
}
// ✅ 正确:使用剩余参数
function sum(...numbers: number[]) {
return numbers.reduce((a, b) => a + b)
}
使用展开运算符: oxlint(prefer-spread)
// ❌ 错误:使用 apply
Math.max.apply(null, numbers)
// ✅ 正确:使用展开运算符
Math.max(...numbers)
使用 trimStart/trimEnd: oxlint(prefer-string-trim-start-end)
// ❌ 错误:使用旧的方法名
str.trimLeft()
str.trimRight()
// ✅ 正确:使用新的标准方法名
str.trimStart()
str.trimEnd()
使用正则 test: oxlint(prefer-regexp-test)
// ❌ 错误:使用 match 仅检查是否匹配
if (str.match(/pattern/)) {
}
// ✅ 正确:使用 test 方法
if (/pattern/.test(str)) {
}
可选的 catch 绑定: oxlint(prefer-optional-catch-binding)
// ❌ 错误:catch 参数未使用但仍然声明
try {
riskyOperation()
} catch (error) {
console.log('操作失败')
}
// ✅ 正确:省略未使用的 catch 参数
try {
riskyOperation()
} catch {
console.log('操作失败')
}
React 开发规范
React 组件开发有其特定的最佳实践。本章规则确保 React 代码的质量和一致性。
函数组件定义: eslint(react/function-component-definition)
组件使用函数声明或函数表达式定义。
// ✅ 正确:使用函数声明
function MyComponent() {
return <div>Hello</div>
}
// ✅ 正确:使用函数表达式
const MyComponent = function () {
return <div>Hello</div>
}
箭头函数组件(仅限 .tsx): eslint(arrow-body-style)
在 .tsx 文件中,箭头函数组件必须显式返回 JSX。
// ✅ 正确:在 .tsx 文件中,必须返回 JSX
const MyComponent = () => {
return <div>Hello</div>
}
// ❌ 错误:在 .tsx 文件中,箭头函数省略 return
const MyComponent = () => <div>Hello</div>
Hooks 规则: eslint(react-hooks/rules-of-hooks) oxlint(rules-of-hooks)
Hooks 必须在组件顶层调用,不能在条件语句、循环或嵌套函数中调用。
// ❌ 错误:条件性调用 Hook
function MyComponent() {
if (condition) {
useEffect(() => {})
}
}
// ✅ 正确:在顶层调用 Hook
function MyComponent() {
useEffect(() => {
if (condition) {
// 逻辑
}
})
}
组件导出: eslint(react-refresh/only-export-components)
组件文件应仅导出组件,导出非组件常量可能影响 Fast Refresh。
// ⚠️ 警告:导出非组件常量可能影响 Fast Refresh
export const API_URL = 'https://api.example.com'
export default function App() {}
// ✅ 更好:仅导出组件
export default function App() {}
// API_URL 移至单独的常量文件
布尔值属性: oxlint(jsx-boolean-value)
布尔属性为 true 时省略值。
// ❌ 错误
<Component enabled={true} />
// ✅ 正确
<Component enabled />
自闭合标签: oxlint(self-closing-comp)
无子元素的标签应使用自闭合形式。
// ❌ 错误
<div></div>
<Component></Component>
// ✅ 正确(无子元素时)
<div />
<Component />
React Namespace: oxlint(react/no-namespace)
避免使用 React namespace,使用短语法。
// ❌ 错误:使用 React namespace
<React.Fragment>
<div />
</React.Fragment>
// ✅ 正确:使用短语法
<>
<div />
</>
禁止特定导入: oxlint(no-restricted-imports)
某些模块的导入受到限制,确保代码架构的一致性。
// ❌ 错误:在 src/**/*.tsx 中默认导入 React
// ✅ 正确:仅允许特定导入和类型导入
import React, { Component, StrictMode } from 'react'
import type { FC } from 'react'
代码质量与安全
本章规则帮助发现潜在问题,提高代码的健壮性和安全性。
禁止 console: oxlint(no-console)
生产代码禁用 console 语句,应使用日志库。
// ❌ 错误
console.log('debug info')
console.error('error')
// ✅ 正确:使用日志库
logger.info('debug info')
logger.error('error')
禁止 alert: oxlint(no-alert)
禁止使用 alert,应使用 UI 组件。
// ❌ 错误
alert('Hello')
// ✅ 正确:使用 UI 组件
showNotification('Hello')
禁止空代码块: oxlint(no-empty)
代码块不能为空,必须有实际逻辑或注释说明。
// ❌ 错误
if (condition) {
}
try {
} catch (e) {}
// ✅ 正确
if (condition) {
handleCondition()
}
try {
riskyOperation()
} catch (e) {
handleError(e)
}
必须处理数组回调返回值: oxlint(array-callback-return)
.map() 等需要返回值的数组方法必须返回值。
// ❌ 错误:.map() 没有返回值
array.map((item) => {
item.process()
})
// ✅ 正确
array.forEach((item) => {
item.process()
})
// 或者
const processed = array.map((item) => item.process())
Guard for...in: oxlint(guard-for-in)
使用 for...in 遍历对象时必须检查属性是否为自有属性。
// ❌ 错误
for (const key in obj) {
console.log(obj[key])
}
// ✅ 正确
for (const key in obj) {
if (Object.hasOwn(obj, key)) {
console.log(obj[key])
}
}
// ✅ 更好:使用 Object.keys()
for (const key of Object.keys(obj)) {
console.log(obj[key])
}
禁止数组索引作为 key: oxlint(no-array-index-key)
React 列表渲染时,key 不应使用数组索引。
// ❌ 错误
{
items.map((item, index) => <div key={index}>{item}</div>)
}
// ✅ 正确
{
items.map((item) => <div key={item.id}>{item}</div>)
}
错误处理: oxlint(catch-error-name, error-message)
捕获的错误应命名为 error,错误对象应包含 message。
// ❌ 错误:捕获的错误命名不规范
try {
} catch (e) {}
// ✅ 正确
try {
} catch (error) {}
禁止多重赋值: oxlint(no-multi-assign)
避免多重赋值,每个变量单独赋值。
// ❌ 错误
const a = (b = c = 1)
// ✅ 正确
const a = 1
const b = 1
const c = 1
数字格式: oxlint(no-zero-fractions, numeric-separators-style)
移除不必要的小数点后零,大数字使用数字分隔符。
// ❌ 错误
const num = 1.0
const large = 1000000
// ✅ 正确
const num = 1
const large = 1_000_000 // 使用数字分隔符
默认参数位置: oxlint(default-param-last)
默认参数应放在最后。
// ❌ 错误:默认参数不在最后
function create(name = 'default', id: number) {}
// ✅ 正确:默认参数放在最后
function create(id: number, name = 'default') {}
未使用的变量和参数: tsc(noUnusedLocals, noUnusedParameters)
移除未使用的变量,未使用的参数可以用下划线前缀。
// ❌ 错误:未使用的变量
function process(data: string) {
const unused = 'value'
return 'result'
}
// ✅ 正确:移除未使用的变量
function process(data: string) {
return 'result'
}
// 💡 如果参数确实不需要使用,可以用下划线前缀
function handler(_event: Event, data: string) {
return data
}
Switch 穿透检查: oxlint(no-fallthrough) tsc(noFallthroughCasesInSwitch)
每个 case 都应有 break/return,防止意外穿透。
// ❌ 错误:case 穿透到下一个 case
switch (status) {
case 'pending':
console.log('pending')
// 穿透到 success
case 'success':
console.log('done')
break
}
// ✅ 正确:每个 case 都有 break/return
switch (status) {
case 'pending':
console.log('pending')
break
case 'success':
console.log('done')
break
}
禁止重复声明: oxlint(no-redeclare)
避免重复声明变量或函数。
// ❌ 错误:重复声明
const value = 1
const value = 2
// ✅ 正确:使用不同的变量名
const value1 = 1
const value2 = 2
禁止在 finally 中返回: oxlint(no-return-in-finally)
finally 块中不应有 return 语句,会覆盖 try/catch 的返回值。
// ❌ 错误:finally 中返回会覆盖 try 的返回值
function process() {
try {
return 'success'
} finally {
return 'override' // 会覆盖前面的返回
}
}
// ✅ 正确:不在 finally 中返回
function process() {
try {
return 'success'
} finally {
cleanup()
}
}
禁止扩展原生对象: oxlint(no-extend-native)
不要扩展原生对象的原型,使用工具函数代替。
// ❌ 错误:扩展原生对象
Array.prototype.myMethod = function () {}
// ✅ 正确:使用工具函数
function myArrayMethod(arr: any[]) {}
禁止使用 __proto__: oxlint(no-proto)
使用 Object.getPrototypeOf/setPrototypeOf 而非 __proto__。
// ❌ 错误:使用 __proto__
const obj = {}
obj.__proto__ = parent
// ✅ 正确:使用标准方法
const obj = {}
Object.setPrototypeOf(obj, parent)
禁止使用 new Buffer: oxlint(no-new-buffer)
使用 Buffer.from/Buffer.alloc 而非已废弃的 new Buffer。
// ❌ 错误:new Buffer 已废弃
const buf = new Buffer('data')
// ✅ 正确:使用 Buffer.from/Buffer.alloc
const buf = Buffer.from('data')
const buf2 = Buffer.alloc(10)
文件名大小写一致: tsc(forceConsistentCasingInFileNames)
导入路径的大小写必须与文件实际大小写一致。
// ❌ 错误:导入路径大小写不一致
// 文件实际是 userService.ts
import { UserService } from './UserService' // Windows 可能工作但 Linux 会失败
// ✅ 正确:保持大小写一致
import { UserService } from './userService'
函数必须有返回值: tsc(noImplicitReturns)
声明了返回类型的函数,所有路径都必须有返回值。
// ❌ 错误:某些路径没有返回值
function getStatus(code: number): string {
if (code === 200) {
return 'success'
}
// 其他情况没有返回
}
// ✅ 正确:所有路径都有返回
function getStatus(code: number): string {
if (code === 200) {
return 'success'
}
return 'error'
}