ESLint plugin for enforcing code style rules including arrow function padding, Vue script lines limit, and TypeScript type comment requirements
npm install @foundbyte/eslint-plugin一个为 FoundByte 项目定制的 ESLint 插件,提供了一系列代码质量检查和最佳实践规则。
``bash`
npm install @foundbyte/eslint-plugin --save-dev或
yarn add @foundbyte/eslint-plugin --dev或
pnpm add @foundbyte/eslint-plugin --dev
在 ESLint 配置文件中添加插件:
`javascript`
// .eslintrc.js
module.exports = {
plugins: ['@foundbyte'],
rules: {
// 启用所有推荐的规则
'@foundbyte/arrow-function-padding': 'error',
'@foundbyte/no-initial-as-assertion': ['error', {
excludeVariableAssertions: true,
excludeTypePatterns: ['^const$', '^[A-Z]$', '^unknown$'],
excludeMethodReturns: ['parse', 'querySelector']
}],
'@foundbyte/hooks-arrow-function-only': 'error',
'@foundbyte/type-comment-requirement': ['error', {
minCommentsPerGroup: 1,
groupSize: 8,
includeTypes: true,
includeInterfaces: true,
}],
'@foundbyte/hooks-export-limit': ['error', { maxExports: 20 }],
'@foundbyte/vue-script-lines-limit': ['error', { maxLines: 300 }],
'@foundbyte/tag-attribute-restriction': ['error', {
restrictions: [
{
tag: 'image',
attributes: [
{ name: 'mode', disallowedValues: [''] },
]
}
]
}],
'@foundbyte/no-restricted-components': ['error', [
{
pattern: '/^a-(textarea|table)$/',
message: '禁止使用的Arco Design组件,请使用Keyblade组件替代'
}
]],
'@foundbyte/no-excessive-any': ['error', { maxAnyCount: 5 }],
'@foundbyte/no-disabled-comments': ['error', {
patterns: [
'/^\\seslint-disable-next-line\\s$/',
'/^\\seslint-disable\\s$/',
'/no-explicit-any/',
'/vue-script-lines-limit/',
'/hooks-export-limit/'
]}]
},
}
限制 any 类型的使用次数,包括:any
- 显式 类型any
- 隐式 类型(没有类型注解且无法推导类型的变量)any
- 泛型类型中的 (如 Ref, Promise, any[] 等)
配置示例:
`javascript
// 基本用法 - 限制 any 类型最多使用 5 次
'@foundbyte/no-excessive-any': ['error', { maxAnyCount: 5 }]
// 严格模式 - 禁止使用 any 类型
'@foundbyte/no-excessive-any': ['error', { maxAnyCount: 0 }]
// 宽松模式 - 允许最多 10 次 any 类型使用
'@foundbyte/no-excessive-any': ['error', { maxAnyCount: 10 }]
`
检测范围:
- 显式 any 类型:const a: any = 1any
- 隐式 类型:const a
- 没有初始值的变量声明:const a = ref()
- 没有参数的函数调用:any
- 泛型类型中的 :Ref
- , Promise, Arrayany[]
- , string | any, T extends anyany
- 类型别名和接口中的
智能类型推导:
以下情况不计入 any 使用:
- 有初始值的变量:const a = 1, const b = ref(60)const obj = { name: 'test' }
- 对象字面量:const arr = [1, 2, 3]
- 数组字面量:const fn = () => {}
- 函数表达式和箭头函数:const a = useRegex(phoneNum, 'phone-number')
- 有参数的函数调用:const value = ref.value
- 属性访问:const res = await ssoMobileAuth(...)
- 异步函数调用:
示例:
`typescript
// ✅ 正确 - 当 maxAnyCount 为 3 时
const a: any = 1
const b: any = 'hello'
const c = ref() // 隐式 any
// ❌ 错误 - 当 maxAnyCount 为 3 时,第 4 次 any 使用会报错
const a: any = 1
const b: any = 'hello'
const c = ref() // 隐式 any
const d: any = true // 错误:any 类型使用次数不能超过 3 次,当前使用了 4 次
// ✅ 正确 - 使用具体类型
const a: number = 1
const b: string = 'hello'
const c: number = 123
const d: boolean = true
// ✅ 正确 - 可以推导类型的情况不计入 any 使用
const time = ref(60) // 有数字字面量参数,可以推导类型
const messages = { noMore: 'text' } // 对象字面量,可以推导类型
const fn = () => {} // 函数表达式,可以推导类型
const res = await ssoMobileAuth({ nonce: nonce.value }) // 异步函数调用,可以推导类型
// ❌ 错误 - 无法推导类型的隐式 any
const a // 没有初始值
const b = ref() // 没有参数的函数调用
const c: Ref
// ❌ 错误 - 泛型类型中的 any
type MyType
interface MyInterface {
value: any
ref: Ref
array: any[]
union: string | any
generic: Promise
}
`
配置选项:
- maxAnyCount (number, 默认 0): 允许的 any 类型最大使用次数any
- 设置为 0 表示完全禁止使用 类型
- 设置为正整数表示允许的最大次数
注意:
- 该规则统计文件中所有 any 类型的使用,包括显式和隐式
- 规则会智能识别可以类型推导的情况,避免过度检测
- 建议在 TypeScript 项目中设置合理的限制,鼓励使用具体类型
- 对于遗留代码迁移,可以设置较高的限制值,然后逐步减少
禁止在初始变量定义时使用 as 类型断言,强制使用显式类型注解。
配置示例:
`javascript
// 基本用法
'@foundbyte/no-initial-as-assertion': 'error'
// 高级配置 - 排除特定情况
'@foundbyte/no-initial-as-assertion': ['error', {
excludeVariableAssertions: true, // 排除对变量的 as 断言
excludeMethodReturns: ['map', 'parse'] // 排除特定方法返回类型的 as 断言
}]
`
示例:
`typescript
// ✅ 正确 - 使用显式类型注解
const a: Ref
const b: ComputedRef
const c = ref() // 没有类型断言
const d: Ref<{ a: string }> = ref({}) // 使用显式类型注解
// ❌ 错误 - 在变量声明时使用 as 类型断言
const a = ref() as Ref
const b = computed(() => {}) as ComputedRef
const c = reactive({}) as Reactive
// ✅ 当 excludeVariableAssertions 为 true 时 - 变量断言被排除
interface ID { a?: string, b?: string }
const d: ID = {}
const e = d as { a?: string } // 不会报错
// ✅ 当 excludeMethodReturns 包含 'map' 时 - 数组方法返回类型断言被排除
const arr = [1, 2, 3]
const mapped = arr.map(x => x * 2) as number[] // 不会报错
// ✅ 当 excludeMethodReturns 包含 'parse' 时 - JSON.parse 返回类型断言被排除
const jsonString = '{"name": "test"}'
const parsed = JSON.parse(jsonString) as { name: string } // 不会报错
`
配置选项:
- excludeVariableAssertions (boolean, 默认 false): 是否排除对变量的 as 断言const a = variable as Type
- 当为 true 时,类似 的情况不会被报错
- 适用于变量到变量的类型断言场景
- excludeMethodReturns (array, 默认 []): 排除特定方法返回类型的 as 断言map
- 支持数组方法:, filter, reduce 等JSON.parse
- 支持全局函数: 等arr.map().filter() as Type
- 支持链式调用:
- excludeTypePatterns (array, 默认 []): 排除匹配正则表达式模式的类型断言as const
- 支持正则表达式字符串数组
- 用于排除特定的类型断言模式,如 ['^const$', '^Readonly<.*>$']
- 示例: 可以排除 as const 和 as Readonly<...>
注意:
- 该规则检查变量声明时的所有类型断言,包括嵌套在函数调用参数中的类型断言
- 推荐使用显式类型注解,因为类型断言会绕过 TypeScript 的类型检查
- 对于函数调用返回值的类型断言,可以通过配置选项排除特定情况
要求函数体之间必须空行。
配置示例:
`javascript`
'@foundbyte/arrow-function-padding': 'error' // 总是要求空行
示例:
`javascript
// ✅ 正确
const one = () => {}
const two = () => {}
// ❌ 错误
const one = () => {}
const two = () => {}
`
要求 React/Vue Hooks 必须使用箭头函数。
示例:
`javascript
// ✅ 正确
const useMyHook = () => {
const [state, setState] = useState()
return state
};
// ❌ 错误
function useMyHook() {
const [state, setState] = useState()
return state
}
`
限制 Hooks 文件内部成员的导出数量。
配置示例:
`javascript`
'@foundbyte/hooks-export-limit': ['error', { maxExports: 2 }] // 最大允许的导出数量
示例:
`javascript
// ✅ 正确 - 只有一个导出
export const useMyHook = () => {
const a = ''
return {
a
}
}
// ❌ 错误 - 多个导出
export const useMyHook = () => {
const a = ''
const b = ''
return {
a,
b
}
}
`
要求复杂类型定义必须有类型注释。
配置示例:
`javascript`
'@foundbyte/type-comment-requirement': ['error', {
/* 至少1个注释 /
minCommentsPerGroup: 1,
/* 每3个类型 /
groupSize: 3,
}]
示例:
`ts
// ✅ 正确 - 有类型注释
interface user {
/* 姓名 /
name: 'John'
/* 年龄 /
age: 30
ddress: { city: 'NY', zip: '10001' }
}
// ❌ 错误 - 复杂对象没有类型注释
interface user { name: 'John', age: 30, address: { city: 'NY', zip: '10001' } }
`
限制 Vue 单文件组件中
`
禁止使用特定的 ESLint 禁用注释,如 eslint-disable、eslint-disable-next-line 等。支持 JavaScript/TypeScript 注释和 Vue 模板中的 HTML 注释。
配置示例:
`javascript`
'@foundbyte/no-disabled-comments': ['error', {
patterns: [
'/^\\seslint-disable-next-line\\s$/', // 匹配 "eslint-disable-next-line"(不加后缀规则)
'/^\\seslint-disable\\s$/', // 匹配 "eslint-disable"
'/no-explicit-any/', // 模糊匹配包含 "no-explicit-any" 的注释
'/vue-script-lines-limit/', // 模糊匹配包含 "vue-script-lines-limit" 的注释
'/hooks-export-limit/' // 模糊匹配包含 "hooks-export-limit" 的注释
]
}]
支持的注释类型:
1. JavaScript/TypeScript 注释:
- 行注释:// eslint-disable-next-line/ eslint-disable /
- 块注释:
2. Vue 模板中的 HTML 注释:
-
-
检测的注释内容:
- 注释内容经过 trim() 处理,去除前后空白字符
- 支持正则表达式模糊匹配
- 支持多个模式同时匹配
示例:
`javascript
// ✅ 正确 - 不匹配任何禁用模式
// 普通注释
/ 另一个普通注释 /
// ❌ 错误 - 匹配 "eslint-disable-next-line" 模式
// eslint-disable-next-line
console.log('test');
// ❌ 错误 - 匹配 "eslint-disable" 模式
/ eslint-disable /
console.log('test');
// ❌ 错误 - 模糊匹配包含 "no-explicit-any" 的注释
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const x: any = 1;
// ❌ 错误 - 模糊匹配包含 "vue-script-lines-limit" 的注释
/ eslint-disable vue-script-lines-limit /
`
`vue
`
配置选项:
- patterns (array, 必填): 要禁用的注释模式列表'/^\\seslint-disable-next-line\\s$/'
- 可以是正则表达式字符串,如
- 可以是普通字符串,会自动转义并创建正则表达式
- 支持多个模式,只要匹配任意一个模式就会报错
- caseSensitive (boolean, 默认 false): 是否区分大小写
- 当为 false 时,匹配不区分大小写
- 当为 true 时,匹配区分大小写
错误消息:
- 禁止使用注释: "eslint-disable-next-line",如必须排除,请在 .eslintignore 中排除,并写清楚排除原因禁止使用注释: "eslint-disable",如必须排除,请在 .eslintignore 中排除,并写清楚排除原因
- 禁止使用注释: "eslint-disable-next-line @typescript-eslint/no-explicit-any",如必须排除,请在 .eslintignore 中排除,并写清楚排除原因
-
注意:
- 该规则会检查所有 JavaScript/TypeScript 注释和 Vue 模板中的 HTML 注释
- 对于 Vue 文件,规则会同时检查