@typescript-eslint/eslint-plugin 8.56.2-alpha.0 → 8.56.2-alpha.10

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.
@@ -269,17 +269,15 @@ exports.default = (0, util_1.createRule)({
269
269
  continue;
270
270
  }
271
271
  const declarations = candidate.getDeclarations();
272
- // If there are multiple declarations, at least one of them must not be
273
- // the default object toString.
274
- //
275
- // This may only matter for older versions of TS
276
- // see https://github.com/typescript-eslint/typescript-eslint/issues/8585
277
- if (declarations?.length !== 1) {
272
+ if (!declarations?.length) {
278
273
  continue;
279
274
  }
280
- // Not being the Object interface means this is user-defined.
281
- if (!ts.isInterfaceDeclaration(declarations[0].parent) ||
282
- declarations[0].parent.name.text !== 'Object') {
275
+ // If any declaration is not from the Object interface, this is
276
+ // user-defined (e.g. overloaded toString on a class or module).
277
+ // see https://github.com/typescript-eslint/typescript-eslint/issues/8585
278
+ // see https://github.com/typescript-eslint/typescript-eslint/issues/11945
279
+ if (declarations.some(declaration => !(ts.isInterfaceDeclaration(declaration.parent) &&
280
+ declaration.parent.name.text === 'Object'))) {
283
281
  return false;
284
282
  }
285
283
  foundFallbackOnObject = true;
@@ -46,7 +46,7 @@ exports.default = (0, util_1.createRule)({
46
46
  return initializer.operator === '-' ? -inner : inner;
47
47
  }
48
48
  case isStaticTemplateLiteral(initializer):
49
- return initializer.quasis[0].value.cooked;
49
+ return initializer.quasis[0].value.cooked ?? undefined;
50
50
  default:
51
51
  return undefined;
52
52
  }
@@ -396,14 +396,6 @@ exports.default = (0, util_1.createRule)({
396
396
  // The right side will be checked if the LogicalExpression is used in a conditional context
397
397
  checkNode(node.left);
398
398
  }
399
- function checkIfWhileLoopIsNecessaryConditional(node) {
400
- if (allowConstantLoopConditionsOption === 'only-allowed-literals' &&
401
- node.test.type === utils_1.AST_NODE_TYPES.Literal &&
402
- constantLoopConditionsAllowedLiterals.has(node.test.value)) {
403
- return;
404
- }
405
- checkIfLoopIsNecessaryConditional(node);
406
- }
407
399
  /**
408
400
  * Checks that a testable expression of a loop is necessarily conditional, reports otherwise.
409
401
  */
@@ -412,6 +404,11 @@ exports.default = (0, util_1.createRule)({
412
404
  // e.g. `for(;;)`
413
405
  return;
414
406
  }
407
+ if (allowConstantLoopConditionsOption === 'only-allowed-literals' &&
408
+ node.test.type === utils_1.AST_NODE_TYPES.Literal &&
409
+ constantLoopConditionsAllowedLiterals.has(node.test.value)) {
410
+ return;
411
+ }
415
412
  if (allowConstantLoopConditionsOption === 'always' &&
416
413
  tsutils.isTrueLiteralType((0, util_1.getConstrainedTypeAtLocation)(services, node.test))) {
417
414
  return;
@@ -663,7 +660,7 @@ exports.default = (0, util_1.createRule)({
663
660
  checkIfBoolExpressionIsNecessaryConditional(test, parent.discriminant, test, '===');
664
661
  }
665
662
  },
666
- WhileStatement: checkIfWhileLoopIsNecessaryConditional,
663
+ WhileStatement: checkIfLoopIsNecessaryConditional,
667
664
  };
668
665
  },
669
666
  });
@@ -169,7 +169,8 @@ exports.default = (0, util_1.createRule)({
169
169
  }
170
170
  else if (receiverProperty.key.type === utils_1.AST_NODE_TYPES.TemplateLiteral &&
171
171
  receiverProperty.key.quasis.length === 1) {
172
- key = receiverProperty.key.quasis[0].value.cooked;
172
+ const cooked = (0, util_1.nullThrows)(receiverProperty.key.quasis[0].value.cooked, 'cooked can only be null inside a TaggedTemplateExpression, which is not possible here');
173
+ key = cooked;
173
174
  }
174
175
  else {
175
176
  // can't figure out the name, so skip it
@@ -236,6 +236,9 @@ exports.default = (0, util_1.createRule)({
236
236
  if (signature.thisParameter) {
237
237
  paramIndex--;
238
238
  }
239
+ if (paramIndex < 0 || paramIndex >= params.length) {
240
+ return null;
241
+ }
239
242
  return checker.getTypeOfSymbol(params[paramIndex]);
240
243
  }
241
244
  if (parent.type === utils_1.AST_NODE_TYPES.AssignmentPattern) {
@@ -1,6 +1,8 @@
1
+ import type { TypeOrValueSpecifier } from '../util';
1
2
  export type MessageIds = 'rejectAnError';
2
3
  export type Options = [
3
4
  {
5
+ allow?: TypeOrValueSpecifier[];
4
6
  allowEmptyReject?: boolean;
5
7
  allowThrowingAny?: boolean;
6
8
  allowThrowingUnknown?: boolean;
@@ -20,6 +20,10 @@ exports.default = (0, util_1.createRule)({
20
20
  type: 'object',
21
21
  additionalProperties: false,
22
22
  properties: {
23
+ allow: {
24
+ ...util_1.typeOrValueSpecifiersSchema,
25
+ description: 'Type specifiers that can be used as Promise rejection reasons.',
26
+ },
23
27
  allowEmptyReject: {
24
28
  type: 'boolean',
25
29
  description: 'Whether to allow calls to `Promise.reject()` with no arguments.',
@@ -38,6 +42,7 @@ exports.default = (0, util_1.createRule)({
38
42
  },
39
43
  defaultOptions: [
40
44
  {
45
+ allow: [],
41
46
  allowEmptyReject: false,
42
47
  allowThrowingAny: false,
43
48
  allowThrowingUnknown: false,
@@ -49,6 +54,9 @@ exports.default = (0, util_1.createRule)({
49
54
  const argument = callExpression.arguments.at(0);
50
55
  if (argument) {
51
56
  const type = services.getTypeAtLocation(argument);
57
+ if ((0, util_1.typeMatchesSomeSpecifier)(type, options.allow, services.program)) {
58
+ return;
59
+ }
52
60
  if (options.allowThrowingAny && (0, util_1.isTypeAnyType)(type)) {
53
61
  return;
54
62
  }
@@ -222,7 +222,11 @@ exports.default = (0, util_1.createRule)({
222
222
  name: context.sourceCode.getText(nameNode),
223
223
  },
224
224
  *fix(fixer) {
225
- yield fixer.insertTextBefore(nameNode, 'readonly ');
225
+ const readonlyInsertionTarget = esNode.type === utils_1.AST_NODE_TYPES.PropertyDefinition &&
226
+ esNode.computed
227
+ ? (0, util_1.nullThrows)(context.sourceCode.getTokenBefore(nameNode), 'Expected to find a token before computed property name')
228
+ : nameNode;
229
+ yield fixer.insertTextBefore(readonlyInsertionTarget, 'readonly ');
226
230
  if (typeAnnotation) {
227
231
  yield fixer.insertTextAfter(nameNode, `: ${typeAnnotation}`);
228
232
  }
@@ -239,9 +243,25 @@ exports.default = (0, util_1.createRule)({
239
243
  }
240
244
  },
241
245
  MemberExpression(node) {
242
- if (classScopeStack.length !== 0 && !node.computed) {
246
+ if (classScopeStack.length === 0) {
247
+ return;
248
+ }
249
+ const classScope = classScopeStack[classScopeStack.length - 1];
250
+ if (!node.computed) {
243
251
  const tsNode = services.esTreeNodeToTSNodeMap.get(node);
244
- handlePropertyAccessExpression(tsNode, tsNode.parent, classScopeStack[classScopeStack.length - 1]);
252
+ handlePropertyAccessExpression(tsNode, tsNode.parent, classScope);
253
+ }
254
+ else {
255
+ const tsNode = services.esTreeNodeToTSNodeMap.get(node);
256
+ if (ts.isElementAccessExpression(tsNode) &&
257
+ ts.isBinaryExpression(tsNode.parent) &&
258
+ tsNode.parent.left === tsNode &&
259
+ tsutils.isAssignmentKind(tsNode.parent.operatorToken.kind)) {
260
+ const memberName = (0, util_1.getStaticMemberAccessValue)(node, context);
261
+ if (typeof memberName === 'string') {
262
+ classScope.addVariableModificationByName(tsNode.expression, memberName);
263
+ }
264
+ }
245
265
  }
246
266
  },
247
267
  };
@@ -285,8 +305,7 @@ class ClassScope {
285
305
  addDeclaredVariable(node) {
286
306
  if (!(tsutils.isModifierFlagSet(node, ts.ModifierFlags.Private) ||
287
307
  node.name.kind === ts.SyntaxKind.PrivateIdentifier) ||
288
- tsutils.isModifierFlagSet(node, ts.ModifierFlags.Accessor | ts.ModifierFlags.Readonly) ||
289
- ts.isComputedPropertyName(node.name)) {
308
+ tsutils.isModifierFlagSet(node, ts.ModifierFlags.Accessor | ts.ModifierFlags.Readonly)) {
290
309
  return;
291
310
  }
292
311
  if (this.onlyInlineLambdas &&
@@ -294,25 +313,32 @@ class ClassScope {
294
313
  !ts.isArrowFunction(node.initializer)) {
295
314
  return;
296
315
  }
316
+ const memberName = getMemberName(node.name);
317
+ if (memberName == null) {
318
+ return;
319
+ }
297
320
  (tsutils.isModifierFlagSet(node, ts.ModifierFlags.Static)
298
321
  ? this.privateModifiableStatics
299
- : this.privateModifiableMembers).set(node.name.getText(), node);
322
+ : this.privateModifiableMembers).set(memberName, node);
300
323
  }
301
324
  addVariableModification(node) {
302
- const modifierType = this.checker.getTypeAtLocation(node.expression);
325
+ this.addVariableModificationByName(node.expression, node.name.text);
326
+ }
327
+ addVariableModificationByName(expression, memberName) {
328
+ const modifierType = this.checker.getTypeAtLocation(expression);
303
329
  const relationOfModifierTypeToClass = this.getTypeToClassRelation(modifierType);
304
330
  if (relationOfModifierTypeToClass === TypeToClassRelation.Instance &&
305
331
  this.constructorScopeDepth === DIRECTLY_INSIDE_CONSTRUCTOR) {
306
- this.memberVariableWithConstructorModifications.add(node.name.text);
332
+ this.memberVariableWithConstructorModifications.add(memberName);
307
333
  return;
308
334
  }
309
335
  if (relationOfModifierTypeToClass === TypeToClassRelation.Instance ||
310
336
  relationOfModifierTypeToClass === TypeToClassRelation.ClassAndInstance) {
311
- this.memberVariableModifications.add(node.name.text);
337
+ this.memberVariableModifications.add(memberName);
312
338
  }
313
339
  if (relationOfModifierTypeToClass === TypeToClassRelation.Class ||
314
340
  relationOfModifierTypeToClass === TypeToClassRelation.ClassAndInstance) {
315
- this.staticVariableModifications.add(node.name.text);
341
+ this.staticVariableModifications.add(memberName);
316
342
  }
317
343
  }
318
344
  enterConstructor(node) {
@@ -390,3 +416,24 @@ class ClassScope {
390
416
  return this.memberVariableWithConstructorModifications.has(name);
391
417
  }
392
418
  }
419
+ function getMemberName(name) {
420
+ if (ts.isIdentifier(name) ||
421
+ ts.isPrivateIdentifier(name) ||
422
+ ts.isStringLiteral(name) ||
423
+ ts.isNoSubstitutionTemplateLiteral(name) ||
424
+ ts.isNumericLiteral(name)) {
425
+ return name.text;
426
+ }
427
+ if (ts.isComputedPropertyName(name)) {
428
+ const expression = name.expression;
429
+ if (ts.isNumericLiteral(expression)) {
430
+ return expression.text;
431
+ }
432
+ if (ts.isPropertyAccessExpression(expression) &&
433
+ ts.isIdentifier(expression.expression) &&
434
+ expression.expression.text === 'Symbol') {
435
+ return expression.getText();
436
+ }
437
+ }
438
+ return undefined;
439
+ }
@@ -164,11 +164,7 @@ exports.default = util.createRule({
164
164
  if (argNode.type === utils_1.AST_NODE_TYPES.SpreadElement) {
165
165
  continue;
166
166
  }
167
- // Check against the contextual type first
168
- if (checkExpressionNode(argNode)) {
169
- continue;
170
- }
171
- // Check against the types from all of the call signatures
167
+ // Collect the types from all of the call signatures
172
168
  const argExpectedReturnTypes = funcSignatures
173
169
  .map(s => s.parameters[argIdx])
174
170
  .filter(Boolean)
@@ -176,20 +172,44 @@ exports.default = util.createRule({
176
172
  .flatMap(paramType => tsutils.unionConstituents(paramType))
177
173
  .flatMap(paramType => paramType.getCallSignatures())
178
174
  .map(paramSignature => paramSignature.getReturnType());
175
+ const hasSingleSignature = funcSignatures.length === 1;
176
+ const allSignaturesReturnVoid = argExpectedReturnTypes.every(type => isVoid(type) ||
177
+ // Treat as void even though it might be technically any.
178
+ isNullishOrAny(type) ||
179
+ // `getTypeOfSymbolAtLocation` returns unresolved type parameters
180
+ // (e.g. `T`), even for overloads that match the call.
181
+ //
182
+ // Since we can't tell whether a generic overload currently matches,
183
+ // we treat TypeParameters similar to void.
184
+ tsutils.isTypeParameter(type));
185
+ // Check against the contextual type first, but only when there is a
186
+ // single signature or when all signatures return void, because
187
+ // `getContextualType` resolves to the first overload's return type even
188
+ // though there may be another one that matches the call.
189
+ if ((hasSingleSignature || allSignaturesReturnVoid) &&
190
+ checkExpressionNode(argNode)) {
191
+ continue;
192
+ }
179
193
  if (
180
194
  // At least one return type is void
181
- argExpectedReturnTypes.some(type => tsutils.isTypeFlagSet(type, ts.TypeFlags.Void)) &&
195
+ argExpectedReturnTypes.some(isVoid) &&
182
196
  // The rest are nullish or any
183
- argExpectedReturnTypes.every(type => tsutils.isTypeFlagSet(type, ts.TypeFlags.VoidLike |
184
- ts.TypeFlags.Undefined |
185
- ts.TypeFlags.Null |
186
- ts.TypeFlags.Any |
187
- ts.TypeFlags.Never))) {
197
+ argExpectedReturnTypes.every(isNullishOrAny)) {
188
198
  // We treat this argument as void even though it might be technically any.
189
199
  reportIfNonVoidFunction(argNode);
190
200
  }
191
201
  }
192
202
  }
203
+ function isNullishOrAny(type) {
204
+ return tsutils.isTypeFlagSet(type, ts.TypeFlags.VoidLike |
205
+ ts.TypeFlags.Undefined |
206
+ ts.TypeFlags.Null |
207
+ ts.TypeFlags.Any |
208
+ ts.TypeFlags.Never);
209
+ }
210
+ function isVoid(type) {
211
+ return tsutils.isTypeFlagSet(type, ts.TypeFlags.Void);
212
+ }
193
213
  /**
194
214
  * Finds errors in an object property.
195
215
  *