@typescript-eslint/eslint-plugin 8.24.2-alpha.3 → 8.24.2-alpha.4

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 (32) hide show
  1. package/dist/rules/no-deprecated.d.ts.map +1 -1
  2. package/dist/rules/no-deprecated.js +11 -1
  3. package/dist/rules/no-floating-promises.d.ts.map +1 -1
  4. package/dist/rules/no-floating-promises.js +1 -4
  5. package/dist/rules/no-inferrable-types.d.ts.map +1 -1
  6. package/dist/rules/no-inferrable-types.js +4 -6
  7. package/dist/rules/no-invalid-void-type.d.ts.map +1 -1
  8. package/dist/rules/no-invalid-void-type.js +22 -0
  9. package/dist/rules/prefer-find.d.ts.map +1 -1
  10. package/dist/rules/prefer-find.js +8 -11
  11. package/dist/rules/prefer-nullish-coalescing.d.ts.map +1 -1
  12. package/dist/rules/prefer-nullish-coalescing.js +61 -26
  13. package/dist/rules/prefer-promise-reject-errors.d.ts.map +1 -1
  14. package/dist/rules/prefer-promise-reject-errors.js +2 -7
  15. package/dist/rules/prefer-string-starts-ends-with.d.ts.map +1 -1
  16. package/dist/rules/prefer-string-starts-ends-with.js +3 -11
  17. package/dist/rules/strict-boolean-expressions.d.ts +2 -1
  18. package/dist/rules/strict-boolean-expressions.d.ts.map +1 -1
  19. package/dist/rules/strict-boolean-expressions.js +457 -512
  20. package/dist/rules/unified-signatures.d.ts.map +1 -1
  21. package/dist/rules/unified-signatures.js +10 -3
  22. package/dist/util/hasOverloadSignatures.d.ts +7 -0
  23. package/dist/util/hasOverloadSignatures.d.ts.map +1 -0
  24. package/dist/util/hasOverloadSignatures.js +47 -0
  25. package/dist/util/index.d.ts +2 -0
  26. package/dist/util/index.d.ts.map +1 -1
  27. package/dist/util/index.js +2 -0
  28. package/dist/util/skipChainExpression.d.ts +3 -0
  29. package/dist/util/skipChainExpression.d.ts.map +1 -0
  30. package/dist/util/skipChainExpression.js +7 -0
  31. package/docs/rules/consistent-type-definitions.mdx +43 -4
  32. package/package.json +7 -7
