@typescript-eslint/eslint-plugin 8.70.1-alpha.9 → 8.70.2-alpha.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.
- package/dist/index.d.ts +1 -1
- package/dist/raw-plugin.d.ts +1 -1
- package/dist/rules/await-thenable.js +14 -3
- package/dist/rules/index.d.ts +1 -1
- package/dist/rules/no-meaningless-void-operator.js +5 -0
- package/dist/rules/no-misused-promises.js +14 -10
- package/dist/rules/no-unnecessary-condition.js +52 -5
- package/dist/rules/no-unnecessary-parameter-property-assignment.js +12 -4
- package/dist/rules/no-unnecessary-type-assertion.js +2 -5
- package/dist/rules/no-useless-default-assignment.d.ts +1 -1
- package/dist/rules/no-useless-default-assignment.js +19 -5
- package/dist/rules/return-await.js +3 -13
- package/dist/rules/unbound-method.js +31 -0
- package/dist/util/getAwaitTokenRemovalRange.d.ts +4 -0
- package/dist/util/getAwaitTokenRemovalRange.js +15 -0
- package/dist/util/index.d.ts +3 -1
- package/dist/util/index.js +3 -1
- package/dist/util/isStartOfArrowFunctionBodyNeedingParentheses.d.ts +2 -0
- package/dist/util/{isStartOfArrowFunctionBody.js → isStartOfArrowFunctionBodyNeedingParentheses.js} +6 -2
- package/dist/util/isStartOfExpressionStatementNeedingParentheses.d.ts +2 -0
- package/dist/util/isStartOfExpressionStatementNeedingParentheses.js +9 -0
- package/package.json +8 -8
- package/dist/util/isStartOfArrowFunctionBody.d.ts +0 -2
package/dist/index.d.ts
CHANGED
|
@@ -994,7 +994,7 @@ declare const _default: {
|
|
|
994
994
|
'no-useless-constructor': import("@typescript-eslint/utils/ts-eslint").RuleModule<"noUselessConstructor" | "removeConstructor", [], import("../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
995
995
|
name: string;
|
|
996
996
|
};
|
|
997
|
-
'no-useless-default-assignment': import("@typescript-eslint/utils/ts-eslint").RuleModule<"noStrictNullCheck" | "preferOptionalSyntax" | "uselessDefaultAssignment" | "uselessUndefined", [{
|
|
997
|
+
'no-useless-default-assignment': import("@typescript-eslint/utils/ts-eslint").RuleModule<"noStrictNullCheck" | "preferOptionalSyntax" | "removeDefaultAssignment" | "useOptionalSyntax" | "uselessDefaultAssignment" | "uselessUndefined", [{
|
|
998
998
|
allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing?: boolean;
|
|
999
999
|
}], import("../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
1000
1000
|
name: string;
|
package/dist/raw-plugin.d.ts
CHANGED
|
@@ -1017,7 +1017,7 @@ declare const _default: {
|
|
|
1017
1017
|
'no-useless-constructor': TSESLint.RuleModule<"noUselessConstructor" | "removeConstructor", [], import("../rules").ESLintPluginDocs, TSESLint.RuleListener> & {
|
|
1018
1018
|
name: string;
|
|
1019
1019
|
};
|
|
1020
|
-
'no-useless-default-assignment': TSESLint.RuleModule<"noStrictNullCheck" | "preferOptionalSyntax" | "uselessDefaultAssignment" | "uselessUndefined", [{
|
|
1020
|
+
'no-useless-default-assignment': TSESLint.RuleModule<"noStrictNullCheck" | "preferOptionalSyntax" | "removeDefaultAssignment" | "useOptionalSyntax" | "uselessDefaultAssignment" | "uselessUndefined", [{
|
|
1021
1021
|
allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing?: boolean;
|
|
1022
1022
|
}], import("../rules").ESLintPluginDocs, TSESLint.RuleListener> & {
|
|
1023
1023
|
name: string;
|
|
@@ -77,7 +77,18 @@ exports.default = (0, util_1.createRule)({
|
|
|
77
77
|
messageId: 'removeAwait',
|
|
78
78
|
fix(fixer) {
|
|
79
79
|
const awaitKeyword = (0, util_1.nullThrows)(context.sourceCode.getFirstToken(node, util_1.isAwaitKeyword), util_1.NullThrowsReasons.MissingToken('await', 'await expression'));
|
|
80
|
-
|
|
80
|
+
const awaitTokenRemovalRange = (0, util_1.getAwaitTokenRemovalRange)(context.sourceCode, awaitKeyword);
|
|
81
|
+
const awaitRemovalFix = fixer.removeRange(awaitTokenRemovalRange);
|
|
82
|
+
const firstOperandToken = (0, util_1.nullThrows)(context.sourceCode.getTokenAfter(awaitKeyword), util_1.NullThrowsReasons.MissingToken('operand', 'await expression'));
|
|
83
|
+
if ((0, util_1.isStartOfArrowFunctionBodyNeedingParentheses)(node, firstOperandToken, context.sourceCode) ||
|
|
84
|
+
(0, util_1.isStartOfExpressionStatementNeedingParentheses)(node, firstOperandToken)) {
|
|
85
|
+
return [
|
|
86
|
+
awaitRemovalFix,
|
|
87
|
+
fixer.insertTextBefore(awaitArgumentEsNode, '('),
|
|
88
|
+
fixer.insertTextAfter(awaitArgumentEsNode, ')'),
|
|
89
|
+
];
|
|
90
|
+
}
|
|
91
|
+
return awaitRemovalFix;
|
|
81
92
|
},
|
|
82
93
|
},
|
|
83
94
|
],
|
|
@@ -135,7 +146,7 @@ exports.default = (0, util_1.createRule)({
|
|
|
135
146
|
messageId: 'convertToOrdinaryFor',
|
|
136
147
|
fix(fixer) {
|
|
137
148
|
const awaitToken = (0, util_1.nullThrows)(context.sourceCode.getFirstToken(node, util_1.isAwaitKeyword), util_1.NullThrowsReasons.MissingToken('await', 'for await loop'));
|
|
138
|
-
return fixer.
|
|
149
|
+
return fixer.removeRange((0, util_1.getAwaitTokenRemovalRange)(context.sourceCode, awaitToken));
|
|
139
150
|
},
|
|
140
151
|
},
|
|
141
152
|
],
|
|
@@ -168,7 +179,7 @@ exports.default = (0, util_1.createRule)({
|
|
|
168
179
|
messageId: 'removeAwait',
|
|
169
180
|
fix(fixer) {
|
|
170
181
|
const awaitToken = (0, util_1.nullThrows)(context.sourceCode.getFirstToken(node, util_1.isAwaitKeyword), util_1.NullThrowsReasons.MissingToken('await', 'await using'));
|
|
171
|
-
return fixer.
|
|
182
|
+
return fixer.removeRange((0, util_1.getAwaitTokenRemovalRange)(context.sourceCode, awaitToken));
|
|
172
183
|
},
|
|
173
184
|
},
|
|
174
185
|
}),
|
package/dist/rules/index.d.ts
CHANGED
|
@@ -315,7 +315,7 @@ declare const rules: {
|
|
|
315
315
|
'no-useless-constructor': import("@typescript-eslint/utils/ts-eslint").RuleModule<"noUselessConstructor" | "removeConstructor", [], import("../../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
316
316
|
name: string;
|
|
317
317
|
};
|
|
318
|
-
'no-useless-default-assignment': import("@typescript-eslint/utils/ts-eslint").RuleModule<"noStrictNullCheck" | "preferOptionalSyntax" | "uselessDefaultAssignment" | "uselessUndefined", [{
|
|
318
|
+
'no-useless-default-assignment': import("@typescript-eslint/utils/ts-eslint").RuleModule<"noStrictNullCheck" | "preferOptionalSyntax" | "removeDefaultAssignment" | "useOptionalSyntax" | "uselessDefaultAssignment" | "uselessUndefined", [{
|
|
319
319
|
allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing?: boolean;
|
|
320
320
|
}], import("../../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
321
321
|
name: string;
|
|
@@ -84,6 +84,11 @@ exports.default = (0, util_1.createRule)({
|
|
|
84
84
|
if (inner.type === utils_1.AST_NODE_TYPES.Literal && inner.value === 0) {
|
|
85
85
|
return;
|
|
86
86
|
}
|
|
87
|
+
// `void (x = value)` discards the assignment result so the
|
|
88
|
+
// expression evaluates to undefined (e.g. `() => void (x = 1)`).
|
|
89
|
+
if (inner.type === utils_1.AST_NODE_TYPES.AssignmentExpression) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
87
92
|
const tsArgument = services.esTreeNodeToTSNodeMap.get(node.argument);
|
|
88
93
|
const argType = services.getTypeAtLocation(node.argument);
|
|
89
94
|
// Allow `void promiseValue` so this rule does not fight no-floating-promises.
|
|
@@ -841,19 +841,23 @@ function hasWellKnownSymbolWithVoidReturn(checker, node, type, symbolName) {
|
|
|
841
841
|
function hasMatchingPromiseTypeArgument(checker, node) {
|
|
842
842
|
const type = checker.getTypeAtLocation(node);
|
|
843
843
|
const unionConstituents = tsutils.unionConstituents(checker.getApparentType(type));
|
|
844
|
-
const
|
|
845
|
-
if (
|
|
844
|
+
const promiseTypes = unionConstituents.filter(type => tsutils.isThenableType(checker, node, type));
|
|
845
|
+
if (promiseTypes.length === 0) {
|
|
846
846
|
return false;
|
|
847
847
|
}
|
|
848
|
-
const nonPromiseUnionConstituents = unionConstituents.filter(type => type
|
|
849
|
-
const
|
|
850
|
-
|
|
851
|
-
|
|
848
|
+
const nonPromiseUnionConstituents = unionConstituents.filter(type => !promiseTypes.includes(type));
|
|
849
|
+
const awaitedTypeConstituents = [];
|
|
850
|
+
for (const promiseType of promiseTypes) {
|
|
851
|
+
const awaitedType = checker.getAwaitedType(promiseType);
|
|
852
|
+
if (!awaitedType) {
|
|
853
|
+
return false;
|
|
854
|
+
}
|
|
855
|
+
awaitedTypeConstituents.push(...tsutils.unionConstituents(awaitedType));
|
|
852
856
|
}
|
|
853
|
-
const
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
+
const typesAreEquivalent = (left, right) => checker.isTypeAssignableTo(left, right) &&
|
|
858
|
+
checker.isTypeAssignableTo(right, left);
|
|
859
|
+
return (nonPromiseUnionConstituents.every(type => awaitedTypeConstituents.some(awaited => typesAreEquivalent(type, awaited))) &&
|
|
860
|
+
awaitedTypeConstituents.every(awaited => nonPromiseUnionConstituents.some(type => typesAreEquivalent(type, awaited))));
|
|
857
861
|
}
|
|
858
862
|
function normalizeFlagUnionsOption(checksConditionals) {
|
|
859
863
|
if (!checksConditionals || checksConditionals === true) {
|
|
@@ -55,6 +55,15 @@ function isPossiblyNullish(type) {
|
|
|
55
55
|
.unionConstituents(type)
|
|
56
56
|
.some(t => isNullishType(t) || (0, util_1.isTypeFlagSet)(t, ts.TypeFlags.Void));
|
|
57
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Note that this differs from {@link isNullableType} in that it doesn't consider
|
|
60
|
+
* `any` or `unknown` to be nullable.
|
|
61
|
+
*/
|
|
62
|
+
function isPossiblyNonNullish(type) {
|
|
63
|
+
return tsutils
|
|
64
|
+
.unionConstituents(type)
|
|
65
|
+
.some(t => !isNullishType(t) && !(0, util_1.isTypeFlagSet)(t, ts.TypeFlags.Void));
|
|
66
|
+
}
|
|
58
67
|
function toStaticValue(type) {
|
|
59
68
|
// type.isLiteral() only covers numbers/bigints and strings, hence the rest of the branches.
|
|
60
69
|
if (tsutils.isBooleanLiteralType(type)) {
|
|
@@ -262,11 +271,48 @@ exports.default = (0, util_1.createRule)({
|
|
|
262
271
|
const propertyType = objectType
|
|
263
272
|
.getProperties()
|
|
264
273
|
.find(prop => prop.name === propertyName);
|
|
265
|
-
|
|
266
|
-
tsutils.isSymbolFlagSet(propertyType, ts.SymbolFlags.Optional))
|
|
267
|
-
|
|
274
|
+
return (propertyType != null &&
|
|
275
|
+
tsutils.isSymbolFlagSet(propertyType, ts.SymbolFlags.Optional));
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Obtains the property types of the computed member expression
|
|
279
|
+
* @param node The member expression
|
|
280
|
+
* @returns The property types, unless the node isn't computed.
|
|
281
|
+
*/
|
|
282
|
+
function getComputedMemberPropertyTypes(node) {
|
|
283
|
+
if (!node.computed) {
|
|
284
|
+
return undefined;
|
|
285
|
+
}
|
|
286
|
+
const objectType = (0, util_1.getConstrainedTypeAtLocation)(services, node.object);
|
|
287
|
+
const propertyType = (0, util_1.getConstrainedTypeAtLocation)(services, node.property);
|
|
288
|
+
const propertyTypes = [];
|
|
289
|
+
for (const keyType of tsutils.unionConstituents(propertyType)) {
|
|
290
|
+
if (!keyType.isStringLiteral() && !keyType.isNumberLiteral()) {
|
|
291
|
+
return undefined;
|
|
292
|
+
}
|
|
293
|
+
const selectedType = (0, util_1.getTypeOfPropertyOfName)(checker, objectType, String(keyType.value));
|
|
294
|
+
if (!selectedType) {
|
|
295
|
+
return undefined;
|
|
296
|
+
}
|
|
297
|
+
propertyTypes.push(selectedType);
|
|
268
298
|
}
|
|
269
|
-
return
|
|
299
|
+
return propertyTypes;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Verify if the node may have non-nullish properties
|
|
303
|
+
* @param node The node to verify
|
|
304
|
+
* @returns If it may have non-nullish properties, unless the node isn't a
|
|
305
|
+
* MemberExpression
|
|
306
|
+
*/
|
|
307
|
+
function hasPossiblyNonNullishComputedMemberProperty(node) {
|
|
308
|
+
if (node.type !== utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
309
|
+
return false;
|
|
310
|
+
}
|
|
311
|
+
const propertyTypes = getComputedMemberPropertyTypes(node);
|
|
312
|
+
if (propertyTypes == null) {
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
return propertyTypes.some(isPossiblyNonNullish);
|
|
270
316
|
}
|
|
271
317
|
/**
|
|
272
318
|
* Checks if a conditional node is necessary:
|
|
@@ -344,7 +390,8 @@ exports.default = (0, util_1.createRule)({
|
|
|
344
390
|
messageId = 'neverNullish';
|
|
345
391
|
}
|
|
346
392
|
}
|
|
347
|
-
else if (isAlwaysNullish(type)
|
|
393
|
+
else if (isAlwaysNullish(type) &&
|
|
394
|
+
!hasPossiblyNonNullishComputedMemberProperty(node)) {
|
|
348
395
|
messageId = 'alwaysNullish';
|
|
349
396
|
}
|
|
350
397
|
if (messageId) {
|
|
@@ -101,10 +101,6 @@ exports.default = (0, util_1.createRule)({
|
|
|
101
101
|
});
|
|
102
102
|
},
|
|
103
103
|
"MethodDefinition[kind='constructor'] > FunctionExpression AssignmentExpression"(node) {
|
|
104
|
-
const leftName = getPropertyName(node.left);
|
|
105
|
-
if (!leftName) {
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
104
|
let functionNode = findParentFunction(node);
|
|
109
105
|
if (functionNode && isArrowIIFE(functionNode)) {
|
|
110
106
|
functionNode = findParentFunction(functionNode.parent);
|
|
@@ -113,6 +109,18 @@ exports.default = (0, util_1.createRule)({
|
|
|
113
109
|
return;
|
|
114
110
|
}
|
|
115
111
|
const { assignedBeforeUnnecessary, unnecessaryAssignments } = (0, util_1.nullThrows)(reportInfoStack.at(reportInfoStack.length - 1), 'The top of stack should exist');
|
|
112
|
+
// A prior write to the parameter binding means a later `this.x = x`
|
|
113
|
+
// may copy a different value than the parameter property initialized.
|
|
114
|
+
if (node.left.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
115
|
+
if (isReferenceFromParameter(node.left)) {
|
|
116
|
+
assignedBeforeUnnecessary.add(node.left.name);
|
|
117
|
+
}
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const leftName = getPropertyName(node.left);
|
|
121
|
+
if (!leftName) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
116
124
|
if (!UNNECESSARY_OPERATORS.has(node.operator)) {
|
|
117
125
|
assignedBeforeUnnecessary.add(leftName);
|
|
118
126
|
return;
|
|
@@ -587,11 +587,8 @@ exports.default = (0, util_1.createRule)({
|
|
|
587
587
|
// is parsed as a block body. In those positions the operand must be
|
|
588
588
|
// wrapped in parentheses to stay an expression.
|
|
589
589
|
const firstOperandToken = (0, util_1.nullThrows)(context.sourceCode.getTokenAfter(closingAngleBracket), util_1.NullThrowsReasons.MissingToken('operand', 'type assertion'));
|
|
590
|
-
const
|
|
591
|
-
(0, util_1.
|
|
592
|
-
const breaksArrowFunctionBody = firstOperandToken.value === '{' &&
|
|
593
|
-
(0, util_1.isStartOfArrowFunctionBody)(node, context.sourceCode);
|
|
594
|
-
const needsParens = breaksExpressionStatement || breaksArrowFunctionBody;
|
|
590
|
+
const needsParens = (0, util_1.isStartOfExpressionStatementNeedingParentheses)(node, firstOperandToken) ||
|
|
591
|
+
(0, util_1.isStartOfArrowFunctionBodyNeedingParentheses)(node, firstOperandToken, context.sourceCode);
|
|
595
592
|
const fixes = [];
|
|
596
593
|
if (needsParens) {
|
|
597
594
|
fixes.push(fixer.insertTextBefore(node, '('));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { TSESLint } from '@typescript-eslint/utils';
|
|
2
|
-
type MessageId = 'noStrictNullCheck' | 'preferOptionalSyntax' | 'uselessDefaultAssignment' | 'uselessUndefined';
|
|
2
|
+
type MessageId = 'noStrictNullCheck' | 'preferOptionalSyntax' | 'removeDefaultAssignment' | 'uselessDefaultAssignment' | 'uselessUndefined' | 'useOptionalSyntax';
|
|
3
3
|
type Options = [
|
|
4
4
|
{
|
|
5
5
|
allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing?: boolean;
|
|
@@ -46,12 +46,14 @@ exports.default = (0, util_1.createRule)({
|
|
|
46
46
|
recommended: 'strict',
|
|
47
47
|
requiresTypeChecking: true,
|
|
48
48
|
},
|
|
49
|
-
|
|
49
|
+
hasSuggestions: true,
|
|
50
50
|
messages: {
|
|
51
51
|
noStrictNullCheck: 'This rule requires the `strictNullChecks` compiler option to be turned on to function correctly.',
|
|
52
|
-
preferOptionalSyntax: 'Using `= undefined` to make a parameter optional adds unnecessary runtime logic.
|
|
52
|
+
preferOptionalSyntax: 'Using `= undefined` to make a parameter optional adds unnecessary runtime logic.',
|
|
53
|
+
removeDefaultAssignment: 'Remove the default value.',
|
|
53
54
|
uselessDefaultAssignment: 'Default value is useless because the {{ type }} is not optional.',
|
|
54
55
|
uselessUndefined: 'Default value is useless because it is undefined. Optional {{ type }}s are already undefined by default.',
|
|
56
|
+
useOptionalSyntax: 'Use the `?` optional syntax instead.',
|
|
55
57
|
},
|
|
56
58
|
schema: [
|
|
57
59
|
{
|
|
@@ -286,7 +288,7 @@ exports.default = (0, util_1.createRule)({
|
|
|
286
288
|
node: node.right,
|
|
287
289
|
messageId: 'uselessDefaultAssignment',
|
|
288
290
|
data: { type },
|
|
289
|
-
|
|
291
|
+
suggest: [removeDefaultSuggestion(node)],
|
|
290
292
|
});
|
|
291
293
|
}
|
|
292
294
|
function reportUselessUndefined(node, type) {
|
|
@@ -294,13 +296,19 @@ exports.default = (0, util_1.createRule)({
|
|
|
294
296
|
node: node.right,
|
|
295
297
|
messageId: 'uselessUndefined',
|
|
296
298
|
data: { type },
|
|
297
|
-
|
|
299
|
+
suggest: [removeDefaultSuggestion(node)],
|
|
298
300
|
});
|
|
299
301
|
}
|
|
300
302
|
function reportPreferOptionalSyntax(node) {
|
|
301
303
|
context.report({
|
|
302
304
|
node: node.right,
|
|
303
305
|
messageId: 'preferOptionalSyntax',
|
|
306
|
+
suggest: [useOptionalSyntaxSuggestion(node)],
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
function useOptionalSyntaxSuggestion(node) {
|
|
310
|
+
return {
|
|
311
|
+
messageId: 'useOptionalSyntax',
|
|
304
312
|
*fix(fixer) {
|
|
305
313
|
yield removeDefault(fixer, node);
|
|
306
314
|
const { left } = node;
|
|
@@ -308,7 +316,13 @@ exports.default = (0, util_1.createRule)({
|
|
|
308
316
|
yield fixer.insertTextAfterRange([left.range[0], left.range[0] + left.name.length], '?');
|
|
309
317
|
}
|
|
310
318
|
},
|
|
311
|
-
}
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
function removeDefaultSuggestion(node) {
|
|
322
|
+
return {
|
|
323
|
+
messageId: 'removeDefaultAssignment',
|
|
324
|
+
fix: fixer => removeDefault(fixer, node),
|
|
325
|
+
};
|
|
312
326
|
}
|
|
313
327
|
function removeDefault(fixer, node) {
|
|
314
328
|
const start = node.left.range[1];
|
|
@@ -211,20 +211,10 @@ exports.default = (0, util_1.createRule)({
|
|
|
211
211
|
/* istanbul ignore if */ if (!awaitToken) {
|
|
212
212
|
return null;
|
|
213
213
|
}
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
// Also remove any extraneous whitespace after `await`, if there is any.
|
|
217
|
-
const nextToken = context.sourceCode.getTokenAfter(awaitToken, {
|
|
218
|
-
includeComments: true,
|
|
219
|
-
});
|
|
220
|
-
if (nextToken) {
|
|
221
|
-
endAt = nextToken.range[0];
|
|
222
|
-
}
|
|
223
|
-
const awaitRemovalFix = fixer.removeRange([startAt, endAt]);
|
|
214
|
+
const awaitTokenRemovalRange = (0, util_1.getAwaitTokenRemovalRange)(context.sourceCode, awaitToken);
|
|
215
|
+
const awaitRemovalFix = fixer.removeRange(awaitTokenRemovalRange);
|
|
224
216
|
const firstOperandToken = (0, util_1.nullThrows)(context.sourceCode.getTokenAfter(awaitToken), util_1.NullThrowsReasons.MissingToken('operand', 'await expression'));
|
|
225
|
-
|
|
226
|
-
(0, util_1.isStartOfArrowFunctionBody)(node, context.sourceCode);
|
|
227
|
-
if (shouldWrapInParentheses) {
|
|
217
|
+
if ((0, util_1.isStartOfArrowFunctionBodyNeedingParentheses)(node, firstOperandToken, context.sourceCode)) {
|
|
228
218
|
return [
|
|
229
219
|
awaitRemovalFix,
|
|
230
220
|
fixer.insertTextBefore(node.argument, '('),
|
|
@@ -90,6 +90,19 @@ const SUPPORTED_GLOBAL_TYPES = [
|
|
|
90
90
|
'Math',
|
|
91
91
|
'JSON',
|
|
92
92
|
];
|
|
93
|
+
/**
|
|
94
|
+
* Methods on instances of built-in classes that are defined by the ECMAScript
|
|
95
|
+
* spec as *bound* to their instance (i.e. they are safe to call without their
|
|
96
|
+
* receiver), but whose TypeScript declarations don't model that binding - they
|
|
97
|
+
* are declared as plain methods that would require a `this` value.
|
|
98
|
+
*
|
|
99
|
+
* For example, `Intl.Collator.prototype.compare` is defined as a bound getter
|
|
100
|
+
* function per the ECMA-402 spec:
|
|
101
|
+
* https://tc39.es/ecma402/#sec-intl.collator.prototype.compare
|
|
102
|
+
* ...but `lib.es5.d.ts` declares it as a regular method, so this rule would
|
|
103
|
+
* otherwise incorrectly flag usages such as `array.sort(collator.compare)`.
|
|
104
|
+
*/
|
|
105
|
+
const nativelyBoundInstanceMethods = new Set(['Collator.compare']);
|
|
93
106
|
const isNotImported = (symbol, currentSourceFile) => {
|
|
94
107
|
const { valueDeclaration } = symbol;
|
|
95
108
|
if (!valueDeclaration) {
|
|
@@ -99,6 +112,18 @@ const isNotImported = (symbol, currentSourceFile) => {
|
|
|
99
112
|
return (!!currentSourceFile &&
|
|
100
113
|
currentSourceFile !== valueDeclaration.getSourceFile());
|
|
101
114
|
};
|
|
115
|
+
/**
|
|
116
|
+
* Returns `true` if `propertyName` is a member of a built-in class instance
|
|
117
|
+
* that is defined by the spec as bound to that instance (see
|
|
118
|
+
* `nativelyBoundInstanceMethods` above).
|
|
119
|
+
*/
|
|
120
|
+
function isSpecBoundBuiltinMethod(program, objectType, propertyName) {
|
|
121
|
+
const symbol = objectType.getSymbol();
|
|
122
|
+
if (!symbol || !(0, util_1.isSymbolFromDefaultLibrary)(program, symbol)) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
return nativelyBoundInstanceMethods.has(`${symbol.getName()}.${propertyName}`);
|
|
126
|
+
}
|
|
102
127
|
const BASE_MESSAGE = [
|
|
103
128
|
`A method that is not declared with \`this: void\` may cause unintentional scoping of \`this\` when separated from its object.`,
|
|
104
129
|
`Consider using an arrow function or explicitly \`.bind()\`ing the method to avoid calling the method with an unintended \`this\` value. `,
|
|
@@ -196,6 +221,12 @@ exports.default = (0, util_1.createRule)({
|
|
|
196
221
|
return true;
|
|
197
222
|
}
|
|
198
223
|
}
|
|
224
|
+
if (property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
225
|
+
const objectType = services.getTypeAtLocation(object);
|
|
226
|
+
if (isSpecBoundBuiltinMethod(services.program, objectType, property.name)) {
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
199
230
|
// if `${object.name}.${property.name}` doesn't match any of
|
|
200
231
|
// the nativelyBoundMembers, then we fallback to type-level checks
|
|
201
232
|
return ((0, util_1.isBuiltinSymbolLike)(services.program, services.getTypeAtLocation(object), SUPPORTED_GLOBAL_TYPES) &&
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getAwaitTokenRemovalRange = getAwaitTokenRemovalRange;
|
|
4
|
+
function getAwaitTokenRemovalRange(sourceCode, awaitToken) {
|
|
5
|
+
const startAt = awaitToken.range[0];
|
|
6
|
+
let endAt = awaitToken.range[1];
|
|
7
|
+
const nextToken = sourceCode.getTokenAfter(awaitToken, {
|
|
8
|
+
includeComments: true,
|
|
9
|
+
});
|
|
10
|
+
/* istanbul ignore else */ if (nextToken) {
|
|
11
|
+
endAt = nextToken.range[0];
|
|
12
|
+
}
|
|
13
|
+
return [startAt, endAt];
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=getAwaitTokenRemovalRange.js.map
|
package/dist/util/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export * from './baseTypeUtils';
|
|
|
4
4
|
export * from './collectUnusedVariables';
|
|
5
5
|
export * from './createRule';
|
|
6
6
|
export * from './FunctionSignature';
|
|
7
|
+
export * from './getAwaitTokenRemovalRange';
|
|
7
8
|
export * from './getBaseTypesOfClassMember';
|
|
8
9
|
export * from './getFixOrSuggest';
|
|
9
10
|
export * from './getFunctionHeadLoc';
|
|
@@ -19,8 +20,9 @@ export * from './isAssignee';
|
|
|
19
20
|
export * from './isConditionalTest';
|
|
20
21
|
export * from './isNodeEqual';
|
|
21
22
|
export * from './isNullLiteral';
|
|
22
|
-
export * from './
|
|
23
|
+
export * from './isStartOfArrowFunctionBodyNeedingParentheses';
|
|
23
24
|
export * from './isStartOfExpressionStatement';
|
|
25
|
+
export * from './isStartOfExpressionStatementNeedingParentheses';
|
|
24
26
|
export * from './isUndefinedIdentifier';
|
|
25
27
|
export * from './misc';
|
|
26
28
|
export * from './needsPrecedingSemiColon';
|
package/dist/util/index.js
CHANGED
|
@@ -21,6 +21,7 @@ __exportStar(require("./baseTypeUtils"), exports);
|
|
|
21
21
|
__exportStar(require("./collectUnusedVariables"), exports);
|
|
22
22
|
__exportStar(require("./createRule"), exports);
|
|
23
23
|
__exportStar(require("./FunctionSignature"), exports);
|
|
24
|
+
__exportStar(require("./getAwaitTokenRemovalRange"), exports);
|
|
24
25
|
__exportStar(require("./getBaseTypesOfClassMember"), exports);
|
|
25
26
|
__exportStar(require("./getFixOrSuggest"), exports);
|
|
26
27
|
__exportStar(require("./getFunctionHeadLoc"), exports);
|
|
@@ -36,8 +37,9 @@ __exportStar(require("./isAssignee"), exports);
|
|
|
36
37
|
__exportStar(require("./isConditionalTest"), exports);
|
|
37
38
|
__exportStar(require("./isNodeEqual"), exports);
|
|
38
39
|
__exportStar(require("./isNullLiteral"), exports);
|
|
39
|
-
__exportStar(require("./
|
|
40
|
+
__exportStar(require("./isStartOfArrowFunctionBodyNeedingParentheses"), exports);
|
|
40
41
|
__exportStar(require("./isStartOfExpressionStatement"), exports);
|
|
42
|
+
__exportStar(require("./isStartOfExpressionStatementNeedingParentheses"), exports);
|
|
41
43
|
__exportStar(require("./isUndefinedIdentifier"), exports);
|
|
42
44
|
__exportStar(require("./misc"), exports);
|
|
43
45
|
__exportStar(require("./needsPrecedingSemiColon"), exports);
|
package/dist/util/{isStartOfArrowFunctionBody.js → isStartOfArrowFunctionBodyNeedingParentheses.js}
RENAMED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.isStartOfArrowFunctionBodyNeedingParentheses = isStartOfArrowFunctionBodyNeedingParentheses;
|
|
4
4
|
const utils_1 = require("@typescript-eslint/utils");
|
|
5
5
|
const astUtils_1 = require("./astUtils");
|
|
6
|
+
function isStartOfArrowFunctionBodyNeedingParentheses(node, firstToken, sourceCode) {
|
|
7
|
+
return ((0, astUtils_1.isOpeningBraceToken)(firstToken) &&
|
|
8
|
+
isStartOfArrowFunctionBody(node, sourceCode));
|
|
9
|
+
}
|
|
6
10
|
function isStartOfArrowFunctionBody(node, sourceCode) {
|
|
7
11
|
let current = node;
|
|
8
12
|
while (true) {
|
|
@@ -23,4 +27,4 @@ function isStartOfArrowFunctionBody(node, sourceCode) {
|
|
|
23
27
|
current = parent;
|
|
24
28
|
}
|
|
25
29
|
}
|
|
26
|
-
//# sourceMappingURL=
|
|
30
|
+
//# sourceMappingURL=isStartOfArrowFunctionBodyNeedingParentheses.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isStartOfExpressionStatementNeedingParentheses = isStartOfExpressionStatementNeedingParentheses;
|
|
4
|
+
const isStartOfExpressionStatement_1 = require("./isStartOfExpressionStatement");
|
|
5
|
+
function isStartOfExpressionStatementNeedingParentheses(node, firstToken) {
|
|
6
|
+
return (['{', 'class', 'function'].includes(firstToken.value) &&
|
|
7
|
+
(0, isStartOfExpressionStatement_1.isStartOfExpressionStatement)(node));
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=isStartOfExpressionStatementNeedingParentheses.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typescript-eslint/eslint-plugin",
|
|
3
|
-
"version": "8.70.
|
|
3
|
+
"version": "8.70.2-alpha.0",
|
|
4
4
|
"description": "TypeScript plugin for ESLint",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -47,10 +47,10 @@
|
|
|
47
47
|
],
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@eslint-community/regexpp": "^4.12.2",
|
|
50
|
-
"@typescript-eslint/scope-manager": "8.70.
|
|
51
|
-
"@typescript-eslint/type-utils": "8.70.
|
|
52
|
-
"@typescript-eslint/utils": "8.70.
|
|
53
|
-
"@typescript-eslint/visitor-keys": "8.70.
|
|
50
|
+
"@typescript-eslint/scope-manager": "8.70.2-alpha.0",
|
|
51
|
+
"@typescript-eslint/type-utils": "8.70.2-alpha.0",
|
|
52
|
+
"@typescript-eslint/utils": "8.70.2-alpha.0",
|
|
53
|
+
"@typescript-eslint/visitor-keys": "8.70.2-alpha.0",
|
|
54
54
|
"ignore": "^7.0.5",
|
|
55
55
|
"natural-compare": "^1.4.0",
|
|
56
56
|
"ts-api-utils": "^2.5.0"
|
|
@@ -60,8 +60,8 @@
|
|
|
60
60
|
"@types/mdast": "^4.0.4",
|
|
61
61
|
"@types/natural-compare": "^1.4.3",
|
|
62
62
|
"@types/react": "^18.3.21",
|
|
63
|
-
"@typescript-eslint/rule-schema-to-typescript-types": "8.70.
|
|
64
|
-
"@typescript-eslint/rule-tester": "8.70.
|
|
63
|
+
"@typescript-eslint/rule-schema-to-typescript-types": "8.70.2-alpha.0",
|
|
64
|
+
"@typescript-eslint/rule-tester": "8.70.2-alpha.0",
|
|
65
65
|
"@typescript/native": "npm:typescript@^7.0.2",
|
|
66
66
|
"@vitest/coverage-v8": "^4.0.18",
|
|
67
67
|
"ajv": "^6.12.6",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"vitest": "^4.0.18"
|
|
82
82
|
},
|
|
83
83
|
"peerDependencies": {
|
|
84
|
-
"@typescript-eslint/parser": "^8.70.
|
|
84
|
+
"@typescript-eslint/parser": "^8.70.2-alpha.0",
|
|
85
85
|
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
|
|
86
86
|
"typescript": ">=4.8.4 <6.1.0"
|
|
87
87
|
},
|