@typescript-eslint/eslint-plugin 8.31.2-alpha.7 → 8.32.0
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/dist/configs/eslintrc/all.d.ts +1 -0
- package/dist/configs/eslintrc/all.d.ts.map +1 -1
- package/dist/configs/eslintrc/all.js +1 -0
- package/dist/configs/eslintrc/disable-type-checked.d.ts +1 -0
- package/dist/configs/eslintrc/disable-type-checked.d.ts.map +1 -1
- package/dist/configs/eslintrc/disable-type-checked.js +1 -0
- package/dist/configs/flat/all.d.ts.map +1 -1
- package/dist/configs/flat/all.js +1 -0
- package/dist/configs/flat/disable-type-checked.d.ts.map +1 -1
- package/dist/configs/flat/disable-type-checked.js +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/raw-plugin.d.ts +3 -0
- package/dist/raw-plugin.d.ts.map +1 -1
- package/dist/rules/adjacent-overload-signatures.js +1 -1
- package/dist/rules/await-thenable.js +2 -2
- package/dist/rules/enum-utils/shared.js +1 -1
- package/dist/rules/explicit-function-return-type.js +2 -2
- package/dist/rules/index.d.ts +1 -0
- package/dist/rules/index.d.ts.map +1 -1
- package/dist/rules/index.js +2 -0
- package/dist/rules/member-ordering.js +1 -1
- package/dist/rules/no-confusing-void-expression.js +3 -3
- package/dist/rules/no-duplicate-enum-values.js +2 -2
- package/dist/rules/no-empty-interface.d.ts.map +1 -1
- package/dist/rules/no-empty-interface.js +2 -2
- package/dist/rules/no-floating-promises.d.ts.map +1 -1
- package/dist/rules/no-floating-promises.js +15 -27
- package/dist/rules/no-meaningless-void-operator.js +1 -1
- package/dist/rules/no-misused-promises.js +8 -8
- package/dist/rules/no-misused-spread.js +2 -2
- package/dist/rules/no-redundant-type-constituents.js +2 -2
- package/dist/rules/no-unnecessary-condition.js +5 -5
- package/dist/rules/no-unnecessary-template-expression.d.ts.map +1 -1
- package/dist/rules/no-unnecessary-template-expression.js +3 -3
- package/dist/rules/no-unnecessary-type-assertion.js +2 -2
- package/dist/rules/no-unnecessary-type-conversion.d.ts +4 -0
- package/dist/rules/no-unnecessary-type-conversion.d.ts.map +1 -0
- package/dist/rules/no-unnecessary-type-conversion.js +307 -0
- package/dist/rules/no-unsafe-assignment.js +1 -1
- package/dist/rules/no-unsafe-enum-comparison.js +4 -4
- package/dist/rules/no-unsafe-unary-minus.js +1 -1
- package/dist/rules/non-nullable-type-assertion-style.js +1 -1
- package/dist/rules/only-throw-error.d.ts +1 -0
- package/dist/rules/only-throw-error.d.ts.map +1 -1
- package/dist/rules/only-throw-error.js +47 -0
- package/dist/rules/prefer-find.js +2 -2
- package/dist/rules/prefer-nullish-coalescing.js +2 -2
- package/dist/rules/prefer-optional-chain-utils/analyzeChain.js +1 -1
- package/dist/rules/prefer-optional-chain-utils/checkNullishAndReport.js +1 -1
- package/dist/rules/prefer-optional-chain-utils/gatherLogicalOperands.js +1 -1
- package/dist/rules/prefer-reduce-type-parameter.js +2 -2
- package/dist/rules/prefer-regexp-exec.js +1 -1
- package/dist/rules/prefer-return-this-type.js +1 -1
- package/dist/rules/restrict-plus-operands.js +4 -4
- package/dist/rules/strict-boolean-expressions.d.ts.map +1 -1
- package/dist/rules/strict-boolean-expressions.js +3 -3
- package/dist/rules/switch-exhaustiveness-check.js +4 -4
- package/dist/rules/unbound-method.d.ts.map +1 -1
- package/dist/rules/unbound-method.js +2 -2
- package/dist/rules/use-unknown-in-catch-callback-variable.js +1 -1
- package/dist/util/getWrappingFixer.d.ts +3 -3
- package/dist/util/getWrappingFixer.d.ts.map +1 -1
- package/dist/util/getWrappingFixer.js +5 -2
- package/dist/util/isArrayMethodCallWithPredicate.js +2 -2
- package/dist/util/promiseUtils.d.ts +28 -0
- package/dist/util/promiseUtils.d.ts.map +1 -0
- package/dist/util/promiseUtils.js +98 -0
- package/dist/util/truthinessUtils.js +4 -4
- package/docs/rules/no-unnecessary-type-conversion.mdx +79 -0
- package/docs/rules/only-throw-error.mdx +6 -0
- package/package.json +13 -14
@@ -0,0 +1,4 @@
|
|
1
|
+
type MessageIds = 'suggestRemove' | 'suggestSatisfies' | 'unnecessaryTypeConversion';
|
2
|
+
declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<MessageIds, [], import("../../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
3
|
+
export default _default;
|
4
|
+
//# sourceMappingURL=no-unnecessary-type-conversion.d.ts.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"no-unnecessary-type-conversion.d.ts","sourceRoot":"","sources":["../../src/rules/no-unnecessary-type-conversion.ts"],"names":[],"mappings":"AAgBA,KAAK,UAAU,GACX,eAAe,GACf,kBAAkB,GAClB,2BAA2B,CAAC;;AAEhC,wBAsVG"}
|
@@ -0,0 +1,307 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
3
|
+
if (k2 === undefined) k2 = k;
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
7
|
+
}
|
8
|
+
Object.defineProperty(o, k2, desc);
|
9
|
+
}) : (function(o, m, k, k2) {
|
10
|
+
if (k2 === undefined) k2 = k;
|
11
|
+
o[k2] = m[k];
|
12
|
+
}));
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
15
|
+
}) : function(o, v) {
|
16
|
+
o["default"] = v;
|
17
|
+
});
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
19
|
+
var ownKeys = function(o) {
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
21
|
+
var ar = [];
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
23
|
+
return ar;
|
24
|
+
};
|
25
|
+
return ownKeys(o);
|
26
|
+
};
|
27
|
+
return function (mod) {
|
28
|
+
if (mod && mod.__esModule) return mod;
|
29
|
+
var result = {};
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
31
|
+
__setModuleDefault(result, mod);
|
32
|
+
return result;
|
33
|
+
};
|
34
|
+
})();
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
36
|
+
const utils_1 = require("@typescript-eslint/utils");
|
37
|
+
const tsutils = __importStar(require("ts-api-utils"));
|
38
|
+
const ts = __importStar(require("typescript"));
|
39
|
+
const util_1 = require("../util");
|
40
|
+
exports.default = (0, util_1.createRule)({
|
41
|
+
name: 'no-unnecessary-type-conversion',
|
42
|
+
meta: {
|
43
|
+
type: 'suggestion',
|
44
|
+
docs: {
|
45
|
+
description: 'Disallow conversion idioms when they do not change the type or value of the expression',
|
46
|
+
requiresTypeChecking: true,
|
47
|
+
},
|
48
|
+
fixable: 'code',
|
49
|
+
hasSuggestions: true,
|
50
|
+
messages: {
|
51
|
+
suggestRemove: 'Remove the type conversion.',
|
52
|
+
suggestSatisfies: 'Instead, assert that the value satisfies the {{type}} type.',
|
53
|
+
unnecessaryTypeConversion: '{{violation}} does not change the type or value of the {{type}}.',
|
54
|
+
},
|
55
|
+
schema: [],
|
56
|
+
},
|
57
|
+
defaultOptions: [],
|
58
|
+
create(context) {
|
59
|
+
function doesUnderlyingTypeMatchFlag(type, typeFlag) {
|
60
|
+
return tsutils
|
61
|
+
.unionConstituents(type)
|
62
|
+
.every(t => (0, util_1.isTypeFlagSet)(t, typeFlag));
|
63
|
+
}
|
64
|
+
const services = (0, util_1.getParserServices)(context);
|
65
|
+
function handleUnaryOperator(node, typeFlag, typeString, violation, isDoubleOperator) {
|
66
|
+
const outerNode = isDoubleOperator ? node.parent : node;
|
67
|
+
const type = services.getTypeAtLocation(node.argument);
|
68
|
+
if (doesUnderlyingTypeMatchFlag(type, typeFlag)) {
|
69
|
+
const wrappingFixerParams = {
|
70
|
+
node: outerNode,
|
71
|
+
innerNode: [node.argument],
|
72
|
+
sourceCode: context.sourceCode,
|
73
|
+
};
|
74
|
+
context.report({
|
75
|
+
loc: {
|
76
|
+
start: outerNode.loc.start,
|
77
|
+
end: {
|
78
|
+
column: node.loc.start.column + 1,
|
79
|
+
line: node.loc.start.line,
|
80
|
+
},
|
81
|
+
},
|
82
|
+
messageId: 'unnecessaryTypeConversion',
|
83
|
+
data: { type: typeString, violation },
|
84
|
+
suggest: [
|
85
|
+
{
|
86
|
+
messageId: 'suggestRemove',
|
87
|
+
fix: (0, util_1.getWrappingFixer)(wrappingFixerParams),
|
88
|
+
},
|
89
|
+
{
|
90
|
+
messageId: 'suggestSatisfies',
|
91
|
+
data: { type: typeString },
|
92
|
+
fix: (0, util_1.getWrappingFixer)({
|
93
|
+
...wrappingFixerParams,
|
94
|
+
wrap: expr => `${expr} satisfies ${typeString}`,
|
95
|
+
}),
|
96
|
+
},
|
97
|
+
],
|
98
|
+
});
|
99
|
+
}
|
100
|
+
}
|
101
|
+
return {
|
102
|
+
'AssignmentExpression[operator = "+="]'(node) {
|
103
|
+
if (node.right.type === utils_1.AST_NODE_TYPES.Literal &&
|
104
|
+
node.right.value === '' &&
|
105
|
+
doesUnderlyingTypeMatchFlag(services.getTypeAtLocation(node.left), ts.TypeFlags.StringLike)) {
|
106
|
+
const wrappingFixerParams = {
|
107
|
+
node,
|
108
|
+
innerNode: [node.left],
|
109
|
+
sourceCode: context.sourceCode,
|
110
|
+
};
|
111
|
+
context.report({
|
112
|
+
node,
|
113
|
+
messageId: 'unnecessaryTypeConversion',
|
114
|
+
data: {
|
115
|
+
type: 'string',
|
116
|
+
violation: "Concatenating a string with ''",
|
117
|
+
},
|
118
|
+
suggest: [
|
119
|
+
{
|
120
|
+
messageId: 'suggestRemove',
|
121
|
+
fix: node.parent.type === utils_1.AST_NODE_TYPES.ExpressionStatement
|
122
|
+
? (fixer) => [
|
123
|
+
fixer.removeRange([
|
124
|
+
node.parent.range[0],
|
125
|
+
node.parent.range[1],
|
126
|
+
]),
|
127
|
+
]
|
128
|
+
: (0, util_1.getWrappingFixer)(wrappingFixerParams),
|
129
|
+
},
|
130
|
+
{
|
131
|
+
messageId: 'suggestSatisfies',
|
132
|
+
data: { type: 'string' },
|
133
|
+
fix: (0, util_1.getWrappingFixer)({
|
134
|
+
...wrappingFixerParams,
|
135
|
+
wrap: expr => `${expr} satisfies string`,
|
136
|
+
}),
|
137
|
+
},
|
138
|
+
],
|
139
|
+
});
|
140
|
+
}
|
141
|
+
},
|
142
|
+
'BinaryExpression[operator = "+"]'(node) {
|
143
|
+
if (node.right.type === utils_1.AST_NODE_TYPES.Literal &&
|
144
|
+
node.right.value === '' &&
|
145
|
+
doesUnderlyingTypeMatchFlag(services.getTypeAtLocation(node.left), ts.TypeFlags.StringLike)) {
|
146
|
+
const wrappingFixerParams = {
|
147
|
+
node,
|
148
|
+
innerNode: [node.left],
|
149
|
+
sourceCode: context.sourceCode,
|
150
|
+
};
|
151
|
+
context.report({
|
152
|
+
loc: {
|
153
|
+
start: node.left.loc.end,
|
154
|
+
end: node.loc.end,
|
155
|
+
},
|
156
|
+
messageId: 'unnecessaryTypeConversion',
|
157
|
+
data: {
|
158
|
+
type: 'string',
|
159
|
+
violation: "Concatenating a string with ''",
|
160
|
+
},
|
161
|
+
suggest: [
|
162
|
+
{
|
163
|
+
messageId: 'suggestRemove',
|
164
|
+
fix: (0, util_1.getWrappingFixer)(wrappingFixerParams),
|
165
|
+
},
|
166
|
+
{
|
167
|
+
messageId: 'suggestSatisfies',
|
168
|
+
data: { type: 'string' },
|
169
|
+
fix: (0, util_1.getWrappingFixer)({
|
170
|
+
...wrappingFixerParams,
|
171
|
+
wrap: expr => `${expr} satisfies string`,
|
172
|
+
}),
|
173
|
+
},
|
174
|
+
],
|
175
|
+
});
|
176
|
+
}
|
177
|
+
else if (node.left.type === utils_1.AST_NODE_TYPES.Literal &&
|
178
|
+
node.left.value === '' &&
|
179
|
+
doesUnderlyingTypeMatchFlag(services.getTypeAtLocation(node.right), ts.TypeFlags.StringLike)) {
|
180
|
+
const wrappingFixerParams = {
|
181
|
+
node,
|
182
|
+
innerNode: [node.right],
|
183
|
+
sourceCode: context.sourceCode,
|
184
|
+
};
|
185
|
+
context.report({
|
186
|
+
loc: {
|
187
|
+
start: node.loc.start,
|
188
|
+
end: node.right.loc.start,
|
189
|
+
},
|
190
|
+
messageId: 'unnecessaryTypeConversion',
|
191
|
+
data: {
|
192
|
+
type: 'string',
|
193
|
+
violation: "Concatenating '' with a string",
|
194
|
+
},
|
195
|
+
suggest: [
|
196
|
+
{
|
197
|
+
messageId: 'suggestRemove',
|
198
|
+
fix: (0, util_1.getWrappingFixer)(wrappingFixerParams),
|
199
|
+
},
|
200
|
+
{
|
201
|
+
messageId: 'suggestSatisfies',
|
202
|
+
data: { type: 'string' },
|
203
|
+
fix: (0, util_1.getWrappingFixer)({
|
204
|
+
...wrappingFixerParams,
|
205
|
+
wrap: expr => `${expr} satisfies string`,
|
206
|
+
}),
|
207
|
+
},
|
208
|
+
],
|
209
|
+
});
|
210
|
+
}
|
211
|
+
},
|
212
|
+
CallExpression(node) {
|
213
|
+
const nodeCallee = node.callee;
|
214
|
+
const builtInTypeFlags = {
|
215
|
+
BigInt: ts.TypeFlags.BigIntLike,
|
216
|
+
Boolean: ts.TypeFlags.BooleanLike,
|
217
|
+
Number: ts.TypeFlags.NumberLike,
|
218
|
+
String: ts.TypeFlags.StringLike,
|
219
|
+
};
|
220
|
+
if (nodeCallee.type !== utils_1.AST_NODE_TYPES.Identifier ||
|
221
|
+
!(nodeCallee.name in builtInTypeFlags)) {
|
222
|
+
return;
|
223
|
+
}
|
224
|
+
const typeFlag = builtInTypeFlags[nodeCallee.name];
|
225
|
+
const scope = context.sourceCode.getScope(node);
|
226
|
+
const variable = scope.set.get(nodeCallee.name);
|
227
|
+
if (!!variable?.defs.length ||
|
228
|
+
!doesUnderlyingTypeMatchFlag((0, util_1.getConstrainedTypeAtLocation)(services, node.arguments[0]), typeFlag)) {
|
229
|
+
return;
|
230
|
+
}
|
231
|
+
const wrappingFixerParams = {
|
232
|
+
node,
|
233
|
+
innerNode: [node.arguments[0]],
|
234
|
+
sourceCode: context.sourceCode,
|
235
|
+
};
|
236
|
+
const typeString = nodeCallee.name.toLowerCase();
|
237
|
+
context.report({
|
238
|
+
node: nodeCallee,
|
239
|
+
messageId: 'unnecessaryTypeConversion',
|
240
|
+
data: {
|
241
|
+
type: nodeCallee.name.toLowerCase(),
|
242
|
+
violation: `Passing a ${typeString} to ${nodeCallee.name}()`,
|
243
|
+
},
|
244
|
+
suggest: [
|
245
|
+
{
|
246
|
+
messageId: 'suggestRemove',
|
247
|
+
fix: (0, util_1.getWrappingFixer)(wrappingFixerParams),
|
248
|
+
},
|
249
|
+
{
|
250
|
+
messageId: 'suggestSatisfies',
|
251
|
+
data: { type: typeString },
|
252
|
+
fix: (0, util_1.getWrappingFixer)({
|
253
|
+
...wrappingFixerParams,
|
254
|
+
wrap: expr => `${expr} satisfies ${typeString}`,
|
255
|
+
}),
|
256
|
+
},
|
257
|
+
],
|
258
|
+
});
|
259
|
+
},
|
260
|
+
'CallExpression > MemberExpression.callee > Identifier[name = "toString"].property'(node) {
|
261
|
+
const memberExpr = node.parent;
|
262
|
+
const type = (0, util_1.getConstrainedTypeAtLocation)(services, memberExpr.object);
|
263
|
+
if (doesUnderlyingTypeMatchFlag(type, ts.TypeFlags.StringLike)) {
|
264
|
+
const wrappingFixerParams = {
|
265
|
+
node: memberExpr.parent,
|
266
|
+
innerNode: [memberExpr.object],
|
267
|
+
sourceCode: context.sourceCode,
|
268
|
+
};
|
269
|
+
context.report({
|
270
|
+
loc: {
|
271
|
+
start: memberExpr.property.loc.start,
|
272
|
+
end: memberExpr.parent.loc.end,
|
273
|
+
},
|
274
|
+
messageId: 'unnecessaryTypeConversion',
|
275
|
+
data: {
|
276
|
+
type: 'string',
|
277
|
+
violation: "Calling a string's .toString() method",
|
278
|
+
},
|
279
|
+
suggest: [
|
280
|
+
{
|
281
|
+
messageId: 'suggestRemove',
|
282
|
+
fix: (0, util_1.getWrappingFixer)(wrappingFixerParams),
|
283
|
+
},
|
284
|
+
{
|
285
|
+
messageId: 'suggestSatisfies',
|
286
|
+
data: { type: 'string' },
|
287
|
+
fix: (0, util_1.getWrappingFixer)({
|
288
|
+
...wrappingFixerParams,
|
289
|
+
wrap: expr => `${expr} satisfies string`,
|
290
|
+
}),
|
291
|
+
},
|
292
|
+
],
|
293
|
+
});
|
294
|
+
}
|
295
|
+
},
|
296
|
+
'UnaryExpression[operator = "!"] > UnaryExpression[operator = "!"]'(node) {
|
297
|
+
handleUnaryOperator(node, ts.TypeFlags.BooleanLike, 'boolean', 'Using !! on a boolean', true);
|
298
|
+
},
|
299
|
+
'UnaryExpression[operator = "+"]'(node) {
|
300
|
+
handleUnaryOperator(node, ts.TypeFlags.NumberLike, 'number', 'Using the unary + operator on a number', false);
|
301
|
+
},
|
302
|
+
'UnaryExpression[operator = "~"] > UnaryExpression[operator = "~"]'(node) {
|
303
|
+
handleUnaryOperator(node, ts.TypeFlags.NumberLike, 'number', 'Using ~~ on a number', true);
|
304
|
+
},
|
305
|
+
};
|
306
|
+
},
|
307
|
+
});
|
@@ -168,7 +168,7 @@ exports.default = (0, util_1.createRule)({
|
|
168
168
|
}
|
169
169
|
else if (receiverProperty.key.type === utils_1.AST_NODE_TYPES.TemplateLiteral &&
|
170
170
|
receiverProperty.key.quasis.length === 1) {
|
171
|
-
key =
|
171
|
+
key = receiverProperty.key.quasis[0].value.cooked;
|
172
172
|
}
|
173
173
|
else {
|
174
174
|
// can't figure out the name, so skip it
|
@@ -46,13 +46,13 @@ function typeViolates(leftTypeParts, rightType) {
|
|
46
46
|
(leftEnumValueTypes.has(ts.TypeFlags.String) && isStringLike(rightType)));
|
47
47
|
}
|
48
48
|
function isNumberLike(type) {
|
49
|
-
const typeParts = tsutils.
|
49
|
+
const typeParts = tsutils.intersectionConstituents(type);
|
50
50
|
return typeParts.some(typePart => {
|
51
51
|
return tsutils.isTypeFlagSet(typePart, ts.TypeFlags.Number | ts.TypeFlags.NumberLike);
|
52
52
|
});
|
53
53
|
}
|
54
54
|
function isStringLike(type) {
|
55
|
-
const typeParts = tsutils.
|
55
|
+
const typeParts = tsutils.intersectionConstituents(type);
|
56
56
|
return typeParts.some(typePart => {
|
57
57
|
return tsutils.isTypeFlagSet(typePart, ts.TypeFlags.String | ts.TypeFlags.StringLike);
|
58
58
|
});
|
@@ -116,8 +116,8 @@ exports.default = (0, util_1.createRule)({
|
|
116
116
|
// declare const something: Fruit | Vegetable;
|
117
117
|
// something === Fruit.Apple;
|
118
118
|
// ```
|
119
|
-
const leftTypeParts = tsutils.
|
120
|
-
const rightTypeParts = tsutils.
|
119
|
+
const leftTypeParts = tsutils.unionConstituents(leftType);
|
120
|
+
const rightTypeParts = tsutils.unionConstituents(rightType);
|
121
121
|
// If a type exists in both sides, we consider this comparison safe:
|
122
122
|
//
|
123
123
|
// ```ts
|
@@ -61,7 +61,7 @@ exports.default = util.createRule({
|
|
61
61
|
const argType = util.getConstrainedTypeAtLocation(services, node.argument);
|
62
62
|
const checker = services.program.getTypeChecker();
|
63
63
|
if (tsutils
|
64
|
-
.
|
64
|
+
.unionConstituents(argType)
|
65
65
|
.some(type => !tsutils.isTypeFlagSet(type, ts.TypeFlags.Any |
|
66
66
|
ts.TypeFlags.Never |
|
67
67
|
ts.TypeFlags.BigIntLike |
|
@@ -60,7 +60,7 @@ exports.default = (0, util_1.createRule)({
|
|
60
60
|
if (tsutils.isTypeFlagSet(type, ts.TypeFlags.Any | ts.TypeFlags.Unknown)) {
|
61
61
|
return undefined;
|
62
62
|
}
|
63
|
-
return tsutils.
|
63
|
+
return tsutils.unionConstituents(type);
|
64
64
|
};
|
65
65
|
const couldBeNullish = (type) => {
|
66
66
|
if (type.flags & ts.TypeFlags.TypeParameter) {
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"only-throw-error.d.ts","sourceRoot":"","sources":["../../src/rules/only-throw-error.ts"],"names":[],"mappings":"
|
1
|
+
{"version":3,"file":"only-throw-error.d.ts","sourceRoot":"","sources":["../../src/rules/only-throw-error.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAepD,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE5C,MAAM,MAAM,OAAO,GAAG;IACpB;QACE,KAAK,CAAC,EAAE,oBAAoB,EAAE,CAAC;QAC/B,eAAe,CAAC,EAAE,OAAO,CAAC;QAC1B,gBAAgB,CAAC,EAAE,OAAO,CAAC;QAC3B,oBAAoB,CAAC,EAAE,OAAO,CAAC;KAChC;CACF,CAAC;;AAEF,wBA4JG"}
|
@@ -34,8 +34,10 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
34
34
|
})();
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
36
36
|
const utils_1 = require("@typescript-eslint/utils");
|
37
|
+
const ts_api_utils_1 = require("ts-api-utils");
|
37
38
|
const ts = __importStar(require("typescript"));
|
38
39
|
const util_1 = require("../util");
|
40
|
+
const promiseUtils_1 = require("../util/promiseUtils");
|
39
41
|
exports.default = (0, util_1.createRule)({
|
40
42
|
name: 'only-throw-error',
|
41
43
|
meta: {
|
@@ -59,6 +61,10 @@ exports.default = (0, util_1.createRule)({
|
|
59
61
|
...util_1.typeOrValueSpecifiersSchema,
|
60
62
|
description: 'Type specifiers that can be thrown.',
|
61
63
|
},
|
64
|
+
allowRethrowing: {
|
65
|
+
type: 'boolean',
|
66
|
+
description: 'Whether to allow rethrowing caught values that are not `Error` objects.',
|
67
|
+
},
|
62
68
|
allowThrowingAny: {
|
63
69
|
type: 'boolean',
|
64
70
|
description: 'Whether to always allow throwing values typed as `any`.',
|
@@ -74,6 +80,7 @@ exports.default = (0, util_1.createRule)({
|
|
74
80
|
defaultOptions: [
|
75
81
|
{
|
76
82
|
allow: [],
|
83
|
+
allowRethrowing: true,
|
77
84
|
allowThrowingAny: true,
|
78
85
|
allowThrowingUnknown: true,
|
79
86
|
},
|
@@ -81,11 +88,51 @@ exports.default = (0, util_1.createRule)({
|
|
81
88
|
create(context, [options]) {
|
82
89
|
const services = (0, util_1.getParserServices)(context);
|
83
90
|
const allow = options.allow;
|
91
|
+
function isRethrownError(node) {
|
92
|
+
if (node.type !== utils_1.AST_NODE_TYPES.Identifier) {
|
93
|
+
return false;
|
94
|
+
}
|
95
|
+
const scope = context.sourceCode.getScope(node);
|
96
|
+
const smVariable = (0, util_1.nullThrows)((0, util_1.findVariable)(scope, node), `Variable ${node.name} should exist in scope manager`);
|
97
|
+
const variableDefinitions = smVariable.defs.filter(def => def.isVariableDefinition);
|
98
|
+
if (variableDefinitions.length !== 1) {
|
99
|
+
return false;
|
100
|
+
}
|
101
|
+
const def = smVariable.defs[0];
|
102
|
+
// try { /* ... */ } catch (x) { throw x; }
|
103
|
+
if (def.node.type === utils_1.AST_NODE_TYPES.CatchClause) {
|
104
|
+
return true;
|
105
|
+
}
|
106
|
+
// promise.catch(x => { throw x; })
|
107
|
+
// promise.then(onFulfilled, x => { throw x; })
|
108
|
+
if (def.node.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression &&
|
109
|
+
def.node.params.length >= 1 &&
|
110
|
+
def.node.params[0] === def.name &&
|
111
|
+
def.node.parent.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
112
|
+
const callExpression = def.node.parent;
|
113
|
+
const parsedPromiseHandlingCall = (0, promiseUtils_1.parseCatchCall)(callExpression, context) ??
|
114
|
+
(0, promiseUtils_1.parseThenCall)(callExpression, context);
|
115
|
+
if (parsedPromiseHandlingCall != null) {
|
116
|
+
const { object, onRejected } = parsedPromiseHandlingCall;
|
117
|
+
if (onRejected === def.node) {
|
118
|
+
const tsObjectNode = services.esTreeNodeToTSNodeMap.get(object);
|
119
|
+
// make sure we're actually dealing with a promise
|
120
|
+
if ((0, ts_api_utils_1.isThenableType)(services.program.getTypeChecker(), tsObjectNode)) {
|
121
|
+
return true;
|
122
|
+
}
|
123
|
+
}
|
124
|
+
}
|
125
|
+
}
|
126
|
+
return false;
|
127
|
+
}
|
84
128
|
function checkThrowArgument(node) {
|
85
129
|
if (node.type === utils_1.AST_NODE_TYPES.AwaitExpression ||
|
86
130
|
node.type === utils_1.AST_NODE_TYPES.YieldExpression) {
|
87
131
|
return;
|
88
132
|
}
|
133
|
+
if (options.allowRethrowing && isRethrownError(node)) {
|
134
|
+
return;
|
135
|
+
}
|
89
136
|
const type = services.getTypeAtLocation(node);
|
90
137
|
if ((0, util_1.typeMatchesSomeSpecifier)(type, allow, services.program)) {
|
91
138
|
return;
|
@@ -109,7 +109,7 @@ exports.default = (0, util_1.createRule)({
|
|
109
109
|
*/
|
110
110
|
function isArrayish(type) {
|
111
111
|
let isAtLeastOneArrayishComponent = false;
|
112
|
-
for (const unionPart of tsutils.
|
112
|
+
for (const unionPart of tsutils.unionConstituents(type)) {
|
113
113
|
if (tsutils.isIntrinsicNullType(unionPart) ||
|
114
114
|
tsutils.isIntrinsicUndefinedType(unionPart)) {
|
115
115
|
continue;
|
@@ -117,7 +117,7 @@ exports.default = (0, util_1.createRule)({
|
|
117
117
|
// apparently checker.isArrayType(T[] & S[]) => false.
|
118
118
|
// so we need to check the intersection parts individually.
|
119
119
|
const isArrayOrIntersectionThereof = tsutils
|
120
|
-
.
|
120
|
+
.intersectionConstituents(unionPart)
|
121
121
|
.every(intersectionPart => checker.isArrayType(intersectionPart) ||
|
122
122
|
checker.isTupleType(intersectionPart));
|
123
123
|
if (!isArrayOrIntersectionThereof) {
|
@@ -195,9 +195,9 @@ exports.default = (0, util_1.createRule)({
|
|
195
195
|
return false;
|
196
196
|
}
|
197
197
|
if (tsutils
|
198
|
-
.
|
198
|
+
.typeConstituents(type)
|
199
199
|
.some(t => tsutils
|
200
|
-
.
|
200
|
+
.intersectionConstituents(t)
|
201
201
|
.some(t => tsutils.isTypeFlagSet(t, ignorableFlags)))) {
|
202
202
|
return false;
|
203
203
|
}
|
@@ -43,7 +43,7 @@ const compareNodes_1 = require("./compareNodes");
|
|
43
43
|
const gatherLogicalOperands_1 = require("./gatherLogicalOperands");
|
44
44
|
function includesType(parserServices, node, typeFlagIn) {
|
45
45
|
const typeFlag = typeFlagIn | ts.TypeFlags.Any | ts.TypeFlags.Unknown;
|
46
|
-
const types = (0, ts_api_utils_1.
|
46
|
+
const types = (0, ts_api_utils_1.unionConstituents)(parserServices.getTypeAtLocation(node));
|
47
47
|
for (const type of types) {
|
48
48
|
if ((0, util_1.isTypeFlagSet)(type, typeFlag)) {
|
49
49
|
return true;
|
@@ -39,7 +39,7 @@ const ts_api_utils_1 = require("ts-api-utils");
|
|
39
39
|
const ts = __importStar(require("typescript"));
|
40
40
|
function checkNullishAndReport(context, parserServices, { requireNullish }, maybeNullishNodes, descriptor) {
|
41
41
|
if (!requireNullish ||
|
42
|
-
maybeNullishNodes.some(node => (0, ts_api_utils_1.
|
42
|
+
maybeNullishNodes.some(node => (0, ts_api_utils_1.unionConstituents)(parserServices.getTypeAtLocation(node)).some(t => (0, type_utils_1.isTypeFlagSet)(t, ts.TypeFlags.Null | ts.TypeFlags.Undefined)))) {
|
43
43
|
context.report(descriptor);
|
44
44
|
}
|
45
45
|
}
|
@@ -72,7 +72,7 @@ var NullishComparisonType;
|
|
72
72
|
const NULLISH_FLAGS = ts.TypeFlags.Null | ts.TypeFlags.Undefined;
|
73
73
|
function isValidFalseBooleanCheckType(node, disallowFalseyLiteral, parserServices, options) {
|
74
74
|
const type = parserServices.getTypeAtLocation(node);
|
75
|
-
const types = (0, ts_api_utils_1.
|
75
|
+
const types = (0, ts_api_utils_1.unionConstituents)(type);
|
76
76
|
if (disallowFalseyLiteral &&
|
77
77
|
/*
|
78
78
|
```
|
@@ -56,9 +56,9 @@ exports.default = (0, util_1.createRule)({
|
|
56
56
|
const checker = services.program.getTypeChecker();
|
57
57
|
function isArrayType(type) {
|
58
58
|
return tsutils
|
59
|
-
.
|
59
|
+
.unionConstituents(type)
|
60
60
|
.every(unionPart => tsutils
|
61
|
-
.
|
61
|
+
.intersectionConstituents(unionPart)
|
62
62
|
.every(t => checker.isArrayType(t) || checker.isTupleType(t)));
|
63
63
|
}
|
64
64
|
return {
|
@@ -147,7 +147,7 @@ exports.default = (0, util_1.createRule)({
|
|
147
147
|
});
|
148
148
|
}
|
149
149
|
const argumentType = services.getTypeAtLocation(argumentNode);
|
150
|
-
const argumentTypes = collectArgumentTypes(tsutils.
|
150
|
+
const argumentTypes = collectArgumentTypes(tsutils.unionConstituents(argumentType));
|
151
151
|
switch (argumentTypes) {
|
152
152
|
case ArgumentType.RegExp:
|
153
153
|
return context.report({
|
@@ -73,7 +73,7 @@ exports.default = (0, util_1.createRule)({
|
|
73
73
|
}
|
74
74
|
function isThisSpecifiedInParameters(originalFunc) {
|
75
75
|
const firstArg = originalFunc.params.at(0);
|
76
|
-
return
|
76
|
+
return (firstArg?.type === utils_1.AST_NODE_TYPES.Identifier && firstArg.name === 'this');
|
77
77
|
}
|
78
78
|
function isFunctionReturningThis(originalFunc, originalClass) {
|
79
79
|
if (isThisSpecifiedInParameters(originalFunc)) {
|
@@ -156,7 +156,7 @@ exports.default = (0, util_1.createRule)({
|
|
156
156
|
continue;
|
157
157
|
}
|
158
158
|
// RegExps also contain ts.TypeFlags.Any & ts.TypeFlags.Object
|
159
|
-
for (const subBaseType of tsutils.
|
159
|
+
for (const subBaseType of tsutils.unionConstituents(baseType)) {
|
160
160
|
const typeName = (0, util_1.getTypeName)(typeChecker, subBaseType);
|
161
161
|
if (typeName === 'RegExp'
|
162
162
|
? !allowRegExp ||
|
@@ -221,11 +221,11 @@ exports.default = (0, util_1.createRule)({
|
|
221
221
|
});
|
222
222
|
function isDeeplyObjectType(type) {
|
223
223
|
return type.isIntersection()
|
224
|
-
? tsutils.
|
225
|
-
: tsutils.
|
224
|
+
? tsutils.intersectionConstituents(type).every(tsutils.isObjectType)
|
225
|
+
: tsutils.unionConstituents(type).every(tsutils.isObjectType);
|
226
226
|
}
|
227
227
|
function isTypeFlagSetInUnion(type, flag) {
|
228
228
|
return tsutils
|
229
|
-
.
|
229
|
+
.unionConstituents(type)
|
230
230
|
.some(subType => tsutils.isTypeFlagSet(subType, flag));
|
231
231
|
}
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"strict-boolean-expressions.d.ts","sourceRoot":"","sources":["../../src/rules/strict-boolean-expressions.ts"],"names":[],"mappings":"AAsBA,MAAM,MAAM,OAAO,GAAG;IACpB;QACE,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,oBAAoB,CAAC,EAAE,OAAO,CAAC;QAC/B,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAC5B,mBAAmB,CAAC,EAAE,OAAO,CAAC;QAC9B,mBAAmB,CAAC,EAAE,OAAO,CAAC;QAC9B,mBAAmB,CAAC,EAAE,OAAO,CAAC;QAC9B,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,sDAAsD,CAAC,EAAE,OAAO,CAAC;QACjE,WAAW,CAAC,EAAE,OAAO,CAAC;KACvB;CACF,CAAC;AAEF,KAAK,uBAAuB,GACxB,mBAAmB,GACnB,+BAA+B,GAC/B,4BAA4B,GAC5B,8BAA8B,GAC9B,8BAA8B,GAC9B,8BAA8B,GAC9B,uBAAuB,GACvB,sBAAsB,GACtB,sBAAsB,GACtB,qBAAqB,GACrB,sBAAsB,CAAC;AAE3B,MAAM,MAAM,SAAS,GACjB,yBAAyB,GACzB,uCAAuC,GACvC,oCAAoC,GACpC,gCAAgC,GAChC,0BAA0B,GAC1B,wBAAwB,GACxB,4BAA4B,GAC5B,iCAAiC,GACjC,yBAAyB,GACzB,yBAAyB,GACzB,gCAAgC,GAChC,0BAA0B,GAC1B,yBAAyB,GACzB,2BAA2B,GAC3B,mBAAmB,GACnB,wBAAwB,GACxB,uBAAuB,CAAC;;AAE5B,
|
1
|
+
{"version":3,"file":"strict-boolean-expressions.d.ts","sourceRoot":"","sources":["../../src/rules/strict-boolean-expressions.ts"],"names":[],"mappings":"AAsBA,MAAM,MAAM,OAAO,GAAG;IACpB;QACE,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,oBAAoB,CAAC,EAAE,OAAO,CAAC;QAC/B,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAC5B,mBAAmB,CAAC,EAAE,OAAO,CAAC;QAC9B,mBAAmB,CAAC,EAAE,OAAO,CAAC;QAC9B,mBAAmB,CAAC,EAAE,OAAO,CAAC;QAC9B,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,sDAAsD,CAAC,EAAE,OAAO,CAAC;QACjE,WAAW,CAAC,EAAE,OAAO,CAAC;KACvB;CACF,CAAC;AAEF,KAAK,uBAAuB,GACxB,mBAAmB,GACnB,+BAA+B,GAC/B,4BAA4B,GAC5B,8BAA8B,GAC9B,8BAA8B,GAC9B,8BAA8B,GAC9B,uBAAuB,GACvB,sBAAsB,GACtB,sBAAsB,GACtB,qBAAqB,GACrB,sBAAsB,CAAC;AAE3B,MAAM,MAAM,SAAS,GACjB,yBAAyB,GACzB,uCAAuC,GACvC,oCAAoC,GACpC,gCAAgC,GAChC,0BAA0B,GAC1B,wBAAwB,GACxB,4BAA4B,GAC5B,iCAAiC,GACjC,yBAAyB,GACzB,yBAAyB,GACzB,gCAAgC,GAChC,0BAA0B,GAC1B,yBAAyB,GACzB,2BAA2B,GAC3B,mBAAmB,GACnB,wBAAwB,GACxB,uBAAuB,CAAC;;AAE5B,wBAq/BG"}
|
@@ -237,7 +237,7 @@ exports.default = (0, util_1.createRule)({
|
|
237
237
|
return type;
|
238
238
|
});
|
239
239
|
const flattenTypes = [
|
240
|
-
...new Set(returnTypes.flatMap(type => tsutils.
|
240
|
+
...new Set(returnTypes.flatMap(type => tsutils.unionConstituents(type))),
|
241
241
|
];
|
242
242
|
const types = inspectVariantTypes(flattenTypes);
|
243
243
|
const reportType = determineReportType(types);
|
@@ -774,7 +774,7 @@ exports.default = (0, util_1.createRule)({
|
|
774
774
|
*/
|
775
775
|
function checkNode(node) {
|
776
776
|
const type = (0, util_1.getConstrainedTypeAtLocation)(services, node);
|
777
|
-
const types = inspectVariantTypes(tsutils.
|
777
|
+
const types = inspectVariantTypes(tsutils.unionConstituents(type));
|
778
778
|
const reportType = determineReportType(types);
|
779
779
|
if (reportType != null) {
|
780
780
|
context.report({
|
@@ -799,7 +799,7 @@ exports.default = (0, util_1.createRule)({
|
|
799
799
|
// If incoming type is either "true" or "false", there will be one type
|
800
800
|
// object with intrinsicName set accordingly
|
801
801
|
// If incoming type is boolean, there will be two type objects with
|
802
|
-
// intrinsicName set "true" and "false" each because of ts-api-utils.
|
802
|
+
// intrinsicName set "true" and "false" each because of ts-api-utils.unionConstituents()
|
803
803
|
if (booleans.length === 1) {
|
804
804
|
variantTypes.add(tsutils.isTrueLiteralType(booleans[0]) ? 'truthy boolean' : 'boolean');
|
805
805
|
}
|
@@ -122,8 +122,8 @@ exports.default = (0, util_1.createRule)({
|
|
122
122
|
caseTypes.add(caseType);
|
123
123
|
}
|
124
124
|
const missingLiteralBranchTypes = [];
|
125
|
-
for (const unionPart of tsutils.
|
126
|
-
for (const intersectionPart of tsutils.
|
125
|
+
for (const unionPart of tsutils.unionConstituents(discriminantType)) {
|
126
|
+
for (const intersectionPart of tsutils.intersectionConstituents(unionPart)) {
|
127
127
|
if (caseTypes.has(intersectionPart) ||
|
128
128
|
!isTypeLiteralLikeType(intersectionPart)) {
|
129
129
|
continue;
|
@@ -284,8 +284,8 @@ function isTypeLiteralLikeType(type) {
|
|
284
284
|
*/
|
285
285
|
function doesTypeContainNonLiteralType(type) {
|
286
286
|
return tsutils
|
287
|
-
.
|
287
|
+
.unionConstituents(type)
|
288
288
|
.some(type => tsutils
|
289
|
-
.
|
289
|
+
.intersectionConstituents(type)
|
290
290
|
.every(subType => !isTypeLiteralLikeType(subType)));
|
291
291
|
}
|