@typescript-eslint/eslint-plugin 8.70.1-alpha.17 → 8.70.1-alpha.19
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.
|
@@ -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;
|
|
@@ -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) &&
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typescript-eslint/eslint-plugin",
|
|
3
|
-
"version": "8.70.1-alpha.
|
|
3
|
+
"version": "8.70.1-alpha.19",
|
|
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.1-alpha.
|
|
51
|
-
"@typescript-eslint/type-utils": "8.70.1-alpha.
|
|
52
|
-
"@typescript-eslint/utils": "8.70.1-alpha.
|
|
53
|
-
"@typescript-eslint/visitor-keys": "8.70.1-alpha.
|
|
50
|
+
"@typescript-eslint/scope-manager": "8.70.1-alpha.19",
|
|
51
|
+
"@typescript-eslint/type-utils": "8.70.1-alpha.19",
|
|
52
|
+
"@typescript-eslint/utils": "8.70.1-alpha.19",
|
|
53
|
+
"@typescript-eslint/visitor-keys": "8.70.1-alpha.19",
|
|
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.1-alpha.
|
|
64
|
-
"@typescript-eslint/rule-tester": "8.70.1-alpha.
|
|
63
|
+
"@typescript-eslint/rule-schema-to-typescript-types": "8.70.1-alpha.19",
|
|
64
|
+
"@typescript-eslint/rule-tester": "8.70.1-alpha.19",
|
|
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.1-alpha.
|
|
84
|
+
"@typescript-eslint/parser": "^8.70.1-alpha.19",
|
|
85
85
|
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
|
|
86
86
|
"typescript": ">=4.8.4 <6.1.0"
|
|
87
87
|
},
|