eslint-plugin-ts-data-forge 0.6.0 → 0.7.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/README.md +1 -0
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/dist/plugin.d.mts +2 -0
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin.mjs +45 -41
- package/dist/plugin.mjs.map +1 -1
- package/dist/rule-types.d.mts +15 -0
- package/dist/rule-types.d.mts.map +1 -1
- package/dist/rule-types.mjs +6 -2
- package/dist/rule-types.mjs.map +1 -1
- package/dist/rules/ast-utils.mjs +107 -50
- package/dist/rules/ast-utils.mjs.map +1 -1
- package/dist/rules/branded-number-types.mjs +41 -37
- package/dist/rules/branded-number-types.mjs.map +1 -1
- package/dist/rules/constants.mjs +2 -4
- package/dist/rules/constants.mjs.map +1 -1
- package/dist/rules/import-utils.mjs +80 -56
- package/dist/rules/import-utils.mjs.map +1 -1
- package/dist/rules/index.mjs +2 -2
- package/dist/rules/index.mjs.map +1 -1
- package/dist/rules/no-side-effect-import.mjs +46 -34
- package/dist/rules/no-side-effect-import.mjs.map +1 -1
- package/dist/rules/no-unnecessary-type-guard.mjs +376 -265
- package/dist/rules/no-unnecessary-type-guard.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-array.mjs +64 -52
- package/dist/rules/prefer-arr-is-array.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-bounded-length-array.mjs +91 -85
- package/dist/rules/prefer-arr-is-bounded-length-array.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-fixed-length-array.mjs +92 -83
- package/dist/rules/prefer-arr-is-fixed-length-array.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-max-length-array.mjs +92 -80
- package/dist/rules/prefer-arr-is-max-length-array.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-min-length-array.mjs +92 -80
- package/dist/rules/prefer-arr-is-min-length-array.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-non-empty.mjs +85 -77
- package/dist/rules/prefer-arr-is-non-empty.mjs.map +1 -1
- package/dist/rules/prefer-arr-scan.d.mts +96 -0
- package/dist/rules/prefer-arr-scan.d.mts.map +1 -0
- package/dist/rules/prefer-arr-scan.mjs +482 -0
- package/dist/rules/prefer-arr-scan.mjs.map +1 -0
- package/dist/rules/prefer-arr-sum.mjs +176 -132
- package/dist/rules/prefer-arr-sum.mjs.map +1 -1
- package/dist/rules/prefer-as-int.mjs +77 -71
- package/dist/rules/prefer-as-int.mjs.map +1 -1
- package/dist/rules/prefer-canonical-array-slicing.mjs +503 -343
- package/dist/rules/prefer-canonical-array-slicing.mjs.map +1 -1
- package/dist/rules/prefer-canonical-length-cast.mjs +217 -144
- package/dist/rules/prefer-canonical-length-cast.mjs.map +1 -1
- package/dist/rules/prefer-canonical-length-guard.mjs +160 -109
- package/dist/rules/prefer-canonical-length-guard.mjs.map +1 -1
- package/dist/rules/prefer-comparison-over-nullish-guard.mjs +89 -71
- package/dist/rules/prefer-comparison-over-nullish-guard.mjs.map +1 -1
- package/dist/rules/prefer-is-non-null-object.mjs +91 -72
- package/dist/rules/prefer-is-non-null-object.mjs.map +1 -1
- package/dist/rules/prefer-is-record-and-has-key.mjs +148 -105
- package/dist/rules/prefer-is-record-and-has-key.mjs.map +1 -1
- package/dist/rules/prefer-num-safe-parse-float.mjs +101 -77
- package/dist/rules/prefer-num-safe-parse-float.mjs.map +1 -1
- package/dist/rules/prefer-num-safe-parse-int.mjs +103 -81
- package/dist/rules/prefer-num-safe-parse-int.mjs.map +1 -1
- package/dist/rules/prefer-obj-over-entries-round-trip.mjs +299 -194
- package/dist/rules/prefer-obj-over-entries-round-trip.mjs.map +1 -1
- package/dist/rules/prefer-range-for-loop.mjs +121 -99
- package/dist/rules/prefer-range-for-loop.mjs.map +1 -1
- package/dist/rules/rules.d.mts +1 -0
- package/dist/rules/rules.d.mts.map +1 -1
- package/dist/rules/rules.mjs +19 -20
- package/dist/rules/rules.mjs.map +1 -1
- package/dist/types.mjs +2 -2
- package/dist/types.mjs.map +1 -1
- package/package.json +6 -7
- package/src/plugin.mts +1 -0
- package/src/rule-types.mts +16 -0
- package/src/rules/prefer-arr-scan.mts +693 -0
- package/src/rules/prefer-arr-scan.test.mts +295 -0
- package/src/rules/rules.mts +2 -0
|
@@ -1,83 +1,95 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import { isIntegerLiteralOrConstant, isLengthAccess, isPartOfBoundedLengthCheck, } from './ast-utils.mjs';
|
|
2
|
+
import { buildImportFixes, getNamedImports, getTsDataForgeImport, } from './import-utils.mjs';
|
|
3
|
+
export const preferArrIsMinLengthArray = {
|
|
4
|
+
meta: {
|
|
5
|
+
type: 'suggestion',
|
|
6
|
+
docs: {
|
|
7
|
+
description: 'Replace `xs.length >= n` with `Arr.isMinLengthArray(xs, n)` from ts-data-forge.',
|
|
8
|
+
},
|
|
9
|
+
fixable: 'code',
|
|
10
|
+
schema: [],
|
|
11
|
+
messages: {
|
|
12
|
+
useIsMinLengthArray: 'Replace `{{original}}` with `Arr.isMinLengthArray({{arrayName}}, {{length}})` from ts-data-forge.',
|
|
13
|
+
},
|
|
9
14
|
},
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const originalText = sourceCode.getText(node);
|
|
61
|
-
context.report({
|
|
62
|
-
node,
|
|
63
|
-
messageId: "useIsMinLengthArray",
|
|
64
|
-
data: {
|
|
65
|
-
original: originalText,
|
|
66
|
-
arrayName: arrayText,
|
|
67
|
-
length: lengthText
|
|
15
|
+
create: (context) => {
|
|
16
|
+
const sourceCode = context.sourceCode;
|
|
17
|
+
const program = sourceCode.ast;
|
|
18
|
+
const tsDataForgeImport = getTsDataForgeImport(program);
|
|
19
|
+
const services = context.sourceCode.parserServices;
|
|
20
|
+
const mut_nodesToFix = [];
|
|
21
|
+
return {
|
|
22
|
+
BinaryExpression: (node) => {
|
|
23
|
+
|
|
24
|
+
if (node.operator !== '>=' && node.operator !== '<=')
|
|
25
|
+
return;
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
if (isPartOfBoundedLengthCheck(node, sourceCode))
|
|
29
|
+
return;
|
|
30
|
+
|
|
31
|
+
const isLengthOnLeft = node.operator === '>=';
|
|
32
|
+
const lengthSide = node[isLengthOnLeft ? 'left' : 'right'];
|
|
33
|
+
const valueSide = node[isLengthOnLeft ? 'right' : 'left'];
|
|
34
|
+
|
|
35
|
+
if (!isLengthAccess(lengthSide))
|
|
36
|
+
return;
|
|
37
|
+
|
|
38
|
+
if (!isIntegerLiteralOrConstant(valueSide, sourceCode))
|
|
39
|
+
return;
|
|
40
|
+
|
|
41
|
+
const arrayExpression = lengthSide.object;
|
|
42
|
+
|
|
43
|
+
if (services?.program !== undefined && services.program !== null) {
|
|
44
|
+
const checker = services.program.getTypeChecker();
|
|
45
|
+
const tsNode = services.esTreeNodeToTSNodeMap?.get(arrayExpression);
|
|
46
|
+
if (tsNode !== undefined) {
|
|
47
|
+
const type = checker.getTypeAtLocation(tsNode);
|
|
48
|
+
|
|
49
|
+
const isArrayType = checker.isArrayType(type) || checker.isTupleType(type);
|
|
50
|
+
if (!isArrayType)
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
mut_nodesToFix.push({
|
|
61
|
+
node,
|
|
62
|
+
arrayExpression,
|
|
63
|
+
lengthExpression: valueSide,
|
|
64
|
+
});
|
|
68
65
|
},
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
66
|
+
'Program:exit': () => {
|
|
67
|
+
const namedImports = getNamedImports(tsDataForgeImport);
|
|
68
|
+
const hasArrImport = namedImports.includes('Arr');
|
|
69
|
+
for (const [index, { node, arrayExpression, lengthExpression },] of mut_nodesToFix.entries()) {
|
|
70
|
+
const arrayText = sourceCode.getText(arrayExpression);
|
|
71
|
+
const lengthText = sourceCode.getText(lengthExpression);
|
|
72
|
+
const originalText = sourceCode.getText(node);
|
|
73
|
+
context.report({
|
|
74
|
+
node,
|
|
75
|
+
messageId: 'useIsMinLengthArray',
|
|
76
|
+
data: {
|
|
77
|
+
original: originalText,
|
|
78
|
+
arrayName: arrayText,
|
|
79
|
+
length: lengthText,
|
|
80
|
+
},
|
|
81
|
+
fix: (fixer) => {
|
|
82
|
+
const replacement = `Arr.isMinLengthArray(${lengthText}, ${arrayText})`;
|
|
83
|
+
const importFixes = index === 0 && !hasArrImport
|
|
84
|
+
? buildImportFixes(fixer, program, tsDataForgeImport, ['Arr'])
|
|
85
|
+
: [];
|
|
86
|
+
return [...importFixes, fixer.replaceText(node, replacement)];
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
},
|
|
93
|
+
defaultOptions: [],
|
|
80
94
|
};
|
|
81
|
-
|
|
82
|
-
export { preferArrIsMinLengthArray };
|
|
83
|
-
//# sourceMappingURL=prefer-arr-is-min-length-array.mjs.map
|
|
95
|
+
//# sourceMappingURL=prefer-arr-is-min-length-array.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prefer-arr-is-min-length-array.mjs","sources":["../../src/rules/prefer-arr-is-min-length-array.mts"],"
|
|
1
|
+
{"version":3,"file":"prefer-arr-is-min-length-array.mjs","sourceRoot":"","sources":["../../src/rules/prefer-arr-is-min-length-array.mts"],"names":[],"mappings":"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"}
|
|
@@ -1,80 +1,88 @@
|
|
|
1
|
-
import { AST_NODE_TYPES } from '@typescript-eslint/utils';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import { AST_NODE_TYPES, } from '@typescript-eslint/utils';
|
|
2
|
+
import { buildImportFixes, getNamedImports, getTsDataForgeImport, } from './import-utils.mjs';
|
|
3
|
+
export const preferArrIsNonEmpty = {
|
|
4
|
+
meta: {
|
|
5
|
+
type: 'suggestion',
|
|
6
|
+
docs: {
|
|
7
|
+
description: 'Replace `xs.length > 0` with `Arr.isNonEmpty(xs)` from ts-data-forge.',
|
|
8
|
+
},
|
|
9
|
+
fixable: 'code',
|
|
10
|
+
schema: [],
|
|
11
|
+
messages: {
|
|
12
|
+
useIsNonEmpty: 'Replace `{{original}}` with `Arr.isNonEmpty({{arrayName}})` from ts-data-forge.',
|
|
13
|
+
},
|
|
9
14
|
},
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
15
|
+
create: (context) => {
|
|
16
|
+
const sourceCode = context.sourceCode;
|
|
17
|
+
const program = sourceCode.ast;
|
|
18
|
+
const tsDataForgeImport = getTsDataForgeImport(program);
|
|
19
|
+
const services = context.sourceCode.parserServices;
|
|
20
|
+
const mut_nodesToFix = [];
|
|
21
|
+
return {
|
|
22
|
+
BinaryExpression: (node) => {
|
|
23
|
+
|
|
24
|
+
if (node.operator !== '>' && node.operator !== '<')
|
|
25
|
+
return;
|
|
26
|
+
const isLengthOnLeft = node.operator === '>';
|
|
27
|
+
const lengthSide = node[isLengthOnLeft ? 'left' : 'right'];
|
|
28
|
+
const numberSide = node[isLengthOnLeft ? 'right' : 'left'];
|
|
29
|
+
|
|
30
|
+
if (lengthSide.type !== AST_NODE_TYPES.MemberExpression ||
|
|
31
|
+
lengthSide.property.type !== AST_NODE_TYPES.Identifier ||
|
|
32
|
+
lengthSide.property.name !== 'length' ||
|
|
33
|
+
numberSide.type !== AST_NODE_TYPES.Literal ||
|
|
34
|
+
numberSide.value !== 0) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const arrayExpression = lengthSide.object;
|
|
38
|
+
|
|
39
|
+
if (services?.program !== undefined && services.program !== null) {
|
|
40
|
+
const checker = services.program.getTypeChecker();
|
|
41
|
+
const tsNode = services.esTreeNodeToTSNodeMap?.get(arrayExpression);
|
|
42
|
+
if (tsNode !== undefined) {
|
|
43
|
+
const type = checker.getTypeAtLocation(tsNode);
|
|
44
|
+
|
|
45
|
+
const isArrayType = checker.isArrayType(type) || checker.isTupleType(type);
|
|
46
|
+
if (!isArrayType)
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
mut_nodesToFix.push({
|
|
57
|
+
node,
|
|
58
|
+
arrayExpression,
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
'Program:exit': () => {
|
|
62
|
+
const namedImports = getNamedImports(tsDataForgeImport);
|
|
63
|
+
const hasArrImport = namedImports.includes('Arr');
|
|
64
|
+
for (const [index, { node, arrayExpression },] of mut_nodesToFix.entries()) {
|
|
65
|
+
const arrayText = sourceCode.getText(arrayExpression);
|
|
66
|
+
const originalText = sourceCode.getText(node);
|
|
67
|
+
context.report({
|
|
68
|
+
node,
|
|
69
|
+
messageId: 'useIsNonEmpty',
|
|
70
|
+
data: {
|
|
71
|
+
original: originalText,
|
|
72
|
+
arrayName: arrayText,
|
|
73
|
+
},
|
|
74
|
+
fix: (fixer) => {
|
|
75
|
+
const replacement = `Arr.isNonEmpty(${arrayText})`;
|
|
76
|
+
const importFixes = index === 0 && !hasArrImport
|
|
77
|
+
? buildImportFixes(fixer, program, tsDataForgeImport, ['Arr'])
|
|
78
|
+
: [];
|
|
79
|
+
return [...importFixes, fixer.replaceText(node, replacement)];
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
}
|
|
65
83
|
},
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
return [...importFixes, fixer.replaceText(node, replacement)];
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
},
|
|
76
|
-
defaultOptions: []
|
|
84
|
+
};
|
|
85
|
+
},
|
|
86
|
+
defaultOptions: [],
|
|
77
87
|
};
|
|
78
|
-
|
|
79
|
-
export { preferArrIsNonEmpty };
|
|
80
|
-
//# sourceMappingURL=prefer-arr-is-non-empty.mjs.map
|
|
88
|
+
//# sourceMappingURL=prefer-arr-is-non-empty.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prefer-arr-is-non-empty.mjs","sources":["../../src/rules/prefer-arr-is-non-empty.mts"],"
|
|
1
|
+
{"version":3,"file":"prefer-arr-is-non-empty.mjs","sourceRoot":"","sources":["../../src/rules/prefer-arr-is-non-empty.mts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { type TSESLint } from '@typescript-eslint/utils';
|
|
2
|
+
type Options = readonly [];
|
|
3
|
+
type MessageIds = 'preferArrScanPrefix' | 'preferArrScanAccumulate';
|
|
4
|
+
/**
|
|
5
|
+
* Report an accumulated value that is rebuilt from scratch for every element,
|
|
6
|
+
* and point at `Arr.scan` from ts-data-forge.
|
|
7
|
+
*
|
|
8
|
+
* ## Why
|
|
9
|
+
*
|
|
10
|
+
* Two shapes compute what `Arr.scan` computes — a value carried from one
|
|
11
|
+
* element to the next — by re-deriving it from the whole prefix each time.
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* // (1) the prefix rebuilt per element: `slice` allocates, and the combining
|
|
15
|
+
* // function runs over the whole prefix again. O(n²).
|
|
16
|
+
* segments.map((_, index) =>
|
|
17
|
+
* path.resolve(root, ...segments.slice(0, index + 1)),
|
|
18
|
+
* );
|
|
19
|
+
*
|
|
20
|
+
* // one pass — and `scan` puts the initial value at the head of its result,
|
|
21
|
+
* // which here is exactly "the root, then each ancestor below it"
|
|
22
|
+
* Arr.scan(
|
|
23
|
+
* segments,
|
|
24
|
+
* (ancestor, segment) => path.resolve(ancestor, segment),
|
|
25
|
+
* root,
|
|
26
|
+
* );
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* ```ts
|
|
30
|
+
* // (2) the accumulator copied on every step, for the same O(n²)
|
|
31
|
+
* xs.reduce<readonly number[]>((acc, x) => [...acc, (acc.at(-1) ?? 0) + x], [0]);
|
|
32
|
+
*
|
|
33
|
+
* Arr.scan(xs, (total, x) => total + x, 0);
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* The complexity is the smaller half of it. What the rebuilt form loses is the
|
|
37
|
+
* name of what is being accumulated: (1) says "for each index, combine
|
|
38
|
+
* everything up to it", and a reader has to turn that back into "carry a value
|
|
39
|
+
* along" before the code says what it does.
|
|
40
|
+
*
|
|
41
|
+
* ## Why only one of the two is fixed
|
|
42
|
+
*
|
|
43
|
+
* **(1) cannot be fixed at all**, and the obstacle is not difficulty. Turning
|
|
44
|
+
* `f(seed, ...xs.slice(0, i + 1))` into a `scan` is only valid if
|
|
45
|
+
* `f(seed, a, b, c) === f(f(f(seed, a), b), c)` — that the variadic callee is a
|
|
46
|
+
* left fold. That is a semantic property of the callee, invisible in the AST
|
|
47
|
+
* and in the type: `path.resolve` and `Math.max` have it, `Array.of` and
|
|
48
|
+
* `console.log` do not. Nothing static licenses the rewrite, so this shape is
|
|
49
|
+
* reported and left to a reader. The direction is a second obstacle —
|
|
50
|
+
* `slice(0, i + 1)` runs forwards while `slice(0, xs.length - i)` runs
|
|
51
|
+
* backwards and needs the result reversed — and the length a third, `scan`
|
|
52
|
+
* putting the initial value at the head where `map` has n elements.
|
|
53
|
+
*
|
|
54
|
+
* **(2) is fixed**, because none of those three apply to it: the combining
|
|
55
|
+
* function is the callback body itself, there is no slice and so no direction
|
|
56
|
+
* to infer, and the lengths already agree — a `reduce` seeded with `[init]`
|
|
57
|
+
* yields n+1 elements and so does `Arr.scan(xs, f, init)`.
|
|
58
|
+
*
|
|
59
|
+
* The fix is withheld unless every one of these holds, since each is what makes
|
|
60
|
+
* the two forms equal rather than merely similar:
|
|
61
|
+
*
|
|
62
|
+
* - the initial value is a one-element array literal `[E]`, so `scan`'s initial
|
|
63
|
+
* value is `E`;
|
|
64
|
+
* - the body is exactly `[...acc, ELEMENT]` — one spread of the accumulator and
|
|
65
|
+
* one new element, since `scan` appends exactly one value per step;
|
|
66
|
+
* - every other mention of the accumulator in `ELEMENT` is `acc.at(-1)`, and
|
|
67
|
+
* there is at least one. `scan` hands its callback the previous *value*, not
|
|
68
|
+
* the prefix, so `acc.length`, `acc[0]` or `acc.filter(...)` cannot be
|
|
69
|
+
* expressed and block the fix. `at(-1)` is the only spelling substituted:
|
|
70
|
+
* `Arr.last` returns an `Optional`, not the element, so it is not a
|
|
71
|
+
* substitution at all;
|
|
72
|
+
* - the parameters are plain identifiers with no type annotations — an
|
|
73
|
+
* annotation on the accumulator describes the array, and would be wrong on
|
|
74
|
+
* `scan`'s value parameter — and there are at most three of them, `reduce`'s
|
|
75
|
+
* fourth parameter (the whole array) having no counterpart in `scan`.
|
|
76
|
+
*
|
|
77
|
+
* `acc.at(-1)` is typed `T | undefined` while `scan`'s accumulator is `S`, so a
|
|
78
|
+
* `?? fallback` guarding it becomes redundant after the fix. It is left in
|
|
79
|
+
* place rather than removed: it evaluates identically either way, and deciding
|
|
80
|
+
* that it is dead needs to know that `S` excludes `undefined`, which is type
|
|
81
|
+
* information this rule does not take.
|
|
82
|
+
*
|
|
83
|
+
* ## What is not reported
|
|
84
|
+
*
|
|
85
|
+
* A `map` callback that reads its element parameter. Rebuilding a prefix *and*
|
|
86
|
+
* using the current element is not an accumulation spelled out the long way:
|
|
87
|
+
* `scan` hands its callback the accumulated value and the element, never the
|
|
88
|
+
* prefix, so such a callback is not one it can express.
|
|
89
|
+
*
|
|
90
|
+
* The array has to be spelled the same way in the `map` and in the `slice`.
|
|
91
|
+
* Deciding that two differently written expressions denote one array needs type
|
|
92
|
+
* information and would still be a guess about aliasing.
|
|
93
|
+
*/
|
|
94
|
+
export declare const preferArrScan: TSESLint.RuleModule<MessageIds, Options>;
|
|
95
|
+
export {};
|
|
96
|
+
//# sourceMappingURL=prefer-arr-scan.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prefer-arr-scan.d.mts","sourceRoot":"","sources":["../../src/rules/prefer-arr-scan.mts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,QAAQ,EAEd,MAAM,0BAA0B,CAAC;AAUlC,KAAK,OAAO,GAAG,SAAS,EAAE,CAAC;AAE3B,KAAK,UAAU,GAAG,qBAAqB,GAAG,yBAAyB,CAAC;AA+CpE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyFG;AACH,eAAO,MAAM,aAAa,EAAE,QAAQ,CAAC,UAAU,CAAC,UAAU,EAAE,OAAO,CAwVzD,CAAC"}
|