@typescript-eslint/eslint-plugin 8.62.1-alpha.0 → 8.62.1-alpha.1

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.
@@ -212,22 +212,33 @@ exports.default = (0, util_1.createRule)({
212
212
  // we'll build up the replacement text from the compared expression outwards.
213
213
  let replacementText = context.sourceCode.getText(comparison.expression);
214
214
  let mayNeedParentheses = !(0, util_1.isStrongPrecedenceNode)(comparison.expression);
215
- // In maybeNullish === false, nullish values have the same truth table
216
- // as `true`.
217
- if (comparison.expressionIsNullableBoolean &&
218
- comparison.booleanLiteral === 'false') {
215
+ const fixWouldReturnExpressionDirectly = !isOverallNegated && comparison.expressionIsNullableBoolean;
216
+ if (fixWouldReturnExpressionDirectly &&
217
+ !(0, util_1.isConditionalTest)(mutatedNode)) {
219
218
  if (mayNeedParentheses) {
220
219
  replacementText = parenthesize(replacementText);
221
220
  }
222
- replacementText = `${replacementText} ?? true`;
221
+ replacementText = `${replacementText} ?? false`;
223
222
  mayNeedParentheses = true;
224
223
  }
225
- if (isOverallNegated) {
226
- if (mayNeedParentheses) {
227
- replacementText = parenthesize(replacementText);
224
+ else {
225
+ // In maybeNullish === false, nullish values have the same truth table
226
+ // as `true`.
227
+ if (comparison.expressionIsNullableBoolean &&
228
+ comparison.booleanLiteral === 'false') {
229
+ if (mayNeedParentheses) {
230
+ replacementText = parenthesize(replacementText);
231
+ }
232
+ replacementText = `${replacementText} ?? true`;
233
+ mayNeedParentheses = true;
234
+ }
235
+ if (isOverallNegated) {
236
+ if (mayNeedParentheses) {
237
+ replacementText = parenthesize(replacementText);
238
+ }
239
+ replacementText = `!${replacementText}`;
240
+ mayNeedParentheses = false;
228
241
  }
229
- replacementText = `!${replacementText}`;
230
- mayNeedParentheses = false;
231
242
  }
232
243
  if (mayNeedParentheses && (0, util_1.isWeakPrecedenceParent)(mutatedNode)) {
233
244
  replacementText = parenthesize(replacementText);
@@ -1,3 +1,4 @@
1
+ import type { TSESLint } from '@typescript-eslint/utils';
1
2
  export type Options = [
2
3
  {
3
4
  checkLiteralConstAssertions?: boolean;
@@ -5,7 +6,7 @@ export type Options = [
5
6
  }
6
7
  ];
7
8
  export type MessageIds = 'contextuallyUnnecessary' | 'unnecessaryAssertion';
8
- declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<MessageIds, Options, import("../../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
9
+ declare const _default: TSESLint.RuleModule<MessageIds, Options, import("../../rules").ESLintPluginDocs, TSESLint.RuleListener> & {
9
10
  name: string;
10
11
  };
11
12
  export default _default;
@@ -37,6 +37,42 @@ const utils_1 = require("@typescript-eslint/utils");
37
37
  const tsutils = __importStar(require("ts-api-utils"));
38
38
  const ts = __importStar(require("typescript"));
39
39
  const util_1 = require("../util");
40
+ function isAtExpressionStatementStart(node) {
41
+ let current = node;
42
+ while (true) {
43
+ const { parent } = current;
44
+ if (parent == null) {
45
+ return false;
46
+ }
47
+ if (parent.range[0] !== current.range[0]) {
48
+ return false;
49
+ }
50
+ if (parent.type === utils_1.AST_NODE_TYPES.ExpressionStatement) {
51
+ return true;
52
+ }
53
+ current = parent;
54
+ }
55
+ }
56
+ function isAtArrowFunctionBodyStart(node, sourceCode) {
57
+ let current = node;
58
+ while (true) {
59
+ if ((0, util_1.isParenthesized)(current, sourceCode)) {
60
+ return false;
61
+ }
62
+ const { parent } = current;
63
+ if (parent == null) {
64
+ return false;
65
+ }
66
+ if (parent.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression &&
67
+ parent.body === current) {
68
+ return true;
69
+ }
70
+ if (parent.range[0] !== current.range[0]) {
71
+ return false;
72
+ }
73
+ current = parent;
74
+ }
75
+ }
40
76
  exports.default = (0, util_1.createRule)({
41
77
  name: 'no-unnecessary-type-assertion',
42
78
  meta: {
@@ -565,17 +601,19 @@ exports.default = (0, util_1.createRule)({
565
601
  token.value === '<'), util_1.NullThrowsReasons.MissingToken('<', 'type annotation'));
566
602
  const closingAngleBracket = (0, util_1.nullThrows)(context.sourceCode.getTokenAfter(node.typeAnnotation, token => token.type === utils_1.AST_TOKEN_TYPES.Punctuator &&
567
603
  token.value === '>'), util_1.NullThrowsReasons.MissingToken('>', 'type annotation'));
568
- // Removing the angle-bracketed type can leave a bare object
569
- // literal in a position where `{` is parsed as a block (concise
570
- // arrow body, or the start of an expression statement). Wrap the
571
- // result in parentheses to preserve the original expression
572
- // semantics.
573
- const needsParens = node.expression.type === utils_1.AST_NODE_TYPES.ObjectExpression &&
574
- ((node.parent.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression &&
575
- node.parent.body === node) ||
576
- node.parent.type === utils_1.AST_NODE_TYPES.ExpressionStatement) &&
577
- !(0, util_1.isParenthesized)(node, context.sourceCode) &&
578
- !(0, util_1.isParenthesized)(node.expression, context.sourceCode);
604
+ // Removing the angle brackets leaves the asserted operand at the
605
+ // assertion's position, so its first token leads whatever the
606
+ // assertion led. A leading `{`/`function`/`class` at the start of an
607
+ // expression statement is parsed as a block / function or class
608
+ // declaration, and a leading `{` at the start of a concise arrow body
609
+ // is parsed as a block body. In those positions the operand must be
610
+ // wrapped in parentheses to stay an expression.
611
+ const firstOperandToken = (0, util_1.nullThrows)(context.sourceCode.getTokenAfter(closingAngleBracket), util_1.NullThrowsReasons.MissingToken('operand', 'type assertion'));
612
+ const breaksExpressionStatement = ['{', 'function', 'class'].includes(firstOperandToken.value) &&
613
+ isAtExpressionStatementStart(node);
614
+ const breaksArrowFunctionBody = firstOperandToken.value === '{' &&
615
+ isAtArrowFunctionBodyStart(node, context.sourceCode);
616
+ const needsParens = breaksExpressionStatement || breaksArrowFunctionBody;
579
617
  const fixes = [];
580
618
  if (needsParens) {
581
619
  fixes.push(fixer.insertTextBefore(node, '('));
@@ -216,7 +216,7 @@ exports.default = (0, util_1.createRule)({
216
216
  * @param testNode The node being tested (i.e. `a`)
217
217
  */
218
218
  function isTruthinessCheckEligibleForPreferNullish({ node, testNode, }) {
219
- if (ignoreConditionalTests === true && isConditionalTest(node)) {
219
+ if (ignoreConditionalTests === true && (0, util_1.isConditionalTest)(node)) {
220
220
  return false;
221
221
  }
222
222
  if (ignoreBooleanCoercion === true &&
@@ -443,36 +443,6 @@ exports.default = (0, util_1.createRule)({
443
443
  };
444
444
  },
445
445
  });
446
- function isConditionalTest(node) {
447
- const parent = node.parent;
448
- if (parent == null) {
449
- return false;
450
- }
451
- if (parent.type === utils_1.AST_NODE_TYPES.LogicalExpression) {
452
- return isConditionalTest(parent);
453
- }
454
- if (parent.type === utils_1.AST_NODE_TYPES.ConditionalExpression &&
455
- (parent.consequent === node || parent.alternate === node)) {
456
- return isConditionalTest(parent);
457
- }
458
- if (parent.type === utils_1.AST_NODE_TYPES.SequenceExpression &&
459
- parent.expressions.at(-1) === node) {
460
- return isConditionalTest(parent);
461
- }
462
- if (parent.type === utils_1.AST_NODE_TYPES.UnaryExpression &&
463
- parent.operator === '!') {
464
- return isConditionalTest(parent);
465
- }
466
- if ((parent.type === utils_1.AST_NODE_TYPES.ConditionalExpression ||
467
- parent.type === utils_1.AST_NODE_TYPES.DoWhileStatement ||
468
- parent.type === utils_1.AST_NODE_TYPES.IfStatement ||
469
- parent.type === utils_1.AST_NODE_TYPES.ForStatement ||
470
- parent.type === utils_1.AST_NODE_TYPES.WhileStatement) &&
471
- parent.test === node) {
472
- return true;
473
- }
474
- return false;
475
- }
476
446
  function isBooleanConstructorContext(node, context) {
477
447
  const parent = node.parent;
478
448
  if (parent == null) {
@@ -252,8 +252,10 @@ function getReportDescriptor(sourceCode, parserServices, node, operator, options
252
252
  // so we need to make sure that there is at least one operand that includes
253
253
  // `undefined`, or else we're going to change the final type - which is
254
254
  // unsafe and might cause downstream type errors.
255
- else if (lastChain ||
256
- lastOperand.comparisonType === gatherLogicalOperands_1.NullishComparisonType.EqualNullOrUndefined ||
255
+ else if (lastChain) {
256
+ useSuggestionFixer = true;
257
+ }
258
+ else if (lastOperand.comparisonType === gatherLogicalOperands_1.NullishComparisonType.EqualNullOrUndefined ||
257
259
  lastOperand.comparisonType ===
258
260
  gatherLogicalOperands_1.NullishComparisonType.NotEqualNullOrUndefined ||
259
261
  lastOperand.comparisonType === gatherLogicalOperands_1.NullishComparisonType.StrictEqualUndefined ||
@@ -15,6 +15,7 @@ export * from './getWrappingFixer';
15
15
  export * from './hasOverloadSignatures';
16
16
  export * from './isArrayMethodCallWithPredicate';
17
17
  export * from './isAssignee';
18
+ export * from './isConditionalTest';
18
19
  export * from './isNodeEqual';
19
20
  export * from './isNullLiteral';
20
21
  export * from './isStartOfExpressionStatement';
@@ -32,6 +32,7 @@ __exportStar(require("./getWrappingFixer"), exports);
32
32
  __exportStar(require("./hasOverloadSignatures"), exports);
33
33
  __exportStar(require("./isArrayMethodCallWithPredicate"), exports);
34
34
  __exportStar(require("./isAssignee"), exports);
35
+ __exportStar(require("./isConditionalTest"), exports);
35
36
  __exportStar(require("./isNodeEqual"), exports);
36
37
  __exportStar(require("./isNullLiteral"), exports);
37
38
  __exportStar(require("./isStartOfExpressionStatement"), exports);
@@ -0,0 +1,2 @@
1
+ import type { TSESTree } from '@typescript-eslint/utils';
2
+ export declare function isConditionalTest(node: TSESTree.Node): boolean;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isConditionalTest = isConditionalTest;
4
+ const utils_1 = require("@typescript-eslint/utils");
5
+ function isConditionalTest(node) {
6
+ const parent = node.parent;
7
+ if (parent == null) {
8
+ return false;
9
+ }
10
+ if (parent.type === utils_1.AST_NODE_TYPES.LogicalExpression) {
11
+ return isConditionalTest(parent);
12
+ }
13
+ if (parent.type === utils_1.AST_NODE_TYPES.ConditionalExpression &&
14
+ (parent.consequent === node || parent.alternate === node)) {
15
+ return isConditionalTest(parent);
16
+ }
17
+ if (parent.type === utils_1.AST_NODE_TYPES.SequenceExpression &&
18
+ parent.expressions.at(-1) === node) {
19
+ return isConditionalTest(parent);
20
+ }
21
+ if (parent.type === utils_1.AST_NODE_TYPES.UnaryExpression &&
22
+ parent.operator === '!') {
23
+ return isConditionalTest(parent);
24
+ }
25
+ if ((parent.type === utils_1.AST_NODE_TYPES.ConditionalExpression ||
26
+ parent.type === utils_1.AST_NODE_TYPES.DoWhileStatement ||
27
+ parent.type === utils_1.AST_NODE_TYPES.IfStatement ||
28
+ parent.type === utils_1.AST_NODE_TYPES.ForStatement ||
29
+ parent.type === utils_1.AST_NODE_TYPES.WhileStatement) &&
30
+ parent.test === node) {
31
+ return true;
32
+ }
33
+ return false;
34
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@typescript-eslint/eslint-plugin",
3
- "version": "8.62.1-alpha.0",
3
+ "version": "8.62.1-alpha.1",
4
4
  "description": "TypeScript plugin for ESLint",
5
5
  "files": [
6
6
  "dist",
@@ -49,10 +49,10 @@
49
49
  "ignore": "^7.0.5",
50
50
  "natural-compare": "^1.4.0",
51
51
  "ts-api-utils": "^2.5.0",
52
- "@typescript-eslint/scope-manager": "8.62.1-alpha.0",
53
- "@typescript-eslint/type-utils": "8.62.1-alpha.0",
54
- "@typescript-eslint/visitor-keys": "8.62.1-alpha.0",
55
- "@typescript-eslint/utils": "8.62.1-alpha.0"
52
+ "@typescript-eslint/scope-manager": "8.62.1-alpha.1",
53
+ "@typescript-eslint/type-utils": "8.62.1-alpha.1",
54
+ "@typescript-eslint/visitor-keys": "8.62.1-alpha.1",
55
+ "@typescript-eslint/utils": "8.62.1-alpha.1"
56
56
  },
57
57
  "devDependencies": {
58
58
  "@types/json-schema": "^7.0.15",
@@ -76,13 +76,13 @@
76
76
  "typescript": ">=4.8.4 <6.1.0",
77
77
  "unist-util-visit": "^5.0.0",
78
78
  "vitest": "^4.0.18",
79
- "@typescript-eslint/rule-schema-to-typescript-types": "8.62.1-alpha.0",
80
- "@typescript-eslint/rule-tester": "8.62.1-alpha.0"
79
+ "@typescript-eslint/rule-tester": "8.62.1-alpha.1",
80
+ "@typescript-eslint/rule-schema-to-typescript-types": "8.62.1-alpha.1"
81
81
  },
82
82
  "peerDependencies": {
83
83
  "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
84
84
  "typescript": ">=4.8.4 <6.1.0",
85
- "@typescript-eslint/parser": "^8.62.1-alpha.0"
85
+ "@typescript-eslint/parser": "^8.62.1-alpha.1"
86
86
  },
87
87
  "funding": {
88
88
  "type": "opencollective",