@typescript-eslint/eslint-plugin 8.58.3-alpha.4 → 8.59.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.
@@ -330,7 +330,8 @@ exports.default = (0, util_1.createRule)({
330
330
  const objectType = services.getTypeAtLocation(node.object);
331
331
  const propertyName = propertyType.isStringLiteral()
332
332
  ? propertyType.value
333
- : String(propertyType.value);
333
+ : // eslint-disable-next-line @typescript-eslint/no-base-to-string
334
+ String(propertyType.value);
334
335
  const property = objectType.getProperty(propertyName);
335
336
  const reason = getJsDocDeprecation(property);
336
337
  if (reason == null) {
@@ -163,35 +163,350 @@ exports.default = (0, util_1.createRule)({
163
163
  maybeDeclarationNode.kind === 'const') ||
164
164
  (parent.type === utils_1.AST_NODE_TYPES.PropertyDefinition && parent.readonly));
165
165
  }
166
- function isTypeUnchanged(uncast, cast) {
166
+ function isTypeUnchanged(node, expression, uncast, cast) {
167
167
  if (uncast === cast) {
168
168
  return true;
169
169
  }
170
+ if (node.typeAnnotation.type === utils_1.AST_NODE_TYPES.TSIntersectionType &&
171
+ containsTypeVariable(cast)) {
172
+ return false;
173
+ }
170
174
  if ((0, util_1.isTypeFlagSet)(uncast, ts.TypeFlags.Undefined) &&
171
175
  (0, util_1.isTypeFlagSet)(cast, ts.TypeFlags.Undefined) &&
172
176
  tsutils.isCompilerOptionEnabled(compilerOptions, 'exactOptionalPropertyTypes')) {
173
- const uncastParts = tsutils
174
- .unionConstituents(uncast)
175
- .filter(part => !(0, util_1.isTypeFlagSet)(part, ts.TypeFlags.Undefined));
176
- const castParts = tsutils
177
- .unionConstituents(cast)
178
- .filter(part => !(0, util_1.isTypeFlagSet)(part, ts.TypeFlags.Undefined));
179
- if (uncastParts.length !== castParts.length) {
180
- return false;
177
+ return areUnionPartsEquivalentIgnoringUndefined(uncast, cast);
178
+ }
179
+ if (((0, util_1.isTypeFlagSet)(uncast, ts.TypeFlags.NonPrimitive) &&
180
+ !(0, util_1.isTypeFlagSet)(cast, ts.TypeFlags.NonPrimitive)) ||
181
+ (hasIndexSignature(uncast) && !hasIndexSignature(cast)) ||
182
+ containsAny(uncast) ||
183
+ containsAny(cast) ||
184
+ (containsTypeVariable(cast) && !containsTypeVariable(uncast))) {
185
+ return false;
186
+ }
187
+ if (isConceptuallyLiteral(expression) &&
188
+ (expression.type !== utils_1.AST_NODE_TYPES.ObjectExpression ||
189
+ expression.properties.length === 0 ||
190
+ cast
191
+ .getProperties()
192
+ .some(p => isTypeLiteral(checker.getTypeOfSymbol(p))))) {
193
+ return false;
194
+ }
195
+ if (cast.isIntersection() && !uncast.isIntersection()) {
196
+ const castParts = cast.types;
197
+ const otherPart = castParts.find(part => part !== uncast);
198
+ if (tsutils.isTypeParameter(uncast) &&
199
+ castParts.length === 2 &&
200
+ castParts.some(part => part === uncast) &&
201
+ otherPart != null &&
202
+ isEmptyObjectType(otherPart) &&
203
+ !containsTypeVariable(otherPart)) {
204
+ const constraint = checker.getBaseConstraintOfType(uncast);
205
+ if (constraint && !(0, util_1.isNullableType)(constraint)) {
206
+ return true;
207
+ }
181
208
  }
182
- const uncastPartsSet = new Set(uncastParts);
183
- return castParts.every(part => uncastPartsSet.has(part));
209
+ return false;
184
210
  }
185
- return false;
211
+ if (!hasSameProperties(uncast, cast) ||
212
+ !haveSameTypeArguments(uncast, cast)) {
213
+ return false;
214
+ }
215
+ return areMutuallyAssignable(uncast, cast);
186
216
  }
187
217
  function isTypeLiteral(type) {
188
218
  return type.isLiteral() || tsutils.isBooleanLiteralType(type);
189
219
  }
220
+ function hasIndexSignature(type) {
221
+ return checker.getIndexInfosOfType(type).length > 0;
222
+ }
223
+ function typeContains(type, predicate, seen = new Set()) {
224
+ if (seen.has(type)) {
225
+ return false;
226
+ }
227
+ seen.add(type);
228
+ if (predicate(type)) {
229
+ return true;
230
+ }
231
+ if (type.isUnionOrIntersection()) {
232
+ return type.types.some(t => typeContains(t, predicate, seen));
233
+ }
234
+ const nestedTypes = [
235
+ ...checker.getTypeArguments(type),
236
+ ...type
237
+ .getCallSignatures()
238
+ .flatMap(sig => [
239
+ sig.getReturnType(),
240
+ ...sig.getParameters().map(p => checker.getTypeOfSymbol(p)),
241
+ ]),
242
+ ];
243
+ return nestedTypes.some(t => typeContains(t, predicate, seen));
244
+ }
245
+ function containsAny(type) {
246
+ return typeContains(type, t => (0, util_1.isTypeFlagSet)(t, ts.TypeFlags.Any));
247
+ }
248
+ function containsTypeVariable(type) {
249
+ return typeContains(type, t => (0, util_1.isTypeFlagSet)(t, ts.TypeFlags.TypeVariable | ts.TypeFlags.Index));
250
+ }
251
+ function hasTypeParams(sig) {
252
+ return (sig.getTypeParameters()?.length ?? 0) > 0;
253
+ }
254
+ function genericsMismatch(uncast, contextual) {
255
+ return contextual.getProperties().some(prop => {
256
+ const contextualSigs = checker.getSignaturesOfType(checker.getTypeOfSymbol(prop), ts.SignatureKind.Call);
257
+ if (!contextualSigs.some(hasTypeParams)) {
258
+ return false;
259
+ }
260
+ const uncastProp = uncast.getProperty(prop.getEscapedName());
261
+ if (!uncastProp) {
262
+ return true;
263
+ }
264
+ return !checker
265
+ .getSignaturesOfType(checker.getTypeOfSymbol(uncastProp), ts.SignatureKind.Call)
266
+ .some(hasTypeParams);
267
+ });
268
+ }
269
+ function hasSameProperties(uncast, cast) {
270
+ const uncastProps = uncast.getProperties();
271
+ const castProps = cast.getProperties();
272
+ if (uncastProps.length !== castProps.length) {
273
+ return false;
274
+ }
275
+ const castPropNames = new Set(castProps.map(p => p.getEscapedName()));
276
+ return uncastProps.every(prop => {
277
+ const name = prop.getEscapedName();
278
+ return (castPropNames.has(name) &&
279
+ tsutils.isPropertyReadonlyInType(uncast, name, checker) ===
280
+ tsutils.isPropertyReadonlyInType(cast, name, checker));
281
+ });
282
+ }
283
+ function haveSameTypeArguments(uncast, cast) {
284
+ const uncastArgs = checker.getTypeArguments(uncast);
285
+ const castArgs = checker.getTypeArguments(cast);
286
+ return (uncastArgs.length === castArgs.length &&
287
+ uncastArgs.every((arg, i) => arg === castArgs[i]));
288
+ }
289
+ function areMutuallyAssignable(a, b) {
290
+ return (checker.isTypeAssignableTo(a, b) && checker.isTypeAssignableTo(b, a));
291
+ }
292
+ function areUnionPartsEquivalentIgnoringUndefined(uncast, cast) {
293
+ const filterUndefined = (part) => !(0, util_1.isTypeFlagSet)(part, ts.TypeFlags.Undefined);
294
+ const uncastParts = tsutils
295
+ .unionConstituents(uncast)
296
+ .filter(filterUndefined);
297
+ const castParts = tsutils.unionConstituents(cast).filter(filterUndefined);
298
+ if (uncastParts.length !== castParts.length) {
299
+ return false;
300
+ }
301
+ const uncastPartsSet = new Set(uncastParts);
302
+ return castParts.every(part => uncastPartsSet.has(part));
303
+ }
304
+ function getOriginalExpression(node) {
305
+ let current = node.expression;
306
+ while (current.type === utils_1.AST_NODE_TYPES.TSAsExpression ||
307
+ current.type === utils_1.AST_NODE_TYPES.TSTypeAssertion) {
308
+ current = current.expression;
309
+ }
310
+ return current;
311
+ }
312
+ function isDoubleAssertionUnnecessary(node, contextualType) {
313
+ const innerExpression = node.expression;
314
+ if (innerExpression.type !== utils_1.AST_NODE_TYPES.TSAsExpression &&
315
+ innerExpression.type !== utils_1.AST_NODE_TYPES.TSTypeAssertion) {
316
+ return false;
317
+ }
318
+ const originalExpr = getOriginalExpression(node);
319
+ const originalType = services.getTypeAtLocation(originalExpr);
320
+ const castType = services.getTypeAtLocation(node);
321
+ if (isTypeUnchanged(node, innerExpression, originalType, castType) &&
322
+ !(0, util_1.isTypeFlagSet)(castType, ts.TypeFlags.Any)) {
323
+ return 'unnecessaryAssertion';
324
+ }
325
+ if (contextualType) {
326
+ const intermediateType = services.getTypeAtLocation(innerExpression);
327
+ if (((0, util_1.isTypeFlagSet)(intermediateType, ts.TypeFlags.Any) ||
328
+ (0, util_1.isTypeFlagSet)(intermediateType, ts.TypeFlags.Unknown)) &&
329
+ checker.isTypeAssignableTo(originalType, contextualType)) {
330
+ return 'contextuallyUnnecessary';
331
+ }
332
+ }
333
+ return false;
334
+ }
335
+ const CONCEPTUALLY_LITERAL_TYPES = new Set([
336
+ utils_1.AST_NODE_TYPES.Literal,
337
+ utils_1.AST_NODE_TYPES.ArrayExpression,
338
+ utils_1.AST_NODE_TYPES.ObjectExpression,
339
+ utils_1.AST_NODE_TYPES.TemplateLiteral,
340
+ utils_1.AST_NODE_TYPES.ClassExpression,
341
+ utils_1.AST_NODE_TYPES.FunctionExpression,
342
+ utils_1.AST_NODE_TYPES.ArrowFunctionExpression,
343
+ utils_1.AST_NODE_TYPES.JSXElement,
344
+ utils_1.AST_NODE_TYPES.JSXFragment,
345
+ ]);
346
+ function isConceptuallyLiteral(node) {
347
+ return CONCEPTUALLY_LITERAL_TYPES.has(node.type);
348
+ }
190
349
  function isIIFE(expression) {
191
350
  return (expression.type === utils_1.AST_NODE_TYPES.CallExpression &&
192
351
  (expression.callee.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
193
352
  expression.callee.type === utils_1.AST_NODE_TYPES.FunctionExpression));
194
353
  }
354
+ function isEmptyObjectType(type) {
355
+ return ((0, util_1.isTypeFlagSet)(type, ts.TypeFlags.NonPrimitive) ||
356
+ (type.getProperties().length === 0 &&
357
+ !type.getCallSignatures().length &&
358
+ !type.getConstructSignatures().length &&
359
+ !type.getStringIndexType() &&
360
+ !type.getNumberIndexType()));
361
+ }
362
+ function hasGenericCallSignature(type) {
363
+ return type.getCallSignatures().some(hasTypeParams);
364
+ }
365
+ function isArgumentToOverloadedFunction(node) {
366
+ const { parent } = node;
367
+ if ((parent.type !== utils_1.AST_NODE_TYPES.CallExpression &&
368
+ parent.type !== utils_1.AST_NODE_TYPES.NewExpression) ||
369
+ !parent.arguments.includes(node)) {
370
+ return false;
371
+ }
372
+ const calleeType = checker.getTypeAtLocation(services.esTreeNodeToTSNodeMap.get(parent.callee));
373
+ const signatures = calleeType.getCallSignatures();
374
+ if (signatures.length <= 1) {
375
+ return false;
376
+ }
377
+ const argIndex = parent.arguments.indexOf(node);
378
+ const paramTypes = signatures.map(sig => {
379
+ const params = sig.getParameters();
380
+ if (argIndex >= params.length) {
381
+ return undefined;
382
+ }
383
+ const param = params[argIndex];
384
+ let paramType = checker.getTypeOfSymbol(param);
385
+ if (param.valueDeclaration &&
386
+ ts.isParameter(param.valueDeclaration) &&
387
+ param.valueDeclaration.dotDotDotToken) {
388
+ const typeArgs = checker.getTypeArguments(paramType);
389
+ if (typeArgs.length > 0) {
390
+ paramType = typeArgs[0];
391
+ }
392
+ }
393
+ return paramType;
394
+ });
395
+ if (paramTypes.some(type => type == null)) {
396
+ return true;
397
+ }
398
+ const definedParamTypes = paramTypes;
399
+ const firstParamType = definedParamTypes[0];
400
+ if (definedParamTypes.every(type => type === firstParamType)) {
401
+ return false;
402
+ }
403
+ const uncastType = services.getTypeAtLocation(node.expression);
404
+ return !definedParamTypes.every(type => checker.isTypeAssignableTo(uncastType, type));
405
+ }
406
+ function isInDestructuringDeclaration(node) {
407
+ const { parent } = node;
408
+ return (parent.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
409
+ parent.init === node &&
410
+ (parent.id.type === utils_1.AST_NODE_TYPES.ObjectPattern ||
411
+ parent.id.type === utils_1.AST_NODE_TYPES.ArrayPattern));
412
+ }
413
+ function isPropertyInProblematicContext(node) {
414
+ const { parent } = node;
415
+ if (parent.type !== utils_1.AST_NODE_TYPES.Property || parent.value !== node) {
416
+ return false;
417
+ }
418
+ const objectExpr = parent.parent;
419
+ if (objectExpr.type !== utils_1.AST_NODE_TYPES.ObjectExpression) {
420
+ return false;
421
+ }
422
+ const objectTsNode = services.esTreeNodeToTSNodeMap.get(objectExpr);
423
+ if (checker.getContextualType(objectTsNode)?.isUnion()) {
424
+ const nodeTsNode = services.esTreeNodeToTSNodeMap.get(node);
425
+ const propContextualType = checker.getContextualType(nodeTsNode);
426
+ if (propContextualType == null) {
427
+ return true;
428
+ }
429
+ const nonNullableContextualType = checker.getNonNullableType(propContextualType);
430
+ if (nonNullableContextualType.isUnion()) {
431
+ return true;
432
+ }
433
+ const uncastType = services.getTypeAtLocation(node.expression);
434
+ return !checker.isTypeAssignableTo(uncastType, nonNullableContextualType);
435
+ }
436
+ const objectParent = objectExpr.parent;
437
+ return (objectParent.type === utils_1.AST_NODE_TYPES.TSSatisfiesExpression ||
438
+ (objectParent.type === utils_1.AST_NODE_TYPES.CallExpression &&
439
+ objectParent.parent.type === utils_1.AST_NODE_TYPES.TSSatisfiesExpression));
440
+ }
441
+ function isAssignmentInNonStatementContext(node) {
442
+ const { parent } = node;
443
+ if (parent.type !== utils_1.AST_NODE_TYPES.AssignmentExpression ||
444
+ parent.right !== node) {
445
+ return false;
446
+ }
447
+ const assignmentParent = parent.parent;
448
+ return assignmentParent.type !== utils_1.AST_NODE_TYPES.ExpressionStatement;
449
+ }
450
+ function isInGenericContext(node) {
451
+ let seenFunction = false;
452
+ for (let current = node.parent; current; current = current.parent) {
453
+ if (current.type === utils_1.AST_NODE_TYPES.FunctionDeclaration) {
454
+ return false;
455
+ }
456
+ if (current.type === utils_1.AST_NODE_TYPES.FunctionExpression ||
457
+ current.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
458
+ if (current.body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
459
+ return false;
460
+ }
461
+ if (seenFunction) {
462
+ return false;
463
+ }
464
+ seenFunction = true;
465
+ }
466
+ if (current.type === utils_1.AST_NODE_TYPES.CallExpression ||
467
+ current.type === utils_1.AST_NODE_TYPES.NewExpression) {
468
+ if (current.typeArguments != null) {
469
+ continue;
470
+ }
471
+ if (current.type === utils_1.AST_NODE_TYPES.CallExpression &&
472
+ current.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
473
+ current.arguments.includes(node)) {
474
+ continue;
475
+ }
476
+ const calleeType = checker.getTypeAtLocation(services.esTreeNodeToTSNodeMap.get(current.callee));
477
+ if (hasGenericCallSignature(calleeType)) {
478
+ return true;
479
+ }
480
+ }
481
+ }
482
+ return false;
483
+ }
484
+ const SKIP_PARENT_TYPES = new Set([
485
+ utils_1.AST_NODE_TYPES.TSAsExpression,
486
+ utils_1.AST_NODE_TYPES.TSTypeAssertion,
487
+ utils_1.AST_NODE_TYPES.SpreadElement,
488
+ utils_1.AST_NODE_TYPES.TSSatisfiesExpression,
489
+ ]);
490
+ function shouldSkipContextualTypeFallback(node, castIsAny) {
491
+ if (castIsAny) {
492
+ return (node.parent.type === utils_1.AST_NODE_TYPES.LogicalExpression ||
493
+ isInGenericContext(node));
494
+ }
495
+ if (SKIP_PARENT_TYPES.has(node.parent.type) ||
496
+ node.expression.type === utils_1.AST_NODE_TYPES.ArrayExpression ||
497
+ isInDestructuringDeclaration(node) ||
498
+ isPropertyInProblematicContext(node) ||
499
+ isAssignmentInNonStatementContext(node) ||
500
+ isArgumentToOverloadedFunction(node)) {
501
+ return true;
502
+ }
503
+ if (isInGenericContext(node)) {
504
+ const originalExpr = getOriginalExpression(node);
505
+ return (!isConceptuallyLiteral(originalExpr) &&
506
+ node.parent.type !== utils_1.AST_NODE_TYPES.Property);
507
+ }
508
+ return false;
509
+ }
195
510
  function getUncastType(node) {
196
511
  // Special handling for IIFE: extract the function's return type
197
512
  if (isIIFE(node.expression)) {
@@ -211,6 +526,44 @@ exports.default = (0, util_1.createRule)({
211
526
  }
212
527
  return services.getTypeAtLocation(node.expression);
213
528
  }
529
+ function createAssertionFixer(node) {
530
+ return fixer => {
531
+ if (node.type === utils_1.AST_NODE_TYPES.TSTypeAssertion) {
532
+ const openingAngleBracket = (0, util_1.nullThrows)(context.sourceCode.getTokenBefore(node.typeAnnotation, token => token.type === utils_1.AST_TOKEN_TYPES.Punctuator &&
533
+ token.value === '<'), util_1.NullThrowsReasons.MissingToken('<', 'type annotation'));
534
+ const closingAngleBracket = (0, util_1.nullThrows)(context.sourceCode.getTokenAfter(node.typeAnnotation, token => token.type === utils_1.AST_TOKEN_TYPES.Punctuator &&
535
+ token.value === '>'), util_1.NullThrowsReasons.MissingToken('>', 'type annotation'));
536
+ return fixer.removeRange([
537
+ openingAngleBracket.range[0],
538
+ closingAngleBracket.range[1],
539
+ ]);
540
+ }
541
+ const asToken = (0, util_1.nullThrows)(context.sourceCode.getTokenAfter(node.expression, token => token.type === utils_1.AST_TOKEN_TYPES.Identifier && token.value === 'as'), util_1.NullThrowsReasons.MissingToken('>', 'type annotation'));
542
+ const tokenBeforeAs = (0, util_1.nullThrows)(context.sourceCode.getTokenBefore(asToken, {
543
+ includeComments: true,
544
+ }), util_1.NullThrowsReasons.MissingToken('comment', 'as'));
545
+ return fixer.removeRange([tokenBeforeAs.range[1], node.range[1]]);
546
+ };
547
+ }
548
+ function reportDoubleAssertionIfUnnecessary(node, contextualType) {
549
+ const doubleAssertionResult = isDoubleAssertionUnnecessary(node, contextualType);
550
+ if (doubleAssertionResult) {
551
+ context.report({
552
+ node,
553
+ messageId: doubleAssertionResult,
554
+ fix(fixer) {
555
+ const originalExpr = getOriginalExpression(node);
556
+ let text = context.sourceCode.getText(originalExpr);
557
+ if (originalExpr.type === utils_1.AST_NODE_TYPES.ObjectExpression &&
558
+ node.parent.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression &&
559
+ node.parent.body === node) {
560
+ text = `(${text})`;
561
+ }
562
+ return fixer.replaceText(node, text);
563
+ },
564
+ });
565
+ }
566
+ }
214
567
  return {
215
568
  'TSAsExpression, TSTypeAssertion'(node) {
216
569
  if (options.typesToIgnore?.includes(context.sourceCode.getText(node.typeAnnotation))) {
@@ -225,7 +578,7 @@ exports.default = (0, util_1.createRule)({
225
578
  return;
226
579
  }
227
580
  const uncastType = getUncastType(node);
228
- const typeIsUnchanged = isTypeUnchanged(uncastType, castType);
581
+ const typeIsUnchanged = isTypeUnchanged(node, node.expression, uncastType, castType);
229
582
  const wouldSameTypeBeInferred = castTypeIsLiteral
230
583
  ? isImplicitlyNarrowedLiteralDeclaration(node)
231
584
  : !typeAnnotationIsConstAssertion;
@@ -233,32 +586,46 @@ exports.default = (0, util_1.createRule)({
233
586
  context.report({
234
587
  node,
235
588
  messageId: 'unnecessaryAssertion',
236
- fix(fixer) {
237
- if (node.type === utils_1.AST_NODE_TYPES.TSTypeAssertion) {
238
- const openingAngleBracket = (0, util_1.nullThrows)(context.sourceCode.getTokenBefore(node.typeAnnotation, token => token.type === utils_1.AST_TOKEN_TYPES.Punctuator &&
239
- token.value === '<'), util_1.NullThrowsReasons.MissingToken('<', 'type annotation'));
240
- const closingAngleBracket = (0, util_1.nullThrows)(context.sourceCode.getTokenAfter(node.typeAnnotation, token => token.type === utils_1.AST_TOKEN_TYPES.Punctuator &&
241
- token.value === '>'), util_1.NullThrowsReasons.MissingToken('>', 'type annotation'));
242
- // < ( number ) > ( 3 + 5 )
243
- // ^---remove---^
244
- return fixer.removeRange([
245
- openingAngleBracket.range[0],
246
- closingAngleBracket.range[1],
247
- ]);
248
- }
249
- // `as` is always present in TSAsExpression
250
- const asToken = (0, util_1.nullThrows)(context.sourceCode.getTokenAfter(node.expression, token => token.type === utils_1.AST_TOKEN_TYPES.Identifier &&
251
- token.value === 'as'), util_1.NullThrowsReasons.MissingToken('>', 'type annotation'));
252
- const tokenBeforeAs = (0, util_1.nullThrows)(context.sourceCode.getTokenBefore(asToken, {
253
- includeComments: true,
254
- }), util_1.NullThrowsReasons.MissingToken('comment', 'as'));
255
- // ( 3 + 5 ) as number
256
- // ^--remove--^
257
- return fixer.removeRange([tokenBeforeAs.range[1], node.range[1]]);
258
- },
589
+ fix: createAssertionFixer(node),
259
590
  });
591
+ return;
592
+ }
593
+ const originalNode = services.esTreeNodeToTSNodeMap.get(node);
594
+ const castIsAny = (0, util_1.isTypeFlagSet)(castType, ts.TypeFlags.Any) &&
595
+ !SKIP_PARENT_TYPES.has(node.parent.type);
596
+ const contextualType = shouldSkipContextualTypeFallback(node, castIsAny)
597
+ ? undefined
598
+ : checker.getContextualType(originalNode);
599
+ if (contextualType) {
600
+ const contextualTypeIsAny = (0, util_1.isTypeFlagSet)(contextualType, ts.TypeFlags.Any);
601
+ const isCallArgument = (node.parent.type === utils_1.AST_NODE_TYPES.CallExpression ||
602
+ node.parent.type === utils_1.AST_NODE_TYPES.NewExpression) &&
603
+ node.parent.arguments.includes(node);
604
+ const anyInvolvedInContextualCheck = contextualTypeIsAny
605
+ ? isCallArgument && !containsAny(castType)
606
+ : !containsAny(contextualType);
607
+ const isNullishLiteralToUnion = castType.isUnion() &&
608
+ ((node.expression.type === utils_1.AST_NODE_TYPES.Literal &&
609
+ node.expression.value == null) ||
610
+ (node.expression.type === utils_1.AST_NODE_TYPES.Identifier &&
611
+ node.expression.name === 'undefined'));
612
+ const isContextuallyUnnecessary = !typeAnnotationIsConstAssertion &&
613
+ !containsAny(uncastType) &&
614
+ anyInvolvedInContextualCheck &&
615
+ (castIsAny || !genericsMismatch(uncastType, contextualType)) &&
616
+ (contextualTypeIsAny ||
617
+ checker.isTypeAssignableTo(uncastType, contextualType)) &&
618
+ !isNullishLiteralToUnion;
619
+ if (isContextuallyUnnecessary) {
620
+ context.report({
621
+ node,
622
+ messageId: 'contextuallyUnnecessary',
623
+ fix: createAssertionFixer(node),
624
+ });
625
+ return;
626
+ }
260
627
  }
261
- // TODO - add contextually unnecessary check for this
628
+ reportDoubleAssertionIfUnnecessary(node, contextualType);
262
629
  },
263
630
  TSNonNullExpression(node) {
264
631
  const removeExclamationFix = fixer => {
@@ -234,13 +234,13 @@ exports.default = (0, util_1.createRule)({
234
234
  /** Given type parameters, returns a function to test whether a type is one of those parameters. */
235
235
  function getIsTypeParameter(typeParameters) {
236
236
  if (typeParameters == null) {
237
- return (() => false);
237
+ return () => false;
238
238
  }
239
239
  const set = new Set();
240
240
  for (const t of typeParameters.params) {
241
241
  set.add(t.name.name);
242
242
  }
243
- return (typeName => set.has(typeName));
243
+ return typeName => set.has(typeName);
244
244
  }
245
245
  /** True if any of the outer type parameters are used in a signature. */
246
246
  function signatureUsesTypeParameter(sig, isTypeParameter) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@typescript-eslint/eslint-plugin",
3
- "version": "8.58.3-alpha.4",
3
+ "version": "8.59.0",
4
4
  "description": "TypeScript plugin for ESLint",
5
5
  "files": [
6
6
  "dist",
@@ -52,10 +52,10 @@
52
52
  "ignore": "^7.0.5",
53
53
  "natural-compare": "^1.4.0",
54
54
  "ts-api-utils": "^2.5.0",
55
- "@typescript-eslint/scope-manager": "8.58.3-alpha.4",
56
- "@typescript-eslint/type-utils": "8.58.3-alpha.4",
57
- "@typescript-eslint/utils": "8.58.3-alpha.4",
58
- "@typescript-eslint/visitor-keys": "8.58.3-alpha.4"
55
+ "@typescript-eslint/scope-manager": "8.59.0",
56
+ "@typescript-eslint/type-utils": "8.59.0",
57
+ "@typescript-eslint/utils": "8.59.0",
58
+ "@typescript-eslint/visitor-keys": "8.59.0"
59
59
  },
60
60
  "devDependencies": {
61
61
  "@types/json-schema": "^7.0.15",
@@ -78,13 +78,13 @@
78
78
  "typescript": ">=4.8.4 <6.1.0",
79
79
  "unist-util-visit": "^5.0.0",
80
80
  "vitest": "^4.0.18",
81
- "@typescript-eslint/rule-schema-to-typescript-types": "8.58.3-alpha.4",
82
- "@typescript-eslint/rule-tester": "8.58.3-alpha.4"
81
+ "@typescript-eslint/rule-schema-to-typescript-types": "8.59.0",
82
+ "@typescript-eslint/rule-tester": "8.59.0"
83
83
  },
84
84
  "peerDependencies": {
85
85
  "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
86
86
  "typescript": ">=4.8.4 <6.1.0",
87
- "@typescript-eslint/parser": "^8.58.3-alpha.4"
87
+ "@typescript-eslint/parser": "^8.59.0"
88
88
  },
89
89
  "funding": {
90
90
  "type": "opencollective",