@foundbyte/eslint-plugin 1.1.0 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -23,7 +23,11 @@ module.exports = {
|
|
|
23
23
|
rules: {
|
|
24
24
|
// 启用所有推荐的规则
|
|
25
25
|
'@foundbyte/arrow-function-padding': 'error',
|
|
26
|
-
'@foundbyte/no-initial-as-assertion': 'error',
|
|
26
|
+
'@foundbyte/no-initial-as-assertion': ['error', {
|
|
27
|
+
excludeVariableAssertions: true,
|
|
28
|
+
excludeTypePatterns: ['^const$'],
|
|
29
|
+
excludeMethodReturns: ['parse', 'querySelector']
|
|
30
|
+
}],
|
|
27
31
|
'@foundbyte/hooks-arrow-function-only': 'error',
|
|
28
32
|
'@foundbyte/type-comment-requirement': ['error', {
|
|
29
33
|
minCommentsPerGroup: 1,
|
|
@@ -55,7 +59,14 @@ module.exports = {
|
|
|
55
59
|
|
|
56
60
|
**配置示例:**
|
|
57
61
|
```javascript
|
|
58
|
-
|
|
62
|
+
// 基本用法
|
|
63
|
+
'@foundbyte/no-initial-as-assertion': 'error'
|
|
64
|
+
|
|
65
|
+
// 高级配置 - 排除特定情况
|
|
66
|
+
'@foundbyte/no-initial-as-assertion': ['error', {
|
|
67
|
+
excludeVariableAssertions: true, // 排除对变量的 as 断言
|
|
68
|
+
excludeMethodReturns: ['map', 'parse'] // 排除特定方法返回类型的 as 断言
|
|
69
|
+
}]
|
|
59
70
|
```
|
|
60
71
|
|
|
61
72
|
**示例:**
|
|
@@ -71,12 +82,41 @@ const a = ref() as Ref<string>
|
|
|
71
82
|
const b = computed(() => {}) as ComputedRef<number>
|
|
72
83
|
const c = reactive({}) as Reactive<object>
|
|
73
84
|
const d = ref({} as { a: string }) // 嵌套的类型断言也会被检测到
|
|
85
|
+
|
|
86
|
+
// ✅ 当 excludeVariableAssertions 为 true 时 - 变量断言被排除
|
|
87
|
+
interface ID { a?: string, b?: string }
|
|
88
|
+
const d: ID = {}
|
|
89
|
+
const e = d as { a?: string } // 不会报错
|
|
90
|
+
|
|
91
|
+
// ✅ 当 excludeMethodReturns 包含 'map' 时 - 数组方法返回类型断言被排除
|
|
92
|
+
const arr = [1, 2, 3]
|
|
93
|
+
const mapped = arr.map(x => x * 2) as number[] // 不会报错
|
|
94
|
+
|
|
95
|
+
// ✅ 当 excludeMethodReturns 包含 'parse' 时 - JSON.parse 返回类型断言被排除
|
|
96
|
+
const jsonString = '{"name": "test"}'
|
|
97
|
+
const parsed = JSON.parse(jsonString) as { name: string } // 不会报错
|
|
74
98
|
```
|
|
75
99
|
|
|
100
|
+
**配置选项:**
|
|
101
|
+
|
|
102
|
+
- `excludeVariableAssertions` (boolean, 默认 false): 是否排除对变量的 as 断言
|
|
103
|
+
- 当为 true 时,类似 `const a = variable as Type` 的情况不会被报错
|
|
104
|
+
- 适用于变量到变量的类型断言场景
|
|
105
|
+
|
|
106
|
+
- `excludeMethodReturns` (array, 默认 []): 排除特定方法返回类型的 as 断言
|
|
107
|
+
- 支持数组方法:`map`, `filter`, `reduce` 等
|
|
108
|
+
- 支持全局函数:`JSON.parse` 等
|
|
109
|
+
- 支持链式调用:`arr.map().filter() as Type`
|
|
110
|
+
|
|
111
|
+
- `excludeTypePatterns` (array, 默认 []): 排除匹配正则表达式模式的类型断言
|
|
112
|
+
- 支持正则表达式字符串数组
|
|
113
|
+
- 用于排除特定的类型断言模式,如 `as const`
|
|
114
|
+
- 示例:`['^const$', '^Readonly<.*>$']` 可以排除 `as const` 和 `as Readonly<...>`
|
|
115
|
+
|
|
76
116
|
**注意:**
|
|
77
117
|
- 该规则检查变量声明时的所有类型断言,包括嵌套在函数调用参数中的类型断言
|
|
78
118
|
- 推荐使用显式类型注解,因为类型断言会绕过 TypeScript 的类型检查
|
|
79
|
-
-
|
|
119
|
+
- 对于函数调用返回值的类型断言,可以通过配置选项排除特定情况
|
|
80
120
|
|
|
81
121
|
### arrow-function-padding
|
|
82
122
|
|
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
import { Rule } from 'eslint'
|
|
2
2
|
|
|
3
|
-
export interface NoInitialAsAssertionOptions {
|
|
3
|
+
export interface NoInitialAsAssertionOptions {
|
|
4
|
+
/**
|
|
5
|
+
* 是否排除对变量的 as 断言
|
|
6
|
+
* @default false
|
|
7
|
+
*/
|
|
8
|
+
excludeVariableAssertions?: boolean
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 排除特定方法返回类型的 as 断言
|
|
12
|
+
* @default []
|
|
13
|
+
*/
|
|
14
|
+
excludeMethodReturns?: string[]
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 排除匹配正则表达式模式的类型断言
|
|
18
|
+
* @default []
|
|
19
|
+
*/
|
|
20
|
+
excludeTypePatterns?: string[]
|
|
21
|
+
}
|
|
4
22
|
|
|
5
23
|
/**
|
|
6
24
|
* 禁止在初始变量定义时使用 as 类型断言
|
|
@@ -7,13 +7,126 @@ module.exports = {
|
|
|
7
7
|
recommended: false,
|
|
8
8
|
},
|
|
9
9
|
fixable: null,
|
|
10
|
-
schema: [
|
|
10
|
+
schema: [
|
|
11
|
+
{
|
|
12
|
+
type: 'object',
|
|
13
|
+
properties: {
|
|
14
|
+
excludeVariableAssertions: {
|
|
15
|
+
type: 'boolean',
|
|
16
|
+
default: false,
|
|
17
|
+
description: '是否排除对变量的 as 断言'
|
|
18
|
+
},
|
|
19
|
+
excludeMethodReturns: {
|
|
20
|
+
type: 'array',
|
|
21
|
+
items: {
|
|
22
|
+
type: 'string'
|
|
23
|
+
},
|
|
24
|
+
default: [],
|
|
25
|
+
description: '排除特定方法返回类型的 as 断言'
|
|
26
|
+
},
|
|
27
|
+
excludeTypePatterns: {
|
|
28
|
+
type: 'array',
|
|
29
|
+
items: {
|
|
30
|
+
type: 'string'
|
|
31
|
+
},
|
|
32
|
+
default: [],
|
|
33
|
+
description: '排除匹配正则表达式模式的类型断言'
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
additionalProperties: false
|
|
37
|
+
}
|
|
38
|
+
],
|
|
11
39
|
messages: {
|
|
12
40
|
noInitialAsAssertion: '禁止在初始变量定义时使用 as 类型断言,请使用显式类型注解',
|
|
13
41
|
},
|
|
14
42
|
},
|
|
15
43
|
|
|
16
44
|
create(context) {
|
|
45
|
+
const options = context.options[0] || {};
|
|
46
|
+
const excludeVariableAssertions = options.excludeVariableAssertions || false;
|
|
47
|
+
const excludeMethodReturns = options.excludeMethodReturns || [];
|
|
48
|
+
const excludeTypePatterns = options.excludeTypePatterns || [];
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 检查是否是变量断言的情况
|
|
52
|
+
* @param {Object} node TSAsExpression 节点
|
|
53
|
+
* @returns {boolean}
|
|
54
|
+
*/
|
|
55
|
+
function isVariableAssertion(node) {
|
|
56
|
+
// 检查是否是变量断言:const a = variable as Type
|
|
57
|
+
// 当 excludeVariableAssertions 为 true 时,返回 true 表示要排除
|
|
58
|
+
return node.expression &&
|
|
59
|
+
node.expression.type === 'Identifier' &&
|
|
60
|
+
excludeVariableAssertions;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 检查是否是排除的方法返回类型断言
|
|
65
|
+
* @param {Object} node TSAsExpression 节点
|
|
66
|
+
* @returns {boolean}
|
|
67
|
+
*/
|
|
68
|
+
function isExcludedMethodReturn(node) {
|
|
69
|
+
if (!node.expression || node.expression.type !== 'CallExpression') {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const callExpression = node.expression;
|
|
74
|
+
if (callExpression.callee && callExpression.callee.type === 'MemberExpression') {
|
|
75
|
+
// 处理数组方法如 arr.map().filter() as Type
|
|
76
|
+
const methodName = callExpression.callee.property && callExpression.callee.property.name;
|
|
77
|
+
if (methodName && excludeMethodReturns.includes(methodName)) {
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
} else if (callExpression.callee && callExpression.callee.type === 'Identifier') {
|
|
81
|
+
// 处理全局函数如 JSON.parse() as Type
|
|
82
|
+
const functionName = callExpression.callee.name;
|
|
83
|
+
if (functionName && excludeMethodReturns.includes(functionName)) {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* 检查是否是 as const 断言
|
|
93
|
+
* @param {Object} node TSAsExpression 节点
|
|
94
|
+
* @returns {boolean}
|
|
95
|
+
*/
|
|
96
|
+
function isConstAssertion(node) {
|
|
97
|
+
// 检查是否是 as const 断言
|
|
98
|
+
return node.typeAnnotation &&
|
|
99
|
+
node.typeAnnotation.type === 'TSTypeReference' &&
|
|
100
|
+
node.typeAnnotation.typeName &&
|
|
101
|
+
node.typeAnnotation.typeName.name === 'const';
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* 检查是否匹配排除的类型模式
|
|
106
|
+
* @param {Object} node TSAsExpression 节点
|
|
107
|
+
* @returns {boolean}
|
|
108
|
+
*/
|
|
109
|
+
function matchesExcludedTypePattern(node) {
|
|
110
|
+
if (excludeTypePatterns.length === 0) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// 获取类型注解的源代码
|
|
115
|
+
const sourceCode = context.getSourceCode();
|
|
116
|
+
const typeAnnotationText = sourceCode.getText(node.typeAnnotation);
|
|
117
|
+
|
|
118
|
+
// 检查是否匹配任何排除模式
|
|
119
|
+
return excludeTypePatterns.some(pattern => {
|
|
120
|
+
try {
|
|
121
|
+
const regex = new RegExp(pattern);
|
|
122
|
+
return regex.test(typeAnnotationText);
|
|
123
|
+
} catch (error) {
|
|
124
|
+
// 如果正则表达式无效,忽略该模式
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
17
130
|
/**
|
|
18
131
|
* 检查变量声明是否使用了 as 类型断言
|
|
19
132
|
* @param {Object} node AST节点
|
|
@@ -30,6 +143,21 @@ module.exports = {
|
|
|
30
143
|
if (declaration.init) {
|
|
31
144
|
// 检查初始化表达式是否直接是类型断言
|
|
32
145
|
if (declaration.init.type === 'TSAsExpression') {
|
|
146
|
+
// 排除变量断言的情况
|
|
147
|
+
if (isVariableAssertion(declaration.init)) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// 排除特定方法返回类型的断言
|
|
152
|
+
if (isExcludedMethodReturn(declaration.init)) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// 排除匹配正则表达式模式的类型断言
|
|
157
|
+
if (matchesExcludedTypePattern(declaration.init)) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
33
161
|
// 对于直接的类型断言,应该限制
|
|
34
162
|
// 例如:const a = 1 as number, const a = {} as { s: string }
|
|
35
163
|
context.report({
|
|
@@ -43,6 +171,16 @@ module.exports = {
|
|
|
43
171
|
if (declaration.init.arguments && declaration.init.arguments.length > 0) {
|
|
44
172
|
for (const arg of declaration.init.arguments) {
|
|
45
173
|
if (arg.type === 'TSAsExpression') {
|
|
174
|
+
// 排除变量断言的情况
|
|
175
|
+
if (isVariableAssertion(arg)) {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// 排除特定方法返回类型的断言
|
|
180
|
+
if (isExcludedMethodReturn(arg)) {
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
|
|
46
184
|
// 对于函数参数中的类型断言,应该限制
|
|
47
185
|
// 例如:ref({} as string) 中的 {} as string
|
|
48
186
|
context.report({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@foundbyte/eslint-plugin",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "ESLint plugin for enforcing code style rules including arrow function padding, Vue script lines limit, and TypeScript type comment requirements",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|