@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.
Files changed (72) hide show
  1. package/dist/configs/eslintrc/all.d.ts +1 -0
  2. package/dist/configs/eslintrc/all.d.ts.map +1 -1
  3. package/dist/configs/eslintrc/all.js +1 -0
  4. package/dist/configs/eslintrc/disable-type-checked.d.ts +1 -0
  5. package/dist/configs/eslintrc/disable-type-checked.d.ts.map +1 -1
  6. package/dist/configs/eslintrc/disable-type-checked.js +1 -0
  7. package/dist/configs/flat/all.d.ts.map +1 -1
  8. package/dist/configs/flat/all.js +1 -0
  9. package/dist/configs/flat/disable-type-checked.d.ts.map +1 -1
  10. package/dist/configs/flat/disable-type-checked.js +1 -0
  11. package/dist/index.d.ts +3 -0
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/raw-plugin.d.ts +3 -0
  14. package/dist/raw-plugin.d.ts.map +1 -1
  15. package/dist/rules/adjacent-overload-signatures.js +1 -1
  16. package/dist/rules/await-thenable.js +2 -2
  17. package/dist/rules/enum-utils/shared.js +1 -1
  18. package/dist/rules/explicit-function-return-type.js +2 -2
  19. package/dist/rules/index.d.ts +1 -0
  20. package/dist/rules/index.d.ts.map +1 -1
  21. package/dist/rules/index.js +2 -0
  22. package/dist/rules/member-ordering.js +1 -1
  23. package/dist/rules/no-confusing-void-expression.js +3 -3
  24. package/dist/rules/no-duplicate-enum-values.js +2 -2
  25. package/dist/rules/no-empty-interface.d.ts.map +1 -1
  26. package/dist/rules/no-empty-interface.js +2 -2
  27. package/dist/rules/no-floating-promises.d.ts.map +1 -1
  28. package/dist/rules/no-floating-promises.js +15 -27
  29. package/dist/rules/no-meaningless-void-operator.js +1 -1
  30. package/dist/rules/no-misused-promises.js +8 -8
  31. package/dist/rules/no-misused-spread.js +2 -2
  32. package/dist/rules/no-redundant-type-constituents.js +2 -2
  33. package/dist/rules/no-unnecessary-condition.js +5 -5
  34. package/dist/rules/no-unnecessary-template-expression.d.ts.map +1 -1
  35. package/dist/rules/no-unnecessary-template-expression.js +3 -3
  36. package/dist/rules/no-unnecessary-type-assertion.js +2 -2
  37. package/dist/rules/no-unnecessary-type-conversion.d.ts +4 -0
  38. package/dist/rules/no-unnecessary-type-conversion.d.ts.map +1 -0
  39. package/dist/rules/no-unnecessary-type-conversion.js +307 -0
  40. package/dist/rules/no-unsafe-assignment.js +1 -1
  41. package/dist/rules/no-unsafe-enum-comparison.js +4 -4
  42. package/dist/rules/no-unsafe-unary-minus.js +1 -1
  43. package/dist/rules/non-nullable-type-assertion-style.js +1 -1
  44. package/dist/rules/only-throw-error.d.ts +1 -0
  45. package/dist/rules/only-throw-error.d.ts.map +1 -1
  46. package/dist/rules/only-throw-error.js +47 -0
  47. package/dist/rules/prefer-find.js +2 -2
  48. package/dist/rules/prefer-nullish-coalescing.js +2 -2
  49. package/dist/rules/prefer-optional-chain-utils/analyzeChain.js +1 -1
  50. package/dist/rules/prefer-optional-chain-utils/checkNullishAndReport.js +1 -1
  51. package/dist/rules/prefer-optional-chain-utils/gatherLogicalOperands.js +1 -1
  52. package/dist/rules/prefer-reduce-type-parameter.js +2 -2
  53. package/dist/rules/prefer-regexp-exec.js +1 -1
  54. package/dist/rules/prefer-return-this-type.js +1 -1
  55. package/dist/rules/restrict-plus-operands.js +4 -4
  56. package/dist/rules/strict-boolean-expressions.d.ts.map +1 -1
  57. package/dist/rules/strict-boolean-expressions.js +3 -3
  58. package/dist/rules/switch-exhaustiveness-check.js +4 -4
  59. package/dist/rules/unbound-method.d.ts.map +1 -1
  60. package/dist/rules/unbound-method.js +2 -2
  61. package/dist/rules/use-unknown-in-catch-callback-variable.js +1 -1
  62. package/dist/util/getWrappingFixer.d.ts +3 -3
  63. package/dist/util/getWrappingFixer.d.ts.map +1 -1
  64. package/dist/util/getWrappingFixer.js +5 -2
  65. package/dist/util/isArrayMethodCallWithPredicate.js +2 -2
  66. package/dist/util/promiseUtils.d.ts +28 -0
  67. package/dist/util/promiseUtils.d.ts.map +1 -0
  68. package/dist/util/promiseUtils.js +98 -0
  69. package/dist/util/truthinessUtils.js +4 -4
  70. package/docs/rules/no-unnecessary-type-conversion.mdx +79 -0
  71. package/docs/rules/only-throw-error.mdx +6 -0
  72. 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 = String(receiverProperty.key.quasis[0].value.cooked);
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.intersectionTypeParts(type);
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.intersectionTypeParts(type);
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.unionTypeParts(leftType);
120
- const rightTypeParts = tsutils.unionTypeParts(rightType);
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
- .unionTypeParts(argType)
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.unionTypeParts(type);
63
+ return tsutils.unionConstituents(type);
64
64
  };
65
65
  const couldBeNullish = (type) => {
66
66
  if (type.flags & ts.TypeFlags.TypeParameter) {
@@ -3,6 +3,7 @@ export type MessageIds = 'object' | 'undef';
3
3
  export type Options = [
4
4
  {
5
5
  allow?: TypeOrValueSpecifier[];
6
+ allowRethrowing?: boolean;
6
7
  allowThrowingAny?: boolean;
7
8
  allowThrowingUnknown?: boolean;
8
9
  }
@@ -1 +1 @@
1
- {"version":3,"file":"only-throw-error.d.ts","sourceRoot":"","sources":["../../src/rules/only-throw-error.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAYpD,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE5C,MAAM,MAAM,OAAO,GAAG;IACpB;QACE,KAAK,CAAC,EAAE,oBAAoB,EAAE,CAAC;QAC/B,gBAAgB,CAAC,EAAE,OAAO,CAAC;QAC3B,oBAAoB,CAAC,EAAE,OAAO,CAAC;KAChC;CACF,CAAC;;AAEF,wBAuFG"}
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.unionTypeParts(type)) {
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
- .intersectionTypeParts(unionPart)
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
- .typeParts(type)
198
+ .typeConstituents(type)
199
199
  .some(t => tsutils
200
- .intersectionTypeParts(t)
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.unionTypeParts)(parserServices.getTypeAtLocation(node));
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.unionTypeParts)(parserServices.getTypeAtLocation(node)).some(t => (0, type_utils_1.isTypeFlagSet)(t, ts.TypeFlags.Null | ts.TypeFlags.Undefined)))) {
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.unionTypeParts)(type);
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
- .unionTypeParts(type)
59
+ .unionConstituents(type)
60
60
  .every(unionPart => tsutils
61
- .intersectionTypeParts(unionPart)
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.unionTypeParts(argumentType));
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 !!(firstArg?.type === utils_1.AST_NODE_TYPES.Identifier && firstArg.name === 'this');
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.unionTypeParts(baseType)) {
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.intersectionTypeParts(type).every(tsutils.isObjectType)
225
- : tsutils.unionTypeParts(type).every(tsutils.isObjectType);
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
- .unionTypeParts(type)
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,wBAm/BG"}
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.unionTypeParts(type))),
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.unionTypeParts(type));
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.unionTypeParts()
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.unionTypeParts(discriminantType)) {
126
- for (const intersectionPart of tsutils.intersectionTypeParts(unionPart)) {
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
- .unionTypeParts(type)
287
+ .unionConstituents(type)
288
288
  .some(type => tsutils
289
- .intersectionTypeParts(type)
289
+ .intersectionConstituents(type)
290
290
  .every(subType => !isTypeLiteralLikeType(subType)));
291
291
  }