@@ -1 +1 @@
1
- {"version":3,"file":"no-deprecated.d.ts","sourceRoot":"","sources":["../../src/rules/no-deprecated.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAepD,KAAK,UAAU,GAAG,YAAY,GAAG,sBAAsB,CAAC;AAExD,KAAK,OAAO,GAAG;IACb;QACE,KAAK,CAAC,EAAE,oBAAoB,EAAE,CAAC;KAChC;CACF,CAAC;;AAEF,wBAwXG"}
1
+ {"version":3,"file":"no-deprecated.d.ts","sourceRoot":"","sources":["../../src/rules/no-deprecated.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAgBpD,KAAK,UAAU,GAAG,YAAY,GAAG,sBAAsB,CAAC;AAExD,KAAK,OAAO,GAAG;IACb;QACE,KAAK,CAAC,EAAE,oBAAoB,EAAE,CAAC;KAChC;CACF,CAAC;;AAEF,wBAyXG"}
@@ -303,7 +303,7 @@ exports.default = (0, util_1.createRule)({
303
303
  if ((0, util_1.typeMatchesSomeSpecifier)(type, allow, services.program)) {
304
304
  return;
305
305
  }
306
- const name = node.type === utils_1.AST_NODE_TYPES.Super ? 'super' : node.name;
306
+ const name = getReportedNodeName(node);
307
307
  context.report({
308
308
  ...(reason
309
309
  ? {
@@ -324,7 +324,17 @@ exports.default = (0, util_1.createRule)({
324
324
  checkIdentifier(node);
325
325
  }
326
326
  },
327
+ PrivateIdentifier: checkIdentifier,
327
328
  Super: checkIdentifier,
328
329
  };
329
330
  },
330
331
  });
332
+ function getReportedNodeName(node) {
333
+ if (node.type === utils_1.AST_NODE_TYPES.Super) {
334
+ return 'super';
335
+ }
336
+ if (node.type === utils_1.AST_NODE_TYPES.PrivateIdentifier) {
337
+ return `#${node.name}`;
338
+ }
339
+ return node.name;
340
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"no-floating-promises.d.ts","sourceRoot":"","sources":["../../src/rules/no-floating-promises.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAY,MAAM,0BAA0B,CAAC;AAMnE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAcpD,MAAM,MAAM,OAAO,GAAG;IACpB;QACE,sBAAsB,CAAC,EAAE,oBAAoB,EAAE,CAAC;QAChD,yBAAyB,CAAC,EAAE,oBAAoB,EAAE,CAAC;QACnD,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,UAAU,CAAC,EAAE,OAAO,CAAC;KACtB;CACF,CAAC;AAEF,MAAM,MAAM,SAAS,GACjB,UAAU,GACV,kBAAkB,GAClB,iBAAiB,GACjB,sBAAsB,GACtB,0BAA0B,GAC1B,iCAAiC,GACjC,qCAAqC,GACrC,cAAc,CAAC;;AAmBnB,wBAsaG"}
1
+ {"version":3,"file":"no-floating-promises.d.ts","sourceRoot":"","sources":["../../src/rules/no-floating-promises.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAY,MAAM,0BAA0B,CAAC;AAMnE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAepD,MAAM,MAAM,OAAO,GAAG;IACpB;QACE,sBAAsB,CAAC,EAAE,oBAAoB,EAAE,CAAC;QAChD,yBAAyB,CAAC,EAAE,oBAAoB,EAAE,CAAC;QACnD,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,UAAU,CAAC,EAAE,OAAO,CAAC;KACtB;CACF,CAAC;AAEF,MAAM,MAAM,SAAS,GACjB,UAAU,GACV,kBAAkB,GAClB,iBAAiB,GACjB,sBAAsB,GACtB,0BAA0B,GAC1B,iCAAiC,GACjC,qCAAqC,GACrC,cAAc,CAAC;;AAmBnB,wBAkaG"}
@@ -116,10 +116,7 @@ exports.default = (0, util_1.createRule)({
116
116
  if (options.ignoreIIFE && isAsyncIife(node)) {
117
117
  return;
118
118
  }
119
- let expression = node.expression;
120
- if (expression.type === utils_1.AST_NODE_TYPES.ChainExpression) {
121
- expression = expression.expression;
122
- }
119
+ const expression = (0, util_1.skipChainExpression)(node.expression);
123
120
  if (isKnownSafePromiseReturn(expression)) {
124
121
  return;
125
122
  }
@@ -1 +1 @@
1
- {"version":3,"file":"no-inferrable-types.d.ts","sourceRoot":"","sources":["../../src/rules/no-inferrable-types.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,OAAO,GAAG;IACpB;QACE,gBAAgB,CAAC,EAAE,OAAO,CAAC;QAC3B,gBAAgB,CAAC,EAAE,OAAO,CAAC;KAC5B;CACF,CAAC;AACF,MAAM,MAAM,UAAU,GAAG,kBAAkB,CAAC;;AAE5C,wBAkRG"}
1
+ {"version":3,"file":"no-inferrable-types.d.ts","sourceRoot":"","sources":["../../src/rules/no-inferrable-types.ts"],"names":[],"mappings":"AAYA,MAAM,MAAM,OAAO,GAAG;IACpB;QACE,gBAAgB,CAAC,EAAE,OAAO,CAAC;QAC3B,gBAAgB,CAAC,EAAE,OAAO,CAAC;KAC5B;CACF,CAAC;AACF,MAAM,MAAM,UAAU,GAAG,kBAAkB,CAAC;;AAE5C,wBAgRG"}
@@ -39,12 +39,10 @@ exports.default = (0, util_1.createRule)({
39
39
  ],
40
40
  create(context, [{ ignoreParameters, ignoreProperties }]) {
41
41
  function isFunctionCall(init, callName) {
42
- if (init.type === utils_1.AST_NODE_TYPES.ChainExpression) {
43
- return isFunctionCall(init.expression, callName);
44
- }
45
- return (init.type === utils_1.AST_NODE_TYPES.CallExpression &&
46
- init.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
47
- init.callee.name === callName);
42
+ const node = (0, util_1.skipChainExpression)(init);
43
+ return (node.type === utils_1.AST_NODE_TYPES.CallExpression &&
44
+ node.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
45
+ node.callee.name === callName);
48
46
  }
49
47
  function isLiteral(init, typeName) {
50
48
  return (init.type === utils_1.AST_NODE_TYPES.Literal && typeof init.value === typeName);
@@ -1 +1 @@
1
- {"version":3,"file":"no-invalid-void-type.d.ts","sourceRoot":"","sources":["../../src/rules/no-invalid-void-type.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,OAAO;IACtB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,2BAA2B,CAAC,EAAE,OAAO,GAAG,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;CAC/D;AAED,MAAM,MAAM,UAAU,GAClB,uBAAuB,GACvB,sBAAsB,GACtB,+BAA+B,GAC/B,iCAAiC,GACjC,0CAA0C,GAC1C,6BAA6B,CAAC;;AAElC,wBA4NG"}
1
+ {"version":3,"file":"no-invalid-void-type.d.ts","sourceRoot":"","sources":["../../src/rules/no-invalid-void-type.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,OAAO;IACtB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,2BAA2B,CAAC,EAAE,OAAO,GAAG,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;CAC/D;AAED,MAAM,MAAM,UAAU,GAClB,uBAAuB,GACvB,sBAAsB,GACtB,+BAA+B,GAC/B,iCAAiC,GACjC,0CAA0C,GAC1C,6BAA6B,CAAC;;AAElC,wBA0OG"}
@@ -152,6 +152,14 @@ exports.default = (0, util_1.createRule)({
152
152
  isValidUnionType(node.parent)) {
153
153
  return;
154
154
  }
155
+ // using `void` as part of the return type of function overloading implementation
156
+ if (node.parent.type === utils_1.AST_NODE_TYPES.TSUnionType) {
157
+ const declaringFunction = getParentFunctionDeclarationNode(node.parent);
158
+ if (declaringFunction &&
159
+ (0, util_1.hasOverloadSignatures)(declaringFunction, context)) {
160
+ return;
161
+ }
162
+ }
155
163
  // this parameter is ok to be void.
156
164
  if (allowAsThisParameter &&
157
165
  node.parent.type === utils_1.AST_NODE_TYPES.TSTypeAnnotation &&
@@ -185,3 +193,17 @@ function getNotReturnOrGenericMessageId(node) {
185
193
  ? 'invalidVoidUnionConstituent'
186
194
  : 'invalidVoidNotReturnOrGeneric';
187
195
  }
196
+ function getParentFunctionDeclarationNode(node) {
197
+ let current = node.parent;
198
+ while (current) {
199
+ if (current.type === utils_1.AST_NODE_TYPES.FunctionDeclaration) {
200
+ return current;
201
+ }
202
+ if (current.type === utils_1.AST_NODE_TYPES.MethodDefinition &&
203
+ current.value.body != null) {
204
+ return current;
205
+ }
206
+ current = current.parent;
207
+ }
208
+ return null;
209
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"prefer-find.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-find.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAY,MAAM,0BAA0B,CAAC;;AAgBnE,wBAoTG"}
1
+ {"version":3,"file":"prefer-find.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-find.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAY,MAAM,0BAA0B,CAAC;;AAiBnE,wBA2SG"}
@@ -58,22 +58,20 @@ exports.default = (0, util_1.createRule)({
58
58
  const services = (0, util_1.getParserServices)(context);
59
59
  const checker = services.program.getTypeChecker();
60
60
  function parseArrayFilterExpressions(expression) {
61
- if (expression.type === utils_1.AST_NODE_TYPES.SequenceExpression) {
61
+ const node = (0, util_1.skipChainExpression)(expression);
62
+ if (node.type === utils_1.AST_NODE_TYPES.SequenceExpression) {
62
63
  // Only the last expression in (a, b, [1, 2, 3].filter(condition))[0] matters
63
- const lastExpression = (0, util_1.nullThrows)(expression.expressions.at(-1), 'Expected to have more than zero expressions in a sequence expression');
64
+ const lastExpression = (0, util_1.nullThrows)(node.expressions.at(-1), 'Expected to have more than zero expressions in a sequence expression');
64
65
  return parseArrayFilterExpressions(lastExpression);
65
66
  }
66
- if (expression.type === utils_1.AST_NODE_TYPES.ChainExpression) {
67
- return parseArrayFilterExpressions(expression.expression);
68
- }
69
67
  // This is the only reason we're returning a list rather than a single value.
70
- if (expression.type === utils_1.AST_NODE_TYPES.ConditionalExpression) {
68
+ if (node.type === utils_1.AST_NODE_TYPES.ConditionalExpression) {
71
69
  // Both branches of the ternary _must_ return results.
72
- const consequentResult = parseArrayFilterExpressions(expression.consequent);
70
+ const consequentResult = parseArrayFilterExpressions(node.consequent);
73
71
  if (consequentResult.length === 0) {
74
72
  return [];
75
73
  }
76
- const alternateResult = parseArrayFilterExpressions(expression.alternate);
74
+ const alternateResult = parseArrayFilterExpressions(node.alternate);
77
75
  if (alternateResult.length === 0) {
78
76
  return [];
79
77
  }
@@ -81,9 +79,8 @@ exports.default = (0, util_1.createRule)({
81
79
  return [...consequentResult, ...alternateResult];
82
80
  }
83
81
  // Check if it looks like <<stuff>>(...), but not <<stuff>>?.(...)
84
- if (expression.type === utils_1.AST_NODE_TYPES.CallExpression &&
85
- !expression.optional) {
86
- const callee = expression.callee;
82
+ if (node.type === utils_1.AST_NODE_TYPES.CallExpression && !node.optional) {
83
+ const callee = node.callee;
87
84
  // Check if it looks like <<stuff>>.filter(...) or <<stuff>>['filter'](...),
88
85
  // or the optional chaining variants.
89
86
  if (callee.type === utils_1.AST_NODE_TYPES.MemberExpression) {
@@ -1 +1 @@
1
- {"version":3,"file":"prefer-nullish-coalescing.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-nullish-coalescing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAY,MAAM,0BAA0B,CAAC;AA0BnE,MAAM,MAAM,OAAO,GAAG;IACpB;QACE,sDAAsD,CAAC,EAAE,OAAO,CAAC;QACjE,qBAAqB,CAAC,EAAE,OAAO,CAAC;QAChC,sBAAsB,CAAC,EAAE,OAAO,CAAC;QACjC,6BAA6B,CAAC,EAAE,OAAO,CAAC;QACxC,gBAAgB,CAAC,EACb;YACE,MAAM,CAAC,EAAE,OAAO,CAAC;YACjB,OAAO,CAAC,EAAE,OAAO,CAAC;YAClB,MAAM,CAAC,EAAE,OAAO,CAAC;YACjB,MAAM,CAAC,EAAE,OAAO,CAAC;SAClB,GACD,IAAI,CAAC;QACT,kBAAkB,CAAC,EAAE,OAAO,CAAC;KAC9B;CACF,CAAC;AAEF,MAAM,MAAM,UAAU,GAClB,mBAAmB,GACnB,qBAAqB,GACrB,0BAA0B,GAC1B,gBAAgB,CAAC;;AAErB,wBA+dG"}
1
+ {"version":3,"file":"prefer-nullish-coalescing.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-nullish-coalescing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAY,MAAM,0BAA0B,CAAC;AA8BnE,MAAM,MAAM,OAAO,GAAG;IACpB;QACE,sDAAsD,CAAC,EAAE,OAAO,CAAC;QACjE,qBAAqB,CAAC,EAAE,OAAO,CAAC;QAChC,sBAAsB,CAAC,EAAE,OAAO,CAAC;QACjC,6BAA6B,CAAC,EAAE,OAAO,CAAC;QACxC,gBAAgB,CAAC,EACb;YACE,MAAM,CAAC,EAAE,OAAO,CAAC;YACjB,OAAO,CAAC,EAAE,OAAO,CAAC;YAClB,MAAM,CAAC,EAAE,OAAO,CAAC;YACjB,MAAM,CAAC,EAAE,OAAO,CAAC;SAClB,GACD,IAAI,CAAC;QACT,kBAAkB,CAAC,EAAE,OAAO,CAAC;KAC9B;CACF,CAAC;AAEF,MAAM,MAAM,UAAU,GAClB,mBAAmB,GACnB,qBAAqB,GACrB,0BAA0B,GAC1B,gBAAgB,CAAC;;AAErB,wBAmeG"}
@@ -37,7 +37,8 @@ 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
- const isIdentifierOrMemberExpression = (0, util_1.isNodeOfTypes)([
40
+ const isIdentifierOrMemberOrChainExpression = (0, util_1.isNodeOfTypes)([
41
+ utils_1.AST_NODE_TYPES.ChainExpression,
41
42
  utils_1.AST_NODE_TYPES.Identifier,
42
43
  utils_1.AST_NODE_TYPES.MemberExpression,
43
44
  ]);
@@ -136,7 +137,6 @@ exports.default = (0, util_1.createRule)({
136
137
  create(context, [{ allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing, ignoreBooleanCoercion, ignoreConditionalTests, ignoreMixedLogicalExpressions, ignorePrimitives, ignoreTernaryTests, },]) {
137
138
  const parserServices = (0, util_1.getParserServices)(context);
138
139
  const compilerOptions = parserServices.program.getCompilerOptions();
139
- const checker = parserServices.program.getTypeChecker();
140
140
  const isStrictNullChecks = tsutils.isStrictCompilerOptionEnabled(compilerOptions, 'strictNullChecks');
141
141
  if (!isStrictNullChecks &&
142
142
  allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing !== true) {
@@ -301,23 +301,26 @@ exports.default = (0, util_1.createRule)({
301
301
  }
302
302
  }
303
303
  }
304
- let identifierOrMemberExpression;
304
+ let nullishCoalescingLeftNode;
305
305
  let hasTruthinessCheck = false;
306
306
  let hasNullCheckWithoutTruthinessCheck = false;
307
307
  let hasUndefinedCheckWithoutTruthinessCheck = false;
308
308
  if (!operator) {
309
+ let testNode;
309
310
  hasTruthinessCheck = true;
310
- if (isIdentifierOrMemberExpression(node.test) &&
311
- (0, util_1.isNodeEqual)(node.test, node.consequent)) {
312
- identifierOrMemberExpression = node.test;
311
+ if (isIdentifierOrMemberOrChainExpression(node.test)) {
312
+ testNode = node.test;
313
313
  }
314
314
  else if (node.test.type === utils_1.AST_NODE_TYPES.UnaryExpression &&
315
- node.test.operator === '!' &&
316
- isIdentifierOrMemberExpression(node.test.argument) &&
317
- (0, util_1.isNodeEqual)(node.test.argument, node.alternate)) {
318
- identifierOrMemberExpression = node.test.argument;
315
+ isIdentifierOrMemberOrChainExpression(node.test.argument) &&
316
+ node.test.operator === '!') {
317
+ testNode = node.test.argument;
319
318
  operator = '!';
320
319
  }
320
+ if (testNode &&
321
+ areNodesSimilarMemberAccess(testNode, getBranchNodes(node, operator).nonNullishBranch)) {
322
+ nullishCoalescingLeftNode = testNode;
323
+ }
321
324
  }
322
325
  else {
323
326
  // we check that the test only contains null, undefined and the identifier
@@ -328,20 +331,21 @@ exports.default = (0, util_1.createRule)({
328
331
  else if ((0, util_1.isUndefinedIdentifier)(testNode)) {
329
332
  hasUndefinedCheckWithoutTruthinessCheck = true;
330
333
  }
331
- else if ((operator === '!==' || operator === '!=') &&
332
- (0, util_1.isNodeEqual)(testNode, node.consequent)) {
333
- identifierOrMemberExpression = testNode;
334
- }
335
- else if ((operator === '===' || operator === '==') &&
336
- (0, util_1.isNodeEqual)(testNode, node.alternate)) {
337
- identifierOrMemberExpression = testNode;
334
+ else if (areNodesSimilarMemberAccess(testNode, getBranchNodes(node, operator).nonNullishBranch)) {
335
+ // Only consider the first expression in a multi-part nullish check,
336
+ // as subsequent expressions might not require all the optional chaining operators.
337
+ // For example: a?.b?.c !== undefined && a.b.c !== null ? a.b.c : 'foo';
338
+ // This works because `node.test` is always evaluated first in the loop
339
+ // and has the same or more necessary optional chaining operators
340
+ // than `node.alternate` or `node.consequent`.
341
+ nullishCoalescingLeftNode ??= testNode;
338
342
  }
339
343
  else {
340
344
  return;
341
345
  }
342
346
  }
343
347
  }
344
- if (!identifierOrMemberExpression) {
348
+ if (!nullishCoalescingLeftNode) {
345
349
  return;
346
350
  }
347
351
  const isFixableWithPreferNullishOverTernary = (() => {
@@ -349,12 +353,9 @@ exports.default = (0, util_1.createRule)({
349
353
  if (hasTruthinessCheck) {
350
354
  return isTruthinessCheckEligibleForPreferNullish({
351
355
  node,
352
- testNode: identifierOrMemberExpression,
356
+ testNode: nullishCoalescingLeftNode,
353
357
  });
354
358
  }
355
- const tsNode = parserServices.esTreeNodeToTSNodeMap.get(identifierOrMemberExpression);
356
- const type = checker.getTypeAtLocation(tsNode);
357
- const flags = (0, util_1.getTypeFlags)(type);
358
359
  // it is fixable if we check for both null and undefined, or not if neither
359
360
  if (hasUndefinedCheckWithoutTruthinessCheck ===
360
361
  hasNullCheckWithoutTruthinessCheck) {
@@ -364,6 +365,8 @@ exports.default = (0, util_1.createRule)({
364
365
  if (operator === '==' || operator === '!=') {
365
366
  return true;
366
367
  }
368
+ const type = parserServices.getTypeAtLocation(nullishCoalescingLeftNode);
369
+ const flags = (0, util_1.getTypeFlags)(type);
367
370
  if (flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) {
368
371
  return false;
369
372
  }
@@ -387,10 +390,7 @@ exports.default = (0, util_1.createRule)({
387
390
  messageId: 'suggestNullish',
388
391
  data: { equals: '' },
389
392
  fix(fixer) {
390
- const [left, right] = operator === '===' || operator === '==' || operator === '!'
391
- ? [identifierOrMemberExpression, node.consequent]
392
- : [identifierOrMemberExpression, node.alternate];
393
- return fixer.replaceText(node, `${(0, util_1.getTextWithParentheses)(context.sourceCode, left)} ?? ${(0, util_1.getTextWithParentheses)(context.sourceCode, right)}`);
393
+ return fixer.replaceText(node, `${(0, util_1.getTextWithParentheses)(context.sourceCode, nullishCoalescingLeftNode)} ?? ${(0, util_1.getTextWithParentheses)(context.sourceCode, getBranchNodes(node, operator).nullishBranch)}`);
394
394
  },
395
395
  },
396
396
  ],
@@ -483,3 +483,38 @@ function isMixedLogicalExpression(node) {
483
483
  }
484
484
  return false;
485
485
  }
486
+ /**
487
+ * Checks if two TSESTree nodes have the same member access sequence,
488
+ * regardless of optional chaining differences.
489
+ *
490
+ * Note: This does not imply that the nodes are runtime-equivalent.
491
+ *
492
+ * Example: `a.b.c`, `a?.b.c`, `a.b?.c`, `(a?.b).c`, `(a.b)?.c` are considered similar.
493
+ *
494
+ * @param a First TSESTree node.
495
+ * @param b Second TSESTree node.
496
+ * @returns `true` if the nodes access members in the same order; otherwise, `false`.
497
+ */
498
+ function areNodesSimilarMemberAccess(a, b) {
499
+ if (a.type === utils_1.AST_NODE_TYPES.MemberExpression &&
500
+ b.type === utils_1.AST_NODE_TYPES.MemberExpression) {
501
+ return ((0, util_1.isNodeEqual)(a.property, b.property) &&
502
+ areNodesSimilarMemberAccess(a.object, b.object));
503
+ }
504
+ if (a.type === utils_1.AST_NODE_TYPES.ChainExpression ||
505
+ b.type === utils_1.AST_NODE_TYPES.ChainExpression) {
506
+ return areNodesSimilarMemberAccess((0, util_1.skipChainExpression)(a), (0, util_1.skipChainExpression)(b));
507
+ }
508
+ return (0, util_1.isNodeEqual)(a, b);
509
+ }
510
+ /**
511
+ * Returns the branch nodes of a conditional expression:
512
+ * - the "nonNullish branch" is the branch when test node is not nullish
513
+ * - the "nullish branch" is the branch when test node is nullish
514
+ */
515
+ function getBranchNodes(node, operator) {
516
+ if (!operator || ['!=', '!=='].includes(operator)) {
517
+ return { nonNullishBranch: node.consequent, nullishBranch: node.alternate };
518
+ }
519
+ return { nonNullishBranch: node.alternate, nullishBranch: node.consequent };
520
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"prefer-promise-reject-errors.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-promise-reject-errors.ts"],"names":[],"mappings":"AAkBA,MAAM,MAAM,UAAU,GAAG,eAAe,CAAC;AAEzC,MAAM,MAAM,OAAO,GAAG;IACpB;QACE,gBAAgB,CAAC,EAAE,OAAO,CAAC;QAC3B,gBAAgB,CAAC,EAAE,OAAO,CAAC;QAC3B,oBAAoB,CAAC,EAAE,OAAO,CAAC;KAChC;CACF,CAAC;;AAEF,wBAoJG"}
1
+ {"version":3,"file":"prefer-promise-reject-errors.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-promise-reject-errors.ts"],"names":[],"mappings":"AAmBA,MAAM,MAAM,UAAU,GAAG,eAAe,CAAC;AAEzC,MAAM,MAAM,OAAO,GAAG;IACpB;QACE,gBAAgB,CAAC,EAAE,OAAO,CAAC;QAC3B,gBAAgB,CAAC,EAAE,OAAO,CAAC;QAC3B,oBAAoB,CAAC,EAAE,OAAO,CAAC;KAChC;CACF,CAAC;;AAEF,wBA4IG"}
@@ -68,11 +68,6 @@ exports.default = (0, util_1.createRule)({
68
68
  messageId: 'rejectAnError',
69
69
  });
70
70
  }
71
- function skipChainExpression(node) {
72
- return node.type === utils_1.AST_NODE_TYPES.ChainExpression
73
- ? node.expression
74
- : node;
75
- }
76
71
  function typeAtLocationIsLikePromise(node) {
77
72
  const type = services.getTypeAtLocation(node);
78
73
  return ((0, util_1.isPromiseConstructorLike)(services.program, type) ||
@@ -80,7 +75,7 @@ exports.default = (0, util_1.createRule)({
80
75
  }
81
76
  return {
82
77
  CallExpression(node) {
83
- const callee = skipChainExpression(node.callee);
78
+ const callee = (0, util_1.skipChainExpression)(node.callee);
84
79
  if (callee.type !== utils_1.AST_NODE_TYPES.MemberExpression) {
85
80
  return;
86
81
  }
@@ -91,7 +86,7 @@ exports.default = (0, util_1.createRule)({
91
86
  checkRejectCall(node);
92
87
  },
93
88
  NewExpression(node) {
94
- const callee = skipChainExpression(node.callee);
89
+ const callee = (0, util_1.skipChainExpression)(node.callee);
95
90
  if (!(0, util_1.isPromiseConstructorLike)(services.program, services.getTypeAtLocation(callee))) {
96
91
  return;
97
92
  }
@@ -1 +1 @@
1
- {"version":3,"file":"prefer-string-starts-ends-with.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-string-starts-ends-with.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAY,MAAM,0BAA0B,CAAC;AAoBnE,KAAK,4BAA4B,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEvD,MAAM,MAAM,OAAO,GAAG;IACpB;QACE,0BAA0B,CAAC,EAAE,4BAA4B,CAAC;KAC3D;CACF,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,gBAAgB,GAAG,kBAAkB,CAAC;;AAE/D,wBAyrBG"}
1
+ {"version":3,"file":"prefer-string-starts-ends-with.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-string-starts-ends-with.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAY,MAAM,0BAA0B,CAAC;AAqBnE,KAAK,4BAA4B,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEvD,MAAM,MAAM,OAAO,GAAG;IACpB;QACE,0BAA0B,CAAC,EAAE,4BAA4B,CAAC;KAC3D;CACF,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,gBAAgB,GAAG,kBAAkB,CAAC;;AAE/D,wBAkrBG"}
@@ -218,17 +218,9 @@ exports.default = (0, util_1.createRule)({
218
218
  }
219
219
  return { isEndsWith, isStartsWith, text };
220
220
  }
221
- function getLeftNode(node) {
222
- if (node.type === utils_1.AST_NODE_TYPES.ChainExpression) {
223
- return getLeftNode(node.expression);
224
- }
225
- let leftNode;
226
- if (node.type === utils_1.AST_NODE_TYPES.CallExpression) {
227
- leftNode = node.callee;
228
- }
229
- else {
230
- leftNode = node;
231
- }
221
+ function getLeftNode(init) {
222
+ const node = (0, util_1.skipChainExpression)(init);
223
+ const leftNode = node.type === utils_1.AST_NODE_TYPES.CallExpression ? node.callee : node;
232
224
  if (leftNode.type !== utils_1.AST_NODE_TYPES.MemberExpression) {
233
225
  throw new Error(`Expected a MemberExpression, got ${leftNode.type}`);
234
226
  }
@@ -11,7 +11,8 @@ export type Options = [
11
11
  allowString?: boolean;
12
12
  }
13
13
  ];
14
- export type MessageId = 'conditionErrorAny' | 'conditionErrorNullableBoolean' | 'conditionErrorNullableEnum' | 'conditionErrorNullableNumber' | 'conditionErrorNullableObject' | 'conditionErrorNullableString' | 'conditionErrorNullish' | 'conditionErrorNumber' | 'conditionErrorObject' | 'conditionErrorOther' | 'conditionErrorString' | 'conditionFixCastBoolean' | 'conditionFixCompareArrayLengthNonzero' | 'conditionFixCompareArrayLengthZero' | 'conditionFixCompareEmptyString' | 'conditionFixCompareFalse' | 'conditionFixCompareNaN' | 'conditionFixCompareNullish' | 'conditionFixCompareStringLength' | 'conditionFixCompareTrue' | 'conditionFixCompareZero' | 'conditionFixDefaultEmptyString' | 'conditionFixDefaultFalse' | 'conditionFixDefaultZero' | 'explicitBooleanReturnType' | 'noStrictNullCheck' | 'predicateCannotBeAsync' | 'predicateReturnsNonBoolean';
14
+ type ConditionErrorMessageId = 'conditionErrorAny' | 'conditionErrorNullableBoolean' | 'conditionErrorNullableEnum' | 'conditionErrorNullableNumber' | 'conditionErrorNullableObject' | 'conditionErrorNullableString' | 'conditionErrorNullish' | 'conditionErrorNumber' | 'conditionErrorObject' | 'conditionErrorOther' | 'conditionErrorString';
15
+ export type MessageId = 'conditionFixCastBoolean' | 'conditionFixCompareArrayLengthNonzero' | 'conditionFixCompareArrayLengthZero' | 'conditionFixCompareEmptyString' | 'conditionFixCompareFalse' | 'conditionFixCompareNaN' | 'conditionFixCompareNullish' | 'conditionFixCompareStringLength' | 'conditionFixCompareTrue' | 'conditionFixCompareZero' | 'conditionFixDefaultEmptyString' | 'conditionFixDefaultFalse' | 'conditionFixDefaultZero' | 'explicitBooleanReturnType' | 'noStrictNullCheck' | 'predicateCannotBeAsync' | ConditionErrorMessageId;
15
16
  declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<MessageId, Options, import("../../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
16
17
  export default _default;
17
18
  //# sourceMappingURL=strict-boolean-expressions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"strict-boolean-expressions.d.ts","sourceRoot":"","sources":["../../src/rules/strict-boolean-expressions.ts"],"names":[],"mappings":"AAqBA,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,MAAM,MAAM,SAAS,GACjB,mBAAmB,GACnB,+BAA+B,GAC/B,4BAA4B,GAC5B,8BAA8B,GAC9B,8BAA8B,GAC9B,8BAA8B,GAC9B,uBAAuB,GACvB,sBAAsB,GACtB,sBAAsB,GACtB,qBAAqB,GACrB,sBAAsB,GACtB,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,4BAA4B,CAAC;;AAEjC,wBAohCG"}
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"}