@typescript-eslint/eslint-plugin 8.71.1-alpha.1 → 8.71.1-alpha.10
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-misused-promises.js +3 -2
- package/dist/rules/no-unsafe-enum-assignment.js +51 -7
- package/dist/rules/no-unused-vars.js +2 -4
- package/dist/rules/require-array-sort-compare.js +4 -1
- package/dist/util/collectUnusedVariables.js +10 -7
- package/dist/util/isTypeOnlyReference.d.ts +4 -0
- package/dist/util/isTypeOnlyReference.js +22 -0
- package/package.json +8 -8
|
@@ -337,8 +337,9 @@ exports.default = (0, util_1.createRule)({
|
|
|
337
337
|
}
|
|
338
338
|
}
|
|
339
339
|
function checkArguments(node) {
|
|
340
|
-
if (node.
|
|
341
|
-
|
|
340
|
+
if (node.arguments.length === 0 ||
|
|
341
|
+
(node.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
|
342
|
+
isPromiseFinallyMethod(node))) {
|
|
342
343
|
return;
|
|
343
344
|
}
|
|
344
345
|
const tsNode = services.esTreeNodeToTSNodeMap.get(node);
|
|
@@ -147,7 +147,7 @@ exports.default = (0, util_1.createRule)({
|
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
149
|
function isUnsafeAssignment(senderNode, receiverType, senderType = services.getTypeAtLocation(senderNode)) {
|
|
150
|
-
return (
|
|
150
|
+
return (hasEnumAssignmentMismatch(checker, senderType, receiverType) &&
|
|
151
151
|
!isSafeEnumBitwiseExpression(senderNode, receiverType));
|
|
152
152
|
}
|
|
153
153
|
function checkAssignment(receiverType, senderNode, reportingNode, messageId = 'unsafeEnumAssignment', senderType) {
|
|
@@ -181,7 +181,7 @@ exports.default = (0, util_1.createRule)({
|
|
|
181
181
|
.flatMap(elementType => {
|
|
182
182
|
const parameterType = signature.getNextParameterType();
|
|
183
183
|
return parameterType != null &&
|
|
184
|
-
|
|
184
|
+
hasEnumAssignmentMismatch(checker, elementType, parameterType)
|
|
185
185
|
? [parameterType]
|
|
186
186
|
: [];
|
|
187
187
|
});
|
|
@@ -311,7 +311,8 @@ exports.default = (0, util_1.createRule)({
|
|
|
311
311
|
// declare const foo: { [key in Fruit]: string };
|
|
312
312
|
// foo[0];
|
|
313
313
|
const senderType = services.getTypeAtLocation(node.property);
|
|
314
|
-
if (receiverTypes.every(receiverType =>
|
|
314
|
+
if (receiverTypes.every(receiverType => hasEnumAssignmentMismatch(checker, senderType, receiverType) ||
|
|
315
|
+
!checker.isTypeAssignableTo(senderType, receiverType))) {
|
|
315
316
|
report(node.property, 'unsafeEnumAccess', receiverTypes);
|
|
316
317
|
}
|
|
317
318
|
},
|
|
@@ -355,9 +356,46 @@ function getConstraintType(checker, type) {
|
|
|
355
356
|
function getTypeArguments(checker, type) {
|
|
356
357
|
return tsutils.isTypeReference(type) ? checker.getTypeArguments(type) : [];
|
|
357
358
|
}
|
|
358
|
-
|
|
359
|
+
const genericTypes = new WeakMap();
|
|
360
|
+
function isGenericType(checker, type) {
|
|
361
|
+
let generic = genericTypes.get(type);
|
|
362
|
+
if (generic == null) {
|
|
363
|
+
genericTypes.set(type, false);
|
|
364
|
+
generic =
|
|
365
|
+
tsutils.isTypeFlagSet(type, ts.TypeFlags.Instantiable) ||
|
|
366
|
+
[
|
|
367
|
+
...(tsutils.isUnionOrIntersectionType(type) ? type.types : []),
|
|
368
|
+
...(type.aliasTypeArguments ?? []),
|
|
369
|
+
...getTypeArguments(checker, type),
|
|
370
|
+
].some(part => isGenericType(checker, part));
|
|
371
|
+
genericTypes.set(type, generic);
|
|
372
|
+
}
|
|
373
|
+
return generic;
|
|
374
|
+
}
|
|
375
|
+
const maximumDepth = 5;
|
|
376
|
+
const topLevelMismatches = new WeakMap();
|
|
377
|
+
function hasEnumAssignmentMismatch(checker, senderType, receiverType) {
|
|
378
|
+
let receiverMismatches = topLevelMismatches.get(senderType);
|
|
379
|
+
if (receiverMismatches == null) {
|
|
380
|
+
receiverMismatches = new WeakMap();
|
|
381
|
+
topLevelMismatches.set(senderType, receiverMismatches);
|
|
382
|
+
}
|
|
383
|
+
let mismatch = receiverMismatches.get(receiverType);
|
|
384
|
+
if (mismatch == null) {
|
|
385
|
+
mismatch = hasDeepEnumAssignmentMismatch(checker, senderType, receiverType);
|
|
386
|
+
receiverMismatches.set(receiverType, mismatch);
|
|
387
|
+
}
|
|
388
|
+
return mismatch;
|
|
389
|
+
}
|
|
390
|
+
function hasDeepEnumAssignmentMismatch(checker, senderType, receiverType, visited = new WeakMap(), depth = 0, withinGenericMembers = false) {
|
|
391
|
+
if (depth > maximumDepth) {
|
|
392
|
+
return false;
|
|
393
|
+
}
|
|
359
394
|
const constrainedSenderType = getConstraintType(checker, senderType);
|
|
360
395
|
const constrainedReceiverType = getConstraintType(checker, receiverType);
|
|
396
|
+
if (constrainedSenderType === constrainedReceiverType) {
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
361
399
|
// Recursive types would otherwise be visited endlessly.
|
|
362
400
|
let visitedReceiverTypes = visited.get(constrainedSenderType);
|
|
363
401
|
if (visitedReceiverTypes == null) {
|
|
@@ -375,22 +413,28 @@ function hasDeepEnumAssignmentMismatch(checker, senderType, receiverType, visite
|
|
|
375
413
|
const senderTypeArguments = getTypeArguments(checker, constrainedSenderType);
|
|
376
414
|
const receiverTypeArguments = getTypeArguments(checker, constrainedReceiverType);
|
|
377
415
|
if (senderTypeArguments.length === receiverTypeArguments.length &&
|
|
378
|
-
senderTypeArguments.some((senderTypeArgument, index) => hasDeepEnumAssignmentMismatch(checker, senderTypeArgument, receiverTypeArguments[index], visited))) {
|
|
416
|
+
senderTypeArguments.some((senderTypeArgument, index) => hasDeepEnumAssignmentMismatch(checker, senderTypeArgument, receiverTypeArguments[index], visited, depth + 1, withinGenericMembers))) {
|
|
379
417
|
return true;
|
|
380
418
|
}
|
|
419
|
+
// Members of generic types can instantiate ever-larger generic types.
|
|
420
|
+
const generic = isGenericType(checker, constrainedSenderType) ||
|
|
421
|
+
isGenericType(checker, constrainedReceiverType);
|
|
422
|
+
if (generic && withinGenericMembers) {
|
|
423
|
+
return false;
|
|
424
|
+
}
|
|
381
425
|
// [number, Fruit] -> Fruit[]
|
|
382
426
|
const senderElementType = constrainedSenderType.getNumberIndexType();
|
|
383
427
|
const receiverElementType = constrainedReceiverType.getNumberIndexType();
|
|
384
428
|
if (senderElementType &&
|
|
385
429
|
receiverElementType &&
|
|
386
|
-
hasDeepEnumAssignmentMismatch(checker, senderElementType, receiverElementType, visited)) {
|
|
430
|
+
hasDeepEnumAssignmentMismatch(checker, senderElementType, receiverElementType, visited, depth + 1, generic || withinGenericMembers)) {
|
|
387
431
|
return true;
|
|
388
432
|
}
|
|
389
433
|
// { fruit: number } -> { fruit: Fruit }
|
|
390
434
|
return constrainedReceiverType.getProperties().some(receiverProperty => {
|
|
391
435
|
const senderProperty = constrainedSenderType.getProperty(receiverProperty.name);
|
|
392
436
|
return (senderProperty != null &&
|
|
393
|
-
hasDeepEnumAssignmentMismatch(checker, checker.getTypeOfSymbol(senderProperty), checker.getTypeOfSymbol(receiverProperty), visited));
|
|
437
|
+
hasDeepEnumAssignmentMismatch(checker, checker.getTypeOfSymbol(senderProperty), checker.getTypeOfSymbol(receiverProperty), visited, depth + 1, generic || withinGenericMembers));
|
|
394
438
|
});
|
|
395
439
|
}
|
|
396
440
|
function getMappedKeyConstraintTypes(checker, declaration) {
|
|
@@ -3,8 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const scope_manager_1 = require("@typescript-eslint/scope-manager");
|
|
4
4
|
const utils_1 = require("@typescript-eslint/utils");
|
|
5
5
|
const util_1 = require("../util");
|
|
6
|
-
const
|
|
7
|
-
const referenceContainsTypeQuery_1 = require("../util/referenceContainsTypeQuery");
|
|
6
|
+
const isTypeOnlyReference_1 = require("../util/isTypeOnlyReference");
|
|
8
7
|
// this is a superset of DefinitionType which defines sub-types for better granularity
|
|
9
8
|
var VariableType;
|
|
10
9
|
(function (VariableType) {
|
|
@@ -821,8 +820,7 @@ exports.default = (0, util_1.createRule)({
|
|
|
821
820
|
for (const unusedVar of unusedVars) {
|
|
822
821
|
// Report the first declaration.
|
|
823
822
|
if (unusedVar.defs.length > 0) {
|
|
824
|
-
const usedOnlyAsType = unusedVar.references.some(ref => (0,
|
|
825
|
-
(0, referenceContainsTypePredicate_1.referenceContainsTypePredicate)(ref.identifier));
|
|
823
|
+
const usedOnlyAsType = unusedVar.references.some(ref => (0, isTypeOnlyReference_1.isTypeOnlyReference)(unusedVar, ref));
|
|
826
824
|
const messageId = usedOnlyAsType ? 'usedOnlyAsType' : 'unusedVar';
|
|
827
825
|
const isImportUsedOnlyAsType = usedOnlyAsType &&
|
|
828
826
|
unusedVar.defs.some(def => def.type === scope_manager_1.DefinitionType.ImportBinding);
|
|
@@ -40,7 +40,10 @@ exports.default = (0, util_1.createRule)({
|
|
|
40
40
|
const type = (0, util_1.getConstrainedTypeAtLocation)(services, node);
|
|
41
41
|
if (checker.isArrayType(type) || checker.isTupleType(type)) {
|
|
42
42
|
const typeArgs = checker.getTypeArguments(type);
|
|
43
|
-
return typeArgs.every(arg =>
|
|
43
|
+
return typeArgs.every(arg => {
|
|
44
|
+
const typeName = (0, util_1.getTypeName)(checker, checker.getBaseConstraintOfType(arg) ?? arg);
|
|
45
|
+
return typeName === 'string';
|
|
46
|
+
});
|
|
44
47
|
}
|
|
45
48
|
return false;
|
|
46
49
|
}
|
|
@@ -4,8 +4,7 @@ exports.collectVariables = collectVariables;
|
|
|
4
4
|
const scope_manager_1 = require("@typescript-eslint/scope-manager");
|
|
5
5
|
const utils_1 = require("@typescript-eslint/utils");
|
|
6
6
|
const isTypeImport_1 = require("./isTypeImport");
|
|
7
|
-
const
|
|
8
|
-
const referenceContainsTypeQuery_1 = require("./referenceContainsTypeQuery");
|
|
7
|
+
const isTypeOnlyReference_1 = require("./isTypeOnlyReference");
|
|
9
8
|
/**
|
|
10
9
|
* This class leverages an AST visitor to mark variables as used via the
|
|
11
10
|
* `eslintUsed` property.
|
|
@@ -289,6 +288,11 @@ const MERGEABLE_TYPES = new Set([
|
|
|
289
288
|
utils_1.AST_NODE_TYPES.TSModuleDeclaration,
|
|
290
289
|
utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration,
|
|
291
290
|
]);
|
|
291
|
+
function isMergedTypeDeclaration(variable, node) {
|
|
292
|
+
return ((node.type === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration ||
|
|
293
|
+
node.type === utils_1.AST_NODE_TYPES.TSInterfaceDeclaration) &&
|
|
294
|
+
(0, isTypeOnlyReference_1.isMergedTypeValueVariable)(variable));
|
|
295
|
+
}
|
|
292
296
|
/**
|
|
293
297
|
* Determine if the variable is directly exported
|
|
294
298
|
* @param variable the variable to check
|
|
@@ -305,7 +309,7 @@ function isMergeableExported(variable) {
|
|
|
305
309
|
if ((MERGEABLE_TYPES.has(def.node.type) &&
|
|
306
310
|
def.node.parent.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration) ||
|
|
307
311
|
def.node.parent.type === utils_1.AST_NODE_TYPES.ExportDefaultDeclaration) {
|
|
308
|
-
return
|
|
312
|
+
return !isMergedTypeDeclaration(variable, def.node);
|
|
309
313
|
}
|
|
310
314
|
}
|
|
311
315
|
return false;
|
|
@@ -324,7 +328,8 @@ function isExported(variable) {
|
|
|
324
328
|
else if (definition.type === utils_1.TSESLint.Scope.DefinitionType.Parameter) {
|
|
325
329
|
return false;
|
|
326
330
|
}
|
|
327
|
-
return node
|
|
331
|
+
return (!isMergedTypeDeclaration(variable, node) &&
|
|
332
|
+
node.parent.type.startsWith('Export'));
|
|
328
333
|
});
|
|
329
334
|
}
|
|
330
335
|
const LOGICAL_ASSIGNMENT_OPERATORS = new Set(['??=', '&&=', '||=']);
|
|
@@ -579,9 +584,7 @@ function isUsedVariable(variable) {
|
|
|
579
584
|
rhsNode = getRhsNode(ref, rhsNode);
|
|
580
585
|
return (ref.isRead() &&
|
|
581
586
|
!forItself &&
|
|
582
|
-
!(!isImportedAsType &&
|
|
583
|
-
((0, referenceContainsTypeQuery_1.referenceContainsTypeQuery)(ref.identifier) ||
|
|
584
|
-
(0, referenceContainsTypePredicate_1.referenceContainsTypePredicate)(ref.identifier))) &&
|
|
587
|
+
!(!isImportedAsType && (0, isTypeOnlyReference_1.isTypeOnlyReference)(variable, ref)) &&
|
|
585
588
|
!(isFunctionDefinition && isSelfReference(ref, functionNodes)) &&
|
|
586
589
|
!(isTypeDecl && isInsideOneOf(ref, typeDeclNodes)) &&
|
|
587
590
|
!(isModuleDecl && isSelfReference(ref, moduleDeclNodes)) &&
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ScopeVariable } from '@typescript-eslint/scope-manager';
|
|
2
|
+
import type { TSESLint } from '@typescript-eslint/utils';
|
|
3
|
+
export declare function isMergedTypeValueVariable(variable: ScopeVariable): boolean;
|
|
4
|
+
export declare function isTypeOnlyReference(variable: ScopeVariable, ref: TSESLint.Scope.Reference): boolean;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isMergedTypeValueVariable = isMergedTypeValueVariable;
|
|
4
|
+
exports.isTypeOnlyReference = isTypeOnlyReference;
|
|
5
|
+
const scope_manager_1 = require("@typescript-eslint/scope-manager");
|
|
6
|
+
const referenceContainsTypePredicate_1 = require("./referenceContainsTypePredicate");
|
|
7
|
+
const referenceContainsTypeQuery_1 = require("./referenceContainsTypeQuery");
|
|
8
|
+
function isMergedTypeValueVariable(variable) {
|
|
9
|
+
return ('isTypeVariable' in variable &&
|
|
10
|
+
'isValueVariable' in variable &&
|
|
11
|
+
variable.isTypeVariable &&
|
|
12
|
+
variable.isValueVariable);
|
|
13
|
+
}
|
|
14
|
+
function isTypeOnlyReference(variable, ref) {
|
|
15
|
+
if ((0, referenceContainsTypeQuery_1.referenceContainsTypeQuery)(ref.identifier) ||
|
|
16
|
+
(0, referenceContainsTypePredicate_1.referenceContainsTypePredicate)(ref.identifier)) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
return (variable.defs.some(def => def.type === scope_manager_1.DefinitionType.Variable) &&
|
|
20
|
+
!ref.isValueReference);
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=isTypeOnlyReference.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typescript-eslint/eslint-plugin",
|
|
3
|
-
"version": "8.71.1-alpha.
|
|
3
|
+
"version": "8.71.1-alpha.10",
|
|
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.71.1-alpha.
|
|
51
|
-
"@typescript-eslint/type-utils": "8.71.1-alpha.
|
|
52
|
-
"@typescript-eslint/utils": "8.71.1-alpha.
|
|
53
|
-
"@typescript-eslint/visitor-keys": "8.71.1-alpha.
|
|
50
|
+
"@typescript-eslint/scope-manager": "8.71.1-alpha.10",
|
|
51
|
+
"@typescript-eslint/type-utils": "8.71.1-alpha.10",
|
|
52
|
+
"@typescript-eslint/utils": "8.71.1-alpha.10",
|
|
53
|
+
"@typescript-eslint/visitor-keys": "8.71.1-alpha.10",
|
|
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.71.1-alpha.
|
|
64
|
-
"@typescript-eslint/rule-tester": "8.71.1-alpha.
|
|
63
|
+
"@typescript-eslint/rule-schema-to-typescript-types": "8.71.1-alpha.10",
|
|
64
|
+
"@typescript-eslint/rule-tester": "8.71.1-alpha.10",
|
|
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.71.1-alpha.
|
|
84
|
+
"@typescript-eslint/parser": "^8.71.1-alpha.10",
|
|
85
85
|
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
|
|
86
86
|
"typescript": ">=4.8.4 <6.1.0"
|
|
87
87
|
},
|