@typescript-eslint/eslint-plugin 8.62.1-alpha.0 → 8.62.1-alpha.2
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.
- package/dist/rules/no-unnecessary-boolean-literal-compare.js +21 -10
- package/dist/rules/no-unnecessary-type-assertion.d.ts +2 -1
- package/dist/rules/no-unnecessary-type-assertion.js +49 -11
- package/dist/rules/prefer-nullish-coalescing.js +1 -31
- package/dist/rules/prefer-optional-chain-utils/analyzeChain.js +4 -2
- package/dist/util/index.d.ts +1 -0
- package/dist/util/index.js +1 -0
- package/dist/util/isConditionalTest.d.ts +2 -0
- package/dist/util/isConditionalTest.js +34 -0
- package/package.json +8 -8
|
@@ -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
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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} ??
|
|
221
|
+
replacementText = `${replacementText} ?? false`;
|
|
223
222
|
mayNeedParentheses = true;
|
|
224
223
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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:
|
|
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
|
|
569
|
-
//
|
|
570
|
-
//
|
|
571
|
-
//
|
|
572
|
-
//
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
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
|
-
|
|
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 ||
|
package/dist/util/index.d.ts
CHANGED
|
@@ -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';
|
package/dist/util/index.js
CHANGED
|
@@ -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,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.
|
|
3
|
+
"version": "8.62.1-alpha.2",
|
|
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/
|
|
53
|
-
"@typescript-eslint/
|
|
54
|
-
"@typescript-eslint/visitor-keys": "8.62.1-alpha.
|
|
55
|
-
"@typescript-eslint/
|
|
52
|
+
"@typescript-eslint/type-utils": "8.62.1-alpha.2",
|
|
53
|
+
"@typescript-eslint/utils": "8.62.1-alpha.2",
|
|
54
|
+
"@typescript-eslint/visitor-keys": "8.62.1-alpha.2",
|
|
55
|
+
"@typescript-eslint/scope-manager": "8.62.1-alpha.2"
|
|
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.
|
|
80
|
-
"@typescript-eslint/rule-tester": "8.62.1-alpha.
|
|
79
|
+
"@typescript-eslint/rule-schema-to-typescript-types": "8.62.1-alpha.2",
|
|
80
|
+
"@typescript-eslint/rule-tester": "8.62.1-alpha.2"
|
|
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.
|
|
85
|
+
"@typescript-eslint/parser": "^8.62.1-alpha.2"
|
|
86
86
|
},
|
|
87
87
|
"funding": {
|
|
88
88
|
"type": "opencollective",
|