eslint-plugin-ts-data-forge 0.8.1 → 0.8.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/ast-utils.d.mts.map +1 -1
- package/dist/rules/ast-utils.mjs +24 -24
- package/dist/rules/ast-utils.mjs.map +1 -1
- package/dist/rules/branded-number-types.mjs.map +1 -1
- package/dist/rules/import-utils.d.mts.map +1 -1
- package/dist/rules/import-utils.mjs +4 -2
- package/dist/rules/import-utils.mjs.map +1 -1
- package/dist/rules/no-unnecessary-type-guard.d.mts.map +1 -1
- package/dist/rules/no-unnecessary-type-guard.mjs +37 -23
- package/dist/rules/no-unnecessary-type-guard.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-array.d.mts.map +1 -1
- package/dist/rules/prefer-arr-is-array.mjs +14 -7
- package/dist/rules/prefer-arr-is-array.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-bounded-length-array.d.mts.map +1 -1
- package/dist/rules/prefer-arr-is-bounded-length-array.mjs +8 -4
- package/dist/rules/prefer-arr-is-bounded-length-array.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-fixed-length-array.d.mts.map +1 -1
- package/dist/rules/prefer-arr-is-fixed-length-array.mjs +8 -4
- package/dist/rules/prefer-arr-is-fixed-length-array.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-max-length-array.d.mts.map +1 -1
- package/dist/rules/prefer-arr-is-max-length-array.mjs +10 -5
- package/dist/rules/prefer-arr-is-max-length-array.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-min-length-array.d.mts.map +1 -1
- package/dist/rules/prefer-arr-is-min-length-array.mjs +10 -5
- package/dist/rules/prefer-arr-is-min-length-array.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-non-empty.d.mts.map +1 -1
- package/dist/rules/prefer-arr-is-non-empty.mjs +4 -2
- package/dist/rules/prefer-arr-is-non-empty.mjs.map +1 -1
- package/dist/rules/prefer-arr-scan.d.mts.map +1 -1
- package/dist/rules/prefer-arr-scan.mjs +35 -18
- package/dist/rules/prefer-arr-scan.mjs.map +1 -1
- package/dist/rules/prefer-arr-sum.d.mts.map +1 -1
- package/dist/rules/prefer-arr-sum.mjs +6 -3
- package/dist/rules/prefer-arr-sum.mjs.map +1 -1
- package/dist/rules/prefer-arr-uniq.d.mts.map +1 -1
- package/dist/rules/prefer-arr-uniq.mjs +30 -24
- package/dist/rules/prefer-arr-uniq.mjs.map +1 -1
- package/dist/rules/prefer-as-int.d.mts.map +1 -1
- package/dist/rules/prefer-as-int.mjs +11 -14
- package/dist/rules/prefer-as-int.mjs.map +1 -1
- package/dist/rules/prefer-canonical-array-slicing.d.mts.map +1 -1
- package/dist/rules/prefer-canonical-array-slicing.mjs +32 -16
- package/dist/rules/prefer-canonical-array-slicing.mjs.map +1 -1
- package/dist/rules/prefer-canonical-length-cast.d.mts.map +1 -1
- package/dist/rules/prefer-canonical-length-cast.mjs +11 -7
- package/dist/rules/prefer-canonical-length-cast.mjs.map +1 -1
- package/dist/rules/prefer-canonical-length-guard.d.mts.map +1 -1
- package/dist/rules/prefer-canonical-length-guard.mjs +8 -4
- package/dist/rules/prefer-canonical-length-guard.mjs.map +1 -1
- package/dist/rules/prefer-comparison-over-nullish-guard.d.mts.map +1 -1
- package/dist/rules/prefer-comparison-over-nullish-guard.mjs +6 -3
- package/dist/rules/prefer-comparison-over-nullish-guard.mjs.map +1 -1
- package/dist/rules/prefer-is-non-null-object.d.mts.map +1 -1
- package/dist/rules/prefer-is-non-null-object.mjs +29 -31
- package/dist/rules/prefer-is-non-null-object.mjs.map +1 -1
- package/dist/rules/prefer-is-record-and-has-key.d.mts.map +1 -1
- package/dist/rules/prefer-is-record-and-has-key.mjs +10 -5
- package/dist/rules/prefer-is-record-and-has-key.mjs.map +1 -1
- package/dist/rules/prefer-num-safe-parse-float.d.mts.map +1 -1
- package/dist/rules/prefer-num-safe-parse-float.mjs +12 -6
- package/dist/rules/prefer-num-safe-parse-float.mjs.map +1 -1
- package/dist/rules/prefer-num-safe-parse-int.d.mts.map +1 -1
- package/dist/rules/prefer-num-safe-parse-int.mjs +12 -6
- package/dist/rules/prefer-num-safe-parse-int.mjs.map +1 -1
- package/dist/rules/prefer-obj-over-entries-round-trip.d.mts.map +1 -1
- package/dist/rules/prefer-obj-over-entries-round-trip.mjs +22 -11
- package/dist/rules/prefer-obj-over-entries-round-trip.mjs.map +1 -1
- package/dist/rules/prefer-range-for-loop.d.mts.map +1 -1
- package/dist/rules/prefer-range-for-loop.mjs +2 -1
- package/dist/rules/prefer-range-for-loop.mjs.map +1 -1
- package/package.json +25 -23
- package/src/rules/ast-utils.mts +38 -26
- package/src/rules/branded-number-types.mts +1 -1
- package/src/rules/import-utils.mts +6 -2
- package/src/rules/no-unnecessary-type-guard.mts +56 -23
- package/src/rules/prefer-arr-is-array.mts +21 -7
- package/src/rules/prefer-arr-is-bounded-length-array.mts +12 -4
- package/src/rules/prefer-arr-is-fixed-length-array.mts +12 -4
- package/src/rules/prefer-arr-is-max-length-array.mts +15 -5
- package/src/rules/prefer-arr-is-min-length-array.mts +15 -5
- package/src/rules/prefer-arr-is-non-empty.mts +6 -2
- package/src/rules/prefer-arr-scan.mts +52 -18
- package/src/rules/prefer-arr-sum.mts +9 -3
- package/src/rules/prefer-arr-uniq.mts +42 -26
- package/src/rules/prefer-as-int.mts +17 -25
- package/src/rules/prefer-canonical-array-slicing.mts +48 -16
- package/src/rules/prefer-canonical-length-cast.mts +15 -7
- package/src/rules/prefer-canonical-length-guard.mts +12 -4
- package/src/rules/prefer-comparison-over-nullish-guard.mts +9 -3
- package/src/rules/prefer-is-non-null-object.mts +35 -31
- package/src/rules/prefer-is-record-and-has-key.mts +15 -5
- package/src/rules/prefer-num-safe-parse-float.mts +17 -6
- package/src/rules/prefer-num-safe-parse-int.mts +18 -6
- package/src/rules/prefer-obj-over-entries-round-trip.mts +33 -11
- package/src/rules/prefer-range-for-loop.mts +3 -1
|
@@ -79,18 +79,25 @@ export const preferArrUniq: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
79
79
|
): void => {
|
|
80
80
|
const arrayExpression = getSetSourceArray(setNode);
|
|
81
81
|
|
|
82
|
-
if (arrayExpression === undefined)
|
|
82
|
+
if (arrayExpression === undefined) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
83
85
|
|
|
84
|
-
if (!isGlobal(sourceCode, node, 'Set'))
|
|
86
|
+
if (!isGlobal(sourceCode, node, 'Set')) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
85
89
|
|
|
86
90
|
const arrayTsNode = getTsNode(arrayExpression);
|
|
87
91
|
|
|
88
|
-
if (arrayTsNode === undefined)
|
|
92
|
+
if (arrayTsNode === undefined) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
89
95
|
|
|
90
96
|
if (
|
|
91
97
|
!isPrimitiveArrayType(checker, checker.getTypeAtLocation(arrayTsNode))
|
|
92
|
-
)
|
|
98
|
+
) {
|
|
93
99
|
return;
|
|
100
|
+
}
|
|
94
101
|
|
|
95
102
|
mut_matches.push({
|
|
96
103
|
node,
|
|
@@ -117,17 +124,23 @@ export const preferArrUniq: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
117
124
|
return;
|
|
118
125
|
}
|
|
119
126
|
|
|
120
|
-
if (!isGlobal(sourceCode, node, 'Array'))
|
|
127
|
+
if (!isGlobal(sourceCode, node, 'Array')) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
121
130
|
|
|
122
131
|
check(node, node.arguments[0]);
|
|
123
132
|
},
|
|
124
133
|
ArrayExpression: (node) => {
|
|
125
134
|
// [...new Set(xs)]
|
|
126
|
-
if (!Arr.isFixedLengthTuple(1, node.elements))
|
|
135
|
+
if (!Arr.isFixedLengthTuple(1, node.elements)) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
127
138
|
|
|
128
139
|
const element = node.elements[0];
|
|
129
140
|
|
|
130
|
-
if (element?.type !== AST_NODE_TYPES.SpreadElement)
|
|
141
|
+
if (element?.type !== AST_NODE_TYPES.SpreadElement) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
131
144
|
|
|
132
145
|
check(node, element.argument);
|
|
133
146
|
},
|
|
@@ -246,11 +259,10 @@ const findVariable = (
|
|
|
246
259
|
): TSESLint.Scope.Variable | undefined => {
|
|
247
260
|
const variable = scope.set.get(name);
|
|
248
261
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
return findVariable(scope.upper, name);
|
|
262
|
+
return (
|
|
263
|
+
variable ??
|
|
264
|
+
(scope.upper === null ? undefined : findVariable(scope.upper, name))
|
|
265
|
+
);
|
|
254
266
|
};
|
|
255
267
|
|
|
256
268
|
/**
|
|
@@ -296,11 +308,9 @@ const isPrimitiveType = (
|
|
|
296
308
|
): boolean => {
|
|
297
309
|
const resolved = resolveConstraint(checker, type);
|
|
298
310
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
return (resolved.flags & primitiveTypeFlags) !== 0;
|
|
311
|
+
return resolved.isUnion()
|
|
312
|
+
? resolved.types.every((t) => isPrimitiveType(checker, t))
|
|
313
|
+
: (resolved.flags & primitiveTypeFlags) !== 0;
|
|
304
314
|
};
|
|
305
315
|
|
|
306
316
|
const primitiveTypeFlags =
|
|
@@ -338,7 +348,9 @@ const needsMutableResult = (
|
|
|
338
348
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
339
349
|
node: TSESTree.Node,
|
|
340
350
|
): boolean => {
|
|
341
|
-
if (isUsedAsMutable(checker, getTsNode, node))
|
|
351
|
+
if (isUsedAsMutable(checker, getTsNode, node)) {
|
|
352
|
+
return true;
|
|
353
|
+
}
|
|
342
354
|
|
|
343
355
|
const parent = node.parent;
|
|
344
356
|
|
|
@@ -401,9 +413,13 @@ const isUsedAsMutable = (
|
|
|
401
413
|
// whose declared type is a mutable array.
|
|
402
414
|
const tsNode = getTsNode(node);
|
|
403
415
|
|
|
404
|
-
if (tsNode === undefined)
|
|
416
|
+
if (tsNode === undefined) {
|
|
417
|
+
return false;
|
|
418
|
+
}
|
|
405
419
|
|
|
406
|
-
if (!ts.isExpression(tsNode))
|
|
420
|
+
if (!ts.isExpression(tsNode)) {
|
|
421
|
+
return false;
|
|
422
|
+
}
|
|
407
423
|
|
|
408
424
|
const expectedType =
|
|
409
425
|
getDeclaredParameterType(checker, tsNode) ??
|
|
@@ -436,7 +452,9 @@ const getDeclaredParameterType = (
|
|
|
436
452
|
|
|
437
453
|
const index = call.arguments?.indexOf(argument) ?? -1;
|
|
438
454
|
|
|
439
|
-
if (index === -1)
|
|
455
|
+
if (index === -1) {
|
|
456
|
+
return undefined;
|
|
457
|
+
}
|
|
440
458
|
|
|
441
459
|
const declaration = checker.getResolvedSignature(call)?.getDeclaration();
|
|
442
460
|
|
|
@@ -444,11 +462,9 @@ const getDeclaredParameterType = (
|
|
|
444
462
|
|
|
445
463
|
// A rest parameter's declared type is the array of arguments, not the type of
|
|
446
464
|
// one; the contextual type is the better answer there.
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
return checker.getTypeAtLocation(parameter);
|
|
465
|
+
return parameter === undefined || parameter.dotDotDotToken !== undefined
|
|
466
|
+
? undefined
|
|
467
|
+
: checker.getTypeAtLocation(parameter);
|
|
452
468
|
};
|
|
453
469
|
|
|
454
470
|
/**
|
|
@@ -47,7 +47,9 @@ export const preferAsInt: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
47
47
|
TSAsExpression: (node) => {
|
|
48
48
|
const typeInfo = getBrandedNumberTypeInfo(node.typeAnnotation);
|
|
49
49
|
|
|
50
|
-
if (typeInfo === undefined)
|
|
50
|
+
if (typeInfo === undefined) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
51
53
|
|
|
52
54
|
mut_nodesToFix.push({
|
|
53
55
|
node,
|
|
@@ -59,26 +61,13 @@ export const preferAsInt: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
59
61
|
const namedImports = getNamedImports(tsDataForgeImport);
|
|
60
62
|
|
|
61
63
|
// Group nodes by function name to handle imports efficiently
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
typeName: string;
|
|
67
|
-
functionName: string;
|
|
68
|
-
}>[]
|
|
69
|
-
>();
|
|
70
|
-
|
|
71
|
-
for (const nodeInfo of mut_nodesToFix) {
|
|
72
|
-
const mut_nodes =
|
|
73
|
-
mut_functionNameToNodes.get(nodeInfo.functionName) ?? [];
|
|
74
|
-
|
|
75
|
-
mut_nodes.push(nodeInfo);
|
|
76
|
-
|
|
77
|
-
mut_functionNameToNodes.set(nodeInfo.functionName, mut_nodes);
|
|
78
|
-
}
|
|
64
|
+
const functionNameToNodes = Map.groupBy(
|
|
65
|
+
mut_nodesToFix,
|
|
66
|
+
(nodeInfo) => nodeInfo.functionName,
|
|
67
|
+
);
|
|
79
68
|
|
|
80
69
|
// Process each group
|
|
81
|
-
for (const [functionName, nodes] of
|
|
70
|
+
for (const [functionName, nodes] of functionNameToNodes) {
|
|
82
71
|
const hasImport = namedImports.includes(functionName);
|
|
83
72
|
|
|
84
73
|
for (const [index, { node, typeName }] of nodes.entries()) {
|
|
@@ -114,18 +103,21 @@ export const preferAsInt: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
114
103
|
const getBrandedNumberTypeInfo = (
|
|
115
104
|
typeAnnotation: DeepReadonly<TSESTree.TypeNode>,
|
|
116
105
|
): Readonly<{ typeName: string; functionName: string }> | undefined => {
|
|
117
|
-
if (typeAnnotation.type !== AST_NODE_TYPES.TSTypeReference)
|
|
106
|
+
if (typeAnnotation.type !== AST_NODE_TYPES.TSTypeReference) {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
118
109
|
|
|
119
|
-
if (typeAnnotation.typeName.type !== AST_NODE_TYPES.Identifier)
|
|
110
|
+
if (typeAnnotation.typeName.type !== AST_NODE_TYPES.Identifier) {
|
|
120
111
|
return undefined;
|
|
112
|
+
}
|
|
121
113
|
|
|
122
114
|
const typeName = typeAnnotation.typeName.name;
|
|
123
115
|
|
|
124
|
-
if (!brandedNumberTypeNameToFunctionName.has(typeName))
|
|
116
|
+
if (!brandedNumberTypeNameToFunctionName.has(typeName)) {
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
125
119
|
|
|
126
120
|
const functionName = brandedNumberTypeNameToFunctionName.get(typeName);
|
|
127
121
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
return { typeName, functionName };
|
|
122
|
+
return functionName === undefined ? undefined : { typeName, functionName };
|
|
131
123
|
};
|
|
@@ -421,7 +421,9 @@ export const preferCanonicalArraySlicing: TSESLint.RuleModule<
|
|
|
421
421
|
return classifyLiteralComparison(op, n);
|
|
422
422
|
}
|
|
423
423
|
|
|
424
|
-
if (!isSimpleReference(receiver))
|
|
424
|
+
if (!isSimpleReference(receiver)) {
|
|
425
|
+
return undefined;
|
|
426
|
+
}
|
|
425
427
|
|
|
426
428
|
const m = matchLengthMinus(other, receiverText);
|
|
427
429
|
|
|
@@ -435,11 +437,15 @@ export const preferCanonicalArraySlicing: TSESLint.RuleModule<
|
|
|
435
437
|
if (args.length === 1) {
|
|
436
438
|
const [start] = args;
|
|
437
439
|
|
|
438
|
-
if (start === undefined)
|
|
440
|
+
if (start === undefined) {
|
|
441
|
+
return undefined;
|
|
442
|
+
}
|
|
439
443
|
|
|
440
444
|
const n = asNonNegativeIntegerLiteral(start);
|
|
441
445
|
|
|
442
|
-
if (n !== undefined)
|
|
446
|
+
if (n !== undefined) {
|
|
447
|
+
return normalizeCountedOp('skip', n);
|
|
448
|
+
}
|
|
443
449
|
|
|
444
450
|
const negated = asNegatedPositiveIntegerLiteral(start);
|
|
445
451
|
|
|
@@ -461,7 +467,9 @@ export const preferCanonicalArraySlicing: TSESLint.RuleModule<
|
|
|
461
467
|
|
|
462
468
|
const n = asNonNegativeIntegerLiteral(end);
|
|
463
469
|
|
|
464
|
-
if (n !== undefined)
|
|
470
|
+
if (n !== undefined) {
|
|
471
|
+
return normalizeCountedOp('take', n);
|
|
472
|
+
}
|
|
465
473
|
|
|
466
474
|
const negated = asNegatedPositiveIntegerLiteral(end);
|
|
467
475
|
|
|
@@ -469,7 +477,9 @@ export const preferCanonicalArraySlicing: TSESLint.RuleModule<
|
|
|
469
477
|
return normalizeCountedOp('skipLast', negated);
|
|
470
478
|
}
|
|
471
479
|
|
|
472
|
-
if (!isSimpleReference(receiver))
|
|
480
|
+
if (!isSimpleReference(receiver)) {
|
|
481
|
+
return undefined;
|
|
482
|
+
}
|
|
473
483
|
|
|
474
484
|
const m = matchLengthMinus(end, sourceCode.getText(receiver));
|
|
475
485
|
|
|
@@ -487,12 +497,16 @@ export const preferCanonicalArraySlicing: TSESLint.RuleModule<
|
|
|
487
497
|
if (args.length === 1) {
|
|
488
498
|
const [start] = args;
|
|
489
499
|
|
|
490
|
-
if (start === undefined)
|
|
500
|
+
if (start === undefined) {
|
|
501
|
+
return undefined;
|
|
502
|
+
}
|
|
491
503
|
|
|
492
504
|
const n = asNonNegativeIntegerLiteral(start);
|
|
493
505
|
|
|
494
506
|
// `xs.toSpliced(N)` removes everything from index N on → take N.
|
|
495
|
-
if (n !== undefined)
|
|
507
|
+
if (n !== undefined) {
|
|
508
|
+
return normalizeCountedOp('take', n);
|
|
509
|
+
}
|
|
496
510
|
|
|
497
511
|
const negated = asNegatedPositiveIntegerLiteral(start);
|
|
498
512
|
|
|
@@ -516,12 +530,16 @@ export const preferCanonicalArraySlicing: TSESLint.RuleModule<
|
|
|
516
530
|
// `xs.toSpliced(0, N)` removes the first N elements → skip N.
|
|
517
531
|
const n = asNonNegativeIntegerLiteral(deleteCount);
|
|
518
532
|
|
|
519
|
-
if (n !== undefined)
|
|
533
|
+
if (n !== undefined) {
|
|
534
|
+
return normalizeCountedOp('skip', n);
|
|
535
|
+
}
|
|
520
536
|
|
|
521
537
|
// `xs.toSpliced(0, xs.length - N)` keeps the last N → takeLast N.
|
|
522
538
|
// (Note: `xs.toSpliced(0, -N)` clamps the delete count to 0 and is a
|
|
523
539
|
// plain copy, so it is deliberately NOT treated as takeLast.)
|
|
524
|
-
if (!isSimpleReference(receiver))
|
|
540
|
+
if (!isSimpleReference(receiver)) {
|
|
541
|
+
return undefined;
|
|
542
|
+
}
|
|
525
543
|
|
|
526
544
|
const m = matchLengthMinus(deleteCount, receiverText);
|
|
527
545
|
|
|
@@ -649,7 +667,9 @@ export const preferCanonicalArraySlicing: TSESLint.RuleModule<
|
|
|
649
667
|
|
|
650
668
|
// The receiver must be an array or tuple (not a string / typed array
|
|
651
669
|
// / other object that happens to share the method name).
|
|
652
|
-
if (!isArrayOrTupleType(getTypeOf(receiver)))
|
|
670
|
+
if (!isArrayOrTupleType(getTypeOf(receiver))) {
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
653
673
|
|
|
654
674
|
const receiverText = sourceCode.getText(receiver);
|
|
655
675
|
|
|
@@ -657,7 +677,9 @@ export const preferCanonicalArraySlicing: TSESLint.RuleModule<
|
|
|
657
677
|
case 'slice': {
|
|
658
678
|
const op = classifySliceArgs(node.arguments, receiver);
|
|
659
679
|
|
|
660
|
-
if (op !== undefined)
|
|
680
|
+
if (op !== undefined) {
|
|
681
|
+
report(node, op, toArgText(receiver));
|
|
682
|
+
}
|
|
661
683
|
|
|
662
684
|
return;
|
|
663
685
|
}
|
|
@@ -669,7 +691,9 @@ export const preferCanonicalArraySlicing: TSESLint.RuleModule<
|
|
|
669
691
|
receiverText,
|
|
670
692
|
);
|
|
671
693
|
|
|
672
|
-
if (op !== undefined)
|
|
694
|
+
if (op !== undefined) {
|
|
695
|
+
report(node, op, toArgText(receiver));
|
|
696
|
+
}
|
|
673
697
|
|
|
674
698
|
return;
|
|
675
699
|
}
|
|
@@ -683,7 +707,9 @@ export const preferCanonicalArraySlicing: TSESLint.RuleModule<
|
|
|
683
707
|
|
|
684
708
|
const op = classifyFilterCallback(callback, receiver, receiverText);
|
|
685
709
|
|
|
686
|
-
if (op !== undefined)
|
|
710
|
+
if (op !== undefined) {
|
|
711
|
+
report(node, op, toArgText(receiver));
|
|
712
|
+
}
|
|
687
713
|
|
|
688
714
|
return;
|
|
689
715
|
}
|
|
@@ -735,7 +761,9 @@ export const preferCanonicalArraySlicing: TSESLint.RuleModule<
|
|
|
735
761
|
ArrayExpression: (node) => {
|
|
736
762
|
// `[item, ...xs]` → `Arr.toUnshifted(xs, item)`,
|
|
737
763
|
// `[...xs, item]` → `Arr.toPushed(xs, item)`.
|
|
738
|
-
if (node.elements.length !== 2)
|
|
764
|
+
if (node.elements.length !== 2) {
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
739
767
|
|
|
740
768
|
const [first, second] = node.elements;
|
|
741
769
|
|
|
@@ -760,11 +788,15 @@ export const preferCanonicalArraySlicing: TSESLint.RuleModule<
|
|
|
760
788
|
// `spread`, `item`, and `fn` are set together (all from the same
|
|
761
789
|
// tuple), so checking `spread` also guarantees the others are
|
|
762
790
|
// defined.
|
|
763
|
-
if (spread === undefined)
|
|
791
|
+
if (spread === undefined) {
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
764
794
|
|
|
765
795
|
// Spreading a non-array iterable (`[v, ...someSet]`) is a genuine
|
|
766
796
|
// conversion, not an element addition to an array.
|
|
767
|
-
if (!isArrayOrTupleType(getTypeOf(spread.argument)))
|
|
797
|
+
if (!isArrayOrTupleType(getTypeOf(spread.argument))) {
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
768
800
|
|
|
769
801
|
report(
|
|
770
802
|
node,
|
|
@@ -145,20 +145,26 @@ export const preferCanonicalLengthCast: TSESLint.RuleModule<
|
|
|
145
145
|
'Arr',
|
|
146
146
|
);
|
|
147
147
|
|
|
148
|
-
if (arrLocalName === undefined)
|
|
148
|
+
if (arrLocalName === undefined) {
|
|
149
|
+
return {};
|
|
150
|
+
}
|
|
149
151
|
|
|
150
152
|
return {
|
|
151
153
|
CallExpression: (node) => {
|
|
152
154
|
const castName = getCastName(node, arrLocalName);
|
|
153
155
|
|
|
154
|
-
if (castName === undefined)
|
|
156
|
+
if (castName === undefined) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
155
159
|
|
|
156
160
|
const rewrite = CAST_REWRITES.find(
|
|
157
161
|
(candidate) =>
|
|
158
162
|
candidate.cast === castName && matchesBounds(node, candidate),
|
|
159
163
|
);
|
|
160
164
|
|
|
161
|
-
if (rewrite === undefined)
|
|
165
|
+
if (rewrite === undefined) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
162
168
|
|
|
163
169
|
const boundsText = node.arguments
|
|
164
170
|
.slice(0, rewrite.bounds.length)
|
|
@@ -238,7 +244,9 @@ const matchesBounds = (
|
|
|
238
244
|
return rewrite.bounds.every((matcher, index) => {
|
|
239
245
|
const value = bounds[index];
|
|
240
246
|
|
|
241
|
-
if (value === undefined)
|
|
247
|
+
if (value === undefined) {
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
242
250
|
|
|
243
251
|
switch (matcher) {
|
|
244
252
|
case 'anyLength':
|
|
@@ -277,9 +285,9 @@ const dropBoundsFix = (
|
|
|
277
285
|
|
|
278
286
|
const next = node.arguments[index + 1];
|
|
279
287
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
288
|
+
return dropped === undefined ||
|
|
289
|
+
next === undefined ||
|
|
290
|
+
rewrite.keep.includes(index)
|
|
283
291
|
? []
|
|
284
292
|
: [fixer.removeRange([dropped.range[0], next.range[0]])];
|
|
285
293
|
});
|
|
@@ -124,7 +124,9 @@ export const preferCanonicalLengthGuard: TSESLint.RuleModule<
|
|
|
124
124
|
rule.create(context),
|
|
125
125
|
);
|
|
126
126
|
|
|
127
|
-
if (arrLocalName === undefined)
|
|
127
|
+
if (arrLocalName === undefined) {
|
|
128
|
+
return mergeVisitors(comparisonVisitors);
|
|
129
|
+
}
|
|
128
130
|
|
|
129
131
|
return mergeVisitors([
|
|
130
132
|
...comparisonVisitors,
|
|
@@ -132,7 +134,9 @@ export const preferCanonicalLengthGuard: TSESLint.RuleModule<
|
|
|
132
134
|
CallExpression: (node) => {
|
|
133
135
|
const guardName = getGuardName(node, arrLocalName);
|
|
134
136
|
|
|
135
|
-
if (guardName === undefined)
|
|
137
|
+
if (guardName === undefined) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
136
140
|
|
|
137
141
|
const rewrite = GUARD_REWRITES.find(
|
|
138
142
|
(entry) => entry.guard === guardName,
|
|
@@ -145,7 +149,9 @@ export const preferCanonicalLengthGuard: TSESLint.RuleModule<
|
|
|
145
149
|
// The array is the last argument; the bounds precede it.
|
|
146
150
|
const array = node.arguments[rewrite.bounds.length];
|
|
147
151
|
|
|
148
|
-
if (array === undefined)
|
|
152
|
+
if (array === undefined) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
149
155
|
|
|
150
156
|
const arrayText = sourceCode.getText(array);
|
|
151
157
|
|
|
@@ -185,7 +191,9 @@ const mergeVisitors = (
|
|
|
185
191
|
|
|
186
192
|
for (const visitor of visitors) {
|
|
187
193
|
for (const [selector, handler] of Object.entries(visitor)) {
|
|
188
|
-
if (typeof handler !== 'function')
|
|
194
|
+
if (typeof handler !== 'function') {
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
189
197
|
|
|
190
198
|
const mut_handlers = mut_merged[selector] ?? [];
|
|
191
199
|
|
|
@@ -97,7 +97,9 @@ export const preferComparisonOverNullishGuard: TSESLint.RuleModule<
|
|
|
97
97
|
|
|
98
98
|
return {
|
|
99
99
|
CallExpression: (node) => {
|
|
100
|
-
if (node.arguments.length !== 1)
|
|
100
|
+
if (node.arguments.length !== 1) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
101
103
|
|
|
102
104
|
const argument = node.arguments[0];
|
|
103
105
|
|
|
@@ -110,11 +112,15 @@ export const preferComparisonOverNullishGuard: TSESLint.RuleModule<
|
|
|
110
112
|
|
|
111
113
|
const resolved = resolveGuard(node.callee);
|
|
112
114
|
|
|
113
|
-
if (resolved === undefined)
|
|
115
|
+
if (resolved === undefined) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
114
118
|
|
|
115
119
|
const spec = COMPARISON_GUARDS[resolved.canonicalName];
|
|
116
120
|
|
|
117
|
-
if (spec === undefined)
|
|
121
|
+
if (spec === undefined) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
118
124
|
|
|
119
125
|
const argRaw = sourceCode.getText(argument);
|
|
120
126
|
|
|
@@ -45,7 +45,9 @@ export const preferIsNonNullObject: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
45
45
|
LogicalExpression: (node) => {
|
|
46
46
|
const identifierName = getNonNullObjectIdentifierName(node);
|
|
47
47
|
|
|
48
|
-
if (identifierName === undefined)
|
|
48
|
+
if (identifierName === undefined) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
49
51
|
|
|
50
52
|
mut_nodesToFix.push({ node, identifierName });
|
|
51
53
|
},
|
|
@@ -89,57 +91,59 @@ export const preferIsNonNullObject: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
89
91
|
const getNonNullObjectIdentifierName = (
|
|
90
92
|
node: DeepReadonly<TSESTree.LogicalExpression>,
|
|
91
93
|
): string | undefined => {
|
|
92
|
-
if (node.operator !== '&&')
|
|
94
|
+
if (node.operator !== '&&') {
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
93
97
|
|
|
94
98
|
const leftIdentifierName = getTypeofObjectIdentifierName(node.left);
|
|
95
99
|
|
|
96
100
|
const rightIdentifierName = getNonNullCheckIdentifierName(node.right);
|
|
97
101
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
return leftIdentifierName;
|
|
102
|
+
return leftIdentifierName === undefined ||
|
|
103
|
+
rightIdentifierName === undefined ||
|
|
104
|
+
leftIdentifierName !== rightIdentifierName
|
|
105
|
+
? undefined
|
|
106
|
+
: leftIdentifierName;
|
|
105
107
|
};
|
|
106
108
|
|
|
107
109
|
const getTypeofObjectIdentifierName = (
|
|
108
110
|
node: DeepReadonly<TSESTree.Expression>,
|
|
109
111
|
): string | undefined => {
|
|
110
|
-
if (node.type !== AST_NODE_TYPES.BinaryExpression)
|
|
112
|
+
if (node.type !== AST_NODE_TYPES.BinaryExpression) {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
111
115
|
|
|
112
|
-
if (node.operator !== '===')
|
|
116
|
+
if (node.operator !== '===') {
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
113
119
|
|
|
114
120
|
const { left, right } = node;
|
|
115
121
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (left.argument.type !== AST_NODE_TYPES.Identifier) return undefined;
|
|
125
|
-
|
|
126
|
-
return left.argument.name;
|
|
122
|
+
return left.type !== AST_NODE_TYPES.UnaryExpression ||
|
|
123
|
+
left.operator !== 'typeof' ||
|
|
124
|
+
right.type !== AST_NODE_TYPES.Literal ||
|
|
125
|
+
right.value !== 'object' ||
|
|
126
|
+
left.argument.type !== AST_NODE_TYPES.Identifier
|
|
127
|
+
? undefined
|
|
128
|
+
: left.argument.name;
|
|
127
129
|
};
|
|
128
130
|
|
|
129
131
|
const getNonNullCheckIdentifierName = (
|
|
130
132
|
node: DeepReadonly<TSESTree.Expression>,
|
|
131
133
|
): string | undefined => {
|
|
132
|
-
if (node.type !== AST_NODE_TYPES.BinaryExpression)
|
|
134
|
+
if (node.type !== AST_NODE_TYPES.BinaryExpression) {
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
133
137
|
|
|
134
|
-
if (node.operator !== '!==')
|
|
138
|
+
if (node.operator !== '!==') {
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
135
141
|
|
|
136
142
|
const { left, right } = node;
|
|
137
143
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
return left.name;
|
|
144
|
+
return left.type !== AST_NODE_TYPES.Identifier ||
|
|
145
|
+
right.type !== AST_NODE_TYPES.Literal ||
|
|
146
|
+
right.value !== null
|
|
147
|
+
? undefined
|
|
148
|
+
: left.name;
|
|
145
149
|
};
|
|
@@ -54,7 +54,9 @@ export const preferIsRecordAndHasKey: TSESLint.RuleModule<MessageIds, Options> =
|
|
|
54
54
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
55
55
|
expression: TSESTree.Expression,
|
|
56
56
|
): boolean => {
|
|
57
|
-
if (checker === undefined)
|
|
57
|
+
if (checker === undefined) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
58
60
|
|
|
59
61
|
const tsNode = services?.esTreeNodeToTSNodeMap?.get(expression);
|
|
60
62
|
|
|
@@ -84,13 +86,17 @@ export const preferIsRecordAndHasKey: TSESLint.RuleModule<MessageIds, Options> =
|
|
|
84
86
|
}
|
|
85
87
|
|
|
86
88
|
// Check arguments: Object.hasOwn(obj, key)
|
|
87
|
-
if (node.arguments.length !== 2)
|
|
89
|
+
if (node.arguments.length !== 2) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
88
92
|
|
|
89
93
|
const objArg = node.arguments[0];
|
|
90
94
|
|
|
91
95
|
const keyArg = node.arguments[1];
|
|
92
96
|
|
|
93
|
-
if (objArg === undefined || keyArg === undefined)
|
|
97
|
+
if (objArg === undefined || keyArg === undefined) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
94
100
|
|
|
95
101
|
// Type guard: ensure arguments are Expressions (not SpreadElement)
|
|
96
102
|
if (
|
|
@@ -109,14 +115,18 @@ export const preferIsRecordAndHasKey: TSESLint.RuleModule<MessageIds, Options> =
|
|
|
109
115
|
|
|
110
116
|
// Handle key in obj
|
|
111
117
|
BinaryExpression: (node) => {
|
|
112
|
-
if (node.operator !== 'in')
|
|
118
|
+
if (node.operator !== 'in') {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
113
121
|
|
|
114
122
|
const keyExpression = node.left;
|
|
115
123
|
|
|
116
124
|
const objExpression = node.right;
|
|
117
125
|
|
|
118
126
|
// Type guard: ensure keyExpression is Expression (not PrivateIdentifier)
|
|
119
|
-
if (keyExpression.type === AST_NODE_TYPES.PrivateIdentifier)
|
|
127
|
+
if (keyExpression.type === AST_NODE_TYPES.PrivateIdentifier) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
120
130
|
|
|
121
131
|
mut_nodesToFix.push({
|
|
122
132
|
node,
|