eslint-plugin-ts-data-forge 0.10.6 → 0.10.7
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 +3 -3
- package/dist/plugin.d.mts +2 -2
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin.mjs +1 -0
- package/dist/plugin.mjs.map +1 -1
- package/dist/rule-types.d.mts +5 -5
- package/dist/rules/ast-utils.d.mts +3 -4
- package/dist/rules/ast-utils.d.mts.map +1 -1
- package/dist/rules/ast-utils.mjs +0 -1
- package/dist/rules/ast-utils.mjs.map +1 -1
- package/dist/rules/import-binding.d.mts +6 -6
- package/dist/rules/no-unnecessary-type-guard.mjs.map +1 -1
- package/dist/rules/prefer-arr-generate.d.mts +2 -2
- package/dist/rules/prefer-arr-generate.d.mts.map +1 -1
- package/dist/rules/prefer-arr-generate.mjs +17 -5
- package/dist/rules/prefer-arr-generate.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-fixed-length-array.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-max-length-array.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-min-length-array.mjs.map +1 -1
- package/dist/rules/prefer-arr-scan.mjs.map +1 -1
- package/dist/rules/prefer-arr-sum.mjs.map +1 -1
- package/dist/rules/prefer-arr-uniq.mjs.map +1 -1
- package/dist/rules/prefer-canonical-array-slicing.d.mts.map +1 -1
- package/dist/rules/prefer-canonical-array-slicing.mjs +0 -1
- package/dist/rules/prefer-canonical-array-slicing.mjs.map +1 -1
- package/dist/rules/prefer-canonical-length-cast.mjs.map +1 -1
- package/dist/rules/prefer-num-safe-parse-float.d.mts +1 -1
- package/dist/rules/prefer-num-safe-parse-float.d.mts.map +1 -1
- package/dist/rules/prefer-num-safe-parse-int.d.mts +1 -1
- package/dist/rules/prefer-num-safe-parse-int.d.mts.map +1 -1
- package/dist/rules/prefer-pipe-map-nullable.d.mts +2 -2
- package/dist/rules/prefer-pipe-map-nullable.mjs.map +1 -1
- package/dist/rules/prefer-range-for-loop.mjs.map +1 -1
- package/dist/rules/rules.d.mts +2 -2
- package/package.json +6 -6
- package/src/plugin.mts +4 -3
- package/src/rule-types.mts +5 -5
- package/src/rules/ast-utils.mts +3 -4
- package/src/rules/import-binding.mts +6 -6
- package/src/rules/no-unnecessary-type-guard.mts +4 -4
- package/src/rules/prefer-arr-generate.mts +45 -27
- package/src/rules/prefer-arr-generate.test.mts +229 -0
- package/src/rules/prefer-arr-is-fixed-length-array.mts +2 -2
- package/src/rules/prefer-arr-is-max-length-array.mts +2 -2
- package/src/rules/prefer-arr-is-min-length-array.mts +2 -2
- package/src/rules/prefer-arr-scan.mts +8 -8
- package/src/rules/prefer-arr-sum.mts +3 -3
- package/src/rules/prefer-arr-uniq.mts +2 -2
- package/src/rules/prefer-canonical-array-slicing.mts +17 -18
- package/src/rules/prefer-canonical-length-cast.mts +11 -11
- package/src/rules/prefer-canonical-length-guard.mts +2 -2
- package/src/rules/prefer-num-safe-parse-float.test.mts +255 -94
- package/src/rules/prefer-num-safe-parse-int.test.mts +212 -76
- package/src/rules/prefer-pipe-map-nullable.mts +11 -11
- package/src/rules/prefer-range-for-loop.mts +6 -6
- package/src/rules/prefer-range-for-loop.test.mts +2 -2
|
@@ -884,6 +884,235 @@ describe('prefer-arr-generate', () => {
|
|
|
884
884
|
{ messageId: 'useArrGenerate' },
|
|
885
885
|
],
|
|
886
886
|
},
|
|
887
|
+
{
|
|
888
|
+
name: 'only suggests when a comment between the receiver and `.flatMap` would be lost',
|
|
889
|
+
code: dedent`
|
|
890
|
+
declare const xs: readonly number[];
|
|
891
|
+
const ys = xs
|
|
892
|
+
// keep only the positives
|
|
893
|
+
.flatMap((x) => (x > 0 ? [x] : []));
|
|
894
|
+
`,
|
|
895
|
+
output: null,
|
|
896
|
+
errors: [
|
|
897
|
+
{
|
|
898
|
+
messageId: 'useArrGenerate',
|
|
899
|
+
suggestions: [
|
|
900
|
+
{
|
|
901
|
+
messageId: 'suggestArrGenerate',
|
|
902
|
+
output: dedent`
|
|
903
|
+
import { Arr } from 'ts-data-forge';
|
|
904
|
+
declare const xs: readonly number[];
|
|
905
|
+
const ys = Arr.generate(function* () {
|
|
906
|
+
for (const x of xs) {
|
|
907
|
+
if (x > 0) {
|
|
908
|
+
yield x;
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
});
|
|
912
|
+
`,
|
|
913
|
+
},
|
|
914
|
+
],
|
|
915
|
+
},
|
|
916
|
+
],
|
|
917
|
+
},
|
|
918
|
+
{
|
|
919
|
+
name: 'only suggests when a comment after `flatMap(` would be lost',
|
|
920
|
+
code: dedent`
|
|
921
|
+
declare const xs: readonly number[];
|
|
922
|
+
const ys = xs.flatMap(
|
|
923
|
+
// keep only the positives
|
|
924
|
+
(x) => (x > 0 ? [x] : []),
|
|
925
|
+
);
|
|
926
|
+
`,
|
|
927
|
+
output: null,
|
|
928
|
+
errors: [
|
|
929
|
+
{
|
|
930
|
+
messageId: 'useArrGenerate',
|
|
931
|
+
suggestions: [
|
|
932
|
+
{
|
|
933
|
+
messageId: 'suggestArrGenerate',
|
|
934
|
+
output: dedent`
|
|
935
|
+
import { Arr } from 'ts-data-forge';
|
|
936
|
+
declare const xs: readonly number[];
|
|
937
|
+
const ys = Arr.generate(function* () {
|
|
938
|
+
for (const x of xs) {
|
|
939
|
+
if (x > 0) {
|
|
940
|
+
yield x;
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
});
|
|
944
|
+
`,
|
|
945
|
+
},
|
|
946
|
+
],
|
|
947
|
+
},
|
|
948
|
+
],
|
|
949
|
+
},
|
|
950
|
+
{
|
|
951
|
+
name: 'only suggests when a comment after the callback, before `)` would be lost',
|
|
952
|
+
code: dedent`
|
|
953
|
+
declare const xs: readonly number[];
|
|
954
|
+
const ys = xs.flatMap(
|
|
955
|
+
(x) => (x > 0 ? [x] : []),
|
|
956
|
+
// keep only the positives
|
|
957
|
+
);
|
|
958
|
+
`,
|
|
959
|
+
output: null,
|
|
960
|
+
errors: [
|
|
961
|
+
{
|
|
962
|
+
messageId: 'useArrGenerate',
|
|
963
|
+
suggestions: [
|
|
964
|
+
{
|
|
965
|
+
messageId: 'suggestArrGenerate',
|
|
966
|
+
output: dedent`
|
|
967
|
+
import { Arr } from 'ts-data-forge';
|
|
968
|
+
declare const xs: readonly number[];
|
|
969
|
+
const ys = Arr.generate(function* () {
|
|
970
|
+
for (const x of xs) {
|
|
971
|
+
if (x > 0) {
|
|
972
|
+
yield x;
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
});
|
|
976
|
+
`,
|
|
977
|
+
},
|
|
978
|
+
],
|
|
979
|
+
},
|
|
980
|
+
],
|
|
981
|
+
},
|
|
982
|
+
{
|
|
983
|
+
name: 'keeps the fix automatic when the comment is inside the carried type argument',
|
|
984
|
+
code: dedent`
|
|
985
|
+
declare const xs: readonly number[];
|
|
986
|
+
const ys = xs.flatMap</* positives */ number>((x) => (x > 0 ? [x] : []));
|
|
987
|
+
`,
|
|
988
|
+
output: dedent`
|
|
989
|
+
import { Arr } from 'ts-data-forge';
|
|
990
|
+
declare const xs: readonly number[];
|
|
991
|
+
const ys = Arr.generate</* positives */ number>(function* () {
|
|
992
|
+
for (const x of xs) {
|
|
993
|
+
if (x > 0) {
|
|
994
|
+
yield x;
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
});
|
|
998
|
+
`,
|
|
999
|
+
errors: [{ messageId: 'useArrGenerate' }],
|
|
1000
|
+
},
|
|
1001
|
+
{
|
|
1002
|
+
name: 'keeps the fix automatic when the comment is inside the receiver or the parameter',
|
|
1003
|
+
code: dedent`
|
|
1004
|
+
declare const f: (a: readonly { a: number }[]) => readonly { a: number }[];
|
|
1005
|
+
declare const xs: readonly { a: number }[];
|
|
1006
|
+
const ys = f(/* all */ xs).flatMap(({ a /* the value */ }) => (a > 0 ? [a] : []));
|
|
1007
|
+
`,
|
|
1008
|
+
output: dedent`
|
|
1009
|
+
import { Arr } from 'ts-data-forge';
|
|
1010
|
+
declare const f: (a: readonly { a: number }[]) => readonly { a: number }[];
|
|
1011
|
+
declare const xs: readonly { a: number }[];
|
|
1012
|
+
const ys = Arr.generate(function* () {
|
|
1013
|
+
for (const { a /* the value */ } of f(/* all */ xs)) {
|
|
1014
|
+
if (a > 0) {
|
|
1015
|
+
yield a;
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
});
|
|
1019
|
+
`,
|
|
1020
|
+
errors: [{ messageId: 'useArrGenerate' }],
|
|
1021
|
+
},
|
|
1022
|
+
{
|
|
1023
|
+
name: 'only suggests when a comment in a negated test would be lost',
|
|
1024
|
+
code: dedent`
|
|
1025
|
+
declare const xs: readonly number[];
|
|
1026
|
+
const ys = xs.flatMap((x) => (x > 0 && /* small */ x < 9 ? [] : [x]));
|
|
1027
|
+
`,
|
|
1028
|
+
output: null,
|
|
1029
|
+
errors: [
|
|
1030
|
+
{
|
|
1031
|
+
messageId: 'useArrGenerate',
|
|
1032
|
+
suggestions: [
|
|
1033
|
+
{
|
|
1034
|
+
messageId: 'suggestArrGenerate',
|
|
1035
|
+
output: dedent`
|
|
1036
|
+
import { Arr } from 'ts-data-forge';
|
|
1037
|
+
declare const xs: readonly number[];
|
|
1038
|
+
const ys = Arr.generate(function* () {
|
|
1039
|
+
for (const x of xs) {
|
|
1040
|
+
if (!(x > 0) || !(x < 9)) {
|
|
1041
|
+
yield x;
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
});
|
|
1045
|
+
`,
|
|
1046
|
+
},
|
|
1047
|
+
],
|
|
1048
|
+
},
|
|
1049
|
+
],
|
|
1050
|
+
},
|
|
1051
|
+
{
|
|
1052
|
+
name: 'only suggests when a comment before `.flatMap` would be lost, even if a kept comment starts with the same text',
|
|
1053
|
+
code: dedent`
|
|
1054
|
+
declare const xs: readonly number[];
|
|
1055
|
+
const ys = xs // TODO
|
|
1056
|
+
.flatMap((x) => {
|
|
1057
|
+
// TODO: check this
|
|
1058
|
+
const y = x + 1;
|
|
1059
|
+
return y > 0 ? [y] : [];
|
|
1060
|
+
});
|
|
1061
|
+
`,
|
|
1062
|
+
output: null,
|
|
1063
|
+
errors: [
|
|
1064
|
+
{
|
|
1065
|
+
messageId: 'useArrGenerate',
|
|
1066
|
+
suggestions: [
|
|
1067
|
+
{
|
|
1068
|
+
messageId: 'suggestArrGenerate',
|
|
1069
|
+
output: dedent`
|
|
1070
|
+
import { Arr } from 'ts-data-forge';
|
|
1071
|
+
declare const xs: readonly number[];
|
|
1072
|
+
const ys = Arr.generate(function* () {
|
|
1073
|
+
for (const x of xs) {
|
|
1074
|
+
// TODO: check this
|
|
1075
|
+
const y = x + 1;
|
|
1076
|
+
if (y > 0) {
|
|
1077
|
+
yield y;
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
});
|
|
1081
|
+
`,
|
|
1082
|
+
},
|
|
1083
|
+
],
|
|
1084
|
+
},
|
|
1085
|
+
],
|
|
1086
|
+
},
|
|
1087
|
+
{
|
|
1088
|
+
name: 'keeps the fix automatic for a multi-line comment in the prelude',
|
|
1089
|
+
code: dedent`
|
|
1090
|
+
declare const xs: readonly number[];
|
|
1091
|
+
const ys = xs.flatMap((x) => {
|
|
1092
|
+
/*
|
|
1093
|
+
* Odd ones only.
|
|
1094
|
+
*/
|
|
1095
|
+
const y = x;
|
|
1096
|
+
return y % 2 === 1 ? [y] : [];
|
|
1097
|
+
});
|
|
1098
|
+
`,
|
|
1099
|
+
output: dedent`
|
|
1100
|
+
import { Arr } from 'ts-data-forge';
|
|
1101
|
+
declare const xs: readonly number[];
|
|
1102
|
+
const ys = Arr.generate(function* () {
|
|
1103
|
+
for (const x of xs) {
|
|
1104
|
+
/*
|
|
1105
|
+
* Odd ones only.
|
|
1106
|
+
*/
|
|
1107
|
+
const y = x;
|
|
1108
|
+
if (y % 2 === 1) {
|
|
1109
|
+
yield y;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
});
|
|
1113
|
+
`,
|
|
1114
|
+
errors: [{ messageId: 'useArrGenerate' }],
|
|
1115
|
+
},
|
|
887
1116
|
{
|
|
888
1117
|
name: 'only suggests when a comment would be lost',
|
|
889
1118
|
code: dedent`
|
|
@@ -79,8 +79,8 @@ export const preferArrIsFixedLengthArray: TSESLint.RuleModule<
|
|
|
79
79
|
mut_nodesToFix.push({
|
|
80
80
|
node,
|
|
81
81
|
arrayExpression,
|
|
82
|
-
// The literal or constant itself: a wrapper such as `as number`
|
|
83
|
-
//
|
|
82
|
+
// The literal or constant itself: a wrapper such as `as number` would
|
|
83
|
+
// only widen the length the guard narrows to.
|
|
84
84
|
lengthExpression: skipTypeWrappers(valueSide),
|
|
85
85
|
isNegated,
|
|
86
86
|
});
|
|
@@ -85,8 +85,8 @@ export const preferArrIsMaxLengthArray: TSESLint.RuleModule<
|
|
|
85
85
|
mut_nodesToFix.push({
|
|
86
86
|
node,
|
|
87
87
|
arrayExpression,
|
|
88
|
-
// The literal or constant itself: a wrapper such as `as number`
|
|
89
|
-
//
|
|
88
|
+
// The literal or constant itself: a wrapper such as `as number` would
|
|
89
|
+
// only widen the length the guard narrows to.
|
|
90
90
|
lengthExpression: skipTypeWrappers(valueSide),
|
|
91
91
|
});
|
|
92
92
|
},
|
|
@@ -85,8 +85,8 @@ export const preferArrIsMinLengthArray: TSESLint.RuleModule<
|
|
|
85
85
|
mut_nodesToFix.push({
|
|
86
86
|
node,
|
|
87
87
|
arrayExpression,
|
|
88
|
-
// The literal or constant itself: a wrapper such as `as number`
|
|
89
|
-
//
|
|
88
|
+
// The literal or constant itself: a wrapper such as `as number` would
|
|
89
|
+
// only widen the length the guard narrows to.
|
|
90
90
|
lengthExpression: skipTypeWrappers(valueSide),
|
|
91
91
|
});
|
|
92
92
|
},
|
|
@@ -45,8 +45,8 @@ type AccumulationCandidate = Readonly<{
|
|
|
45
45
|
call: TSESTree.CallExpression;
|
|
46
46
|
/**
|
|
47
47
|
* What the call is replaced with, after the name `Arr` is bound to
|
|
48
|
-
* (`scan(xs, f, init)`), or `undefined` when the rewrite could not be
|
|
49
|
-
*
|
|
48
|
+
* (`scan(xs, f, init)`), or `undefined` when the rewrite could not be derived
|
|
49
|
+
* and the report carries no fix.
|
|
50
50
|
*/
|
|
51
51
|
replacement: string | undefined;
|
|
52
52
|
}>;
|
|
@@ -280,12 +280,12 @@ export const preferArrScan: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
280
280
|
// `acc.at(-1)` is `T | undefined` only because `at` can go out of bounds,
|
|
281
281
|
// which a seeded accumulator never does. So once the value `scan` carries
|
|
282
282
|
// is known not to be nullish, a `?? fallback` guarding the read is dead
|
|
283
|
-
// and the fix takes it with the read — leaving it would hand the author
|
|
284
|
-
// unnecessary condition that no rule can fix for them. Every layer of
|
|
285
|
-
// type wrappers is asked (see typeWrapperLayers): the value under an
|
|
286
|
-
//
|
|
287
|
-
//
|
|
288
|
-
//
|
|
283
|
+
// and the fix takes it with the read — leaving it would hand the author
|
|
284
|
+
// an unnecessary condition that no rule can fix for them. Every layer of
|
|
285
|
+
// type wrappers is asked (see typeWrapperLayers): the value under an `as`
|
|
286
|
+
// / `!` is what `scan` carries at run time (`[init!]` may still hold
|
|
287
|
+
// `undefined`), and a wider type a wrapper states is the author saying it
|
|
288
|
+
// may be nullish.
|
|
289
289
|
const carriesNullish = [seed, element].some((node) =>
|
|
290
290
|
typeWrapperLayers(node).some(isNullish),
|
|
291
291
|
);
|
|
@@ -67,9 +67,9 @@ export const preferArrSum: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
67
67
|
|
|
68
68
|
const reducer = node.arguments[0];
|
|
69
69
|
|
|
70
|
-
// The initial value and the operands of the body are read through
|
|
71
|
-
//
|
|
72
|
-
//
|
|
70
|
+
// The initial value and the operands of the body are read through type
|
|
71
|
+
// wrappers: `0 as number` is still 0, `(b satisfies number)` is still
|
|
72
|
+
// `b`. Types are asked of the values underneath too.
|
|
73
73
|
const initialValue = skipTypeWrappers(node.arguments[1]);
|
|
74
74
|
|
|
75
75
|
// Check initial value is 0
|
|
@@ -55,8 +55,8 @@ export const preferArrUniq: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
55
55
|
|
|
56
56
|
const esTreeNodeToTSNodeMap = services?.esTreeNodeToTSNodeMap;
|
|
57
57
|
|
|
58
|
-
// Without type information the rule cannot tell an array of primitives
|
|
59
|
-
//
|
|
58
|
+
// Without type information the rule cannot tell an array of primitives from
|
|
59
|
+
// an array of objects, and would suggest code that does not compile.
|
|
60
60
|
if (checker === undefined || esTreeNodeToTSNodeMap === undefined) {
|
|
61
61
|
return {};
|
|
62
62
|
}
|
|
@@ -87,8 +87,8 @@ const asNegatedPositiveIntegerLiteral = (
|
|
|
87
87
|
|
|
88
88
|
/**
|
|
89
89
|
* Whether re-reading the expression is free of side effects (a plain variable
|
|
90
|
-
* or a non-computed property chain, type wrappers aside). Required for
|
|
91
|
-
*
|
|
90
|
+
* or a non-computed property chain, type wrappers aside). Required for patterns
|
|
91
|
+
* that reference the receiver twice, such as
|
|
92
92
|
* `xs.filter((_, i) => i < xs.length - 1)`.
|
|
93
93
|
*/
|
|
94
94
|
const isSimpleReference = (wrapped: TSESTree.Node): boolean => {
|
|
@@ -213,8 +213,8 @@ export const preferCanonicalArraySlicing: TSESLint.RuleModule<
|
|
|
213
213
|
const checker = parserServices.program.getTypeChecker();
|
|
214
214
|
|
|
215
215
|
/**
|
|
216
|
-
* The in-scope way to reference the `Arr` namespace, the name the
|
|
217
|
-
*
|
|
216
|
+
* The in-scope way to reference the `Arr` namespace, the name the reference
|
|
217
|
+
* starts with, and the edits that bring it into scope: a value
|
|
218
218
|
* import of `Arr` (possibly aliased), a namespace import member, or `Arr`
|
|
219
219
|
* bound by {@link bindValueImports}, which adds it to an existing import or
|
|
220
220
|
* drops `type` from one. `undefined` when none can: `import type { Arr }`
|
|
@@ -235,8 +235,8 @@ export const preferCanonicalArraySlicing: TSESLint.RuleModule<
|
|
|
235
235
|
const arrRef = arrTarget?.ref ?? 'Arr';
|
|
236
236
|
|
|
237
237
|
/**
|
|
238
|
-
* Deferred (non-concrete) types are reduced to their base constraint
|
|
239
|
-
*
|
|
238
|
+
* Deferred (non-concrete) types are reduced to their base constraint before
|
|
239
|
+
* checking; `any` / `unknown` are conservatively not array-like.
|
|
240
240
|
*/
|
|
241
241
|
const isArrayOrTupleType = (type: ts.Type): boolean => {
|
|
242
242
|
if ((type.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !== 0) {
|
|
@@ -398,8 +398,8 @@ export const preferCanonicalArraySlicing: TSESLint.RuleModule<
|
|
|
398
398
|
|
|
399
399
|
/**
|
|
400
400
|
* Matches an index-only filter callback (`(_, i) => <comparison>`) and
|
|
401
|
-
* classifies the comparison. Both a leading index (`i >= 1`) and a
|
|
402
|
-
*
|
|
401
|
+
* classifies the comparison. Both a leading index (`i >= 1`) and a trailing
|
|
402
|
+
* index (`1 <= i`) operand order are accepted.
|
|
403
403
|
*/
|
|
404
404
|
const classifyFilterCallback = (
|
|
405
405
|
callback: TSESTree.CallExpressionArgument,
|
|
@@ -471,10 +471,10 @@ export const preferCanonicalArraySlicing: TSESLint.RuleModule<
|
|
|
471
471
|
return undefined;
|
|
472
472
|
}
|
|
473
473
|
|
|
474
|
-
// The element parameter must not participate in the comparison (only
|
|
475
|
-
//
|
|
476
|
-
//
|
|
477
|
-
//
|
|
474
|
+
// The element parameter must not participate in the comparison (only the
|
|
475
|
+
// index side and a receiver-independent bound are recognized, so reaching
|
|
476
|
+
// here with an element reference is impossible for literals, but
|
|
477
|
+
// `xs.length - N` needs a stable receiver).
|
|
478
478
|
const n = asNonNegativeIntegerLiteral(other);
|
|
479
479
|
|
|
480
480
|
if (n !== undefined) {
|
|
@@ -724,8 +724,8 @@ export const preferCanonicalArraySlicing: TSESLint.RuleModule<
|
|
|
724
724
|
return;
|
|
725
725
|
}
|
|
726
726
|
|
|
727
|
-
// The receiver must be an array or tuple (not a string / typed array
|
|
728
|
-
//
|
|
727
|
+
// The receiver must be an array or tuple (not a string / typed array /
|
|
728
|
+
// other object that happens to share the method name).
|
|
729
729
|
if (!holdsThroughWrappers(receiver, isArrayOrTupleType)) {
|
|
730
730
|
return;
|
|
731
731
|
}
|
|
@@ -798,8 +798,8 @@ export const preferCanonicalArraySlicing: TSESLint.RuleModule<
|
|
|
798
798
|
return;
|
|
799
799
|
}
|
|
800
800
|
|
|
801
|
-
// `xs.concat(item)` appends only when `item` can never be an
|
|
802
|
-
//
|
|
801
|
+
// `xs.concat(item)` appends only when `item` can never be an array
|
|
802
|
+
// (otherwise `concat` would flatten it).
|
|
803
803
|
if (holdsThroughWrappers(arg, isDefinitelyNotArrayType)) {
|
|
804
804
|
report(
|
|
805
805
|
node,
|
|
@@ -839,8 +839,7 @@ export const preferCanonicalArraySlicing: TSESLint.RuleModule<
|
|
|
839
839
|
: [undefined, undefined, undefined];
|
|
840
840
|
|
|
841
841
|
// `spread`, `item`, and `fn` are set together (all from the same
|
|
842
|
-
// tuple), so checking `spread` also guarantees the others are
|
|
843
|
-
// defined.
|
|
842
|
+
// tuple), so checking `spread` also guarantees the others are defined.
|
|
844
843
|
if (spread === undefined) {
|
|
845
844
|
return;
|
|
846
845
|
}
|
|
@@ -288,10 +288,10 @@ const matchesBounds = (
|
|
|
288
288
|
/**
|
|
289
289
|
* Removes the length arguments the rewrite drops. Each removal runs from the
|
|
290
290
|
* first token after the delimiter before the dropped argument (the call's `(`
|
|
291
|
-
* or the previous comma) up to the first token after the comma that follows
|
|
292
|
-
*
|
|
293
|
-
*
|
|
294
|
-
*
|
|
291
|
+
* or the previous comma) up to the first token after the comma that follows it.
|
|
292
|
+
* Parentheses the source put around the dropped argument, the comma, whitespace
|
|
293
|
+
* and any comments in between go with it; parentheses around the next argument
|
|
294
|
+
* stay. The ranges never overlap.
|
|
295
295
|
*/
|
|
296
296
|
const dropBoundsFix = (
|
|
297
297
|
fixer: TSESLint.RuleFixer,
|
|
@@ -303,13 +303,13 @@ const dropBoundsFix = (
|
|
|
303
303
|
rewrite: CastRewrite,
|
|
304
304
|
): readonly TSESLint.RuleFix[] =>
|
|
305
305
|
rewrite.bounds.flatMap((_, index) => {
|
|
306
|
-
// Read before the `includes` below, not after. The strict standard
|
|
307
|
-
//
|
|
308
|
-
//
|
|
309
|
-
//
|
|
310
|
-
// `
|
|
311
|
-
//
|
|
312
|
-
//
|
|
306
|
+
// Read before the `includes` below, not after. The strict standard library
|
|
307
|
+
// types `includes` as `searchElement is T` — that is what lets a literal
|
|
308
|
+
// union narrow a wider value — but the predicate applies to the false
|
|
309
|
+
// branch too, and here `keep` is `readonly number[]` against a `number`, so
|
|
310
|
+
// `index` narrows to `never` past the guard and `index + 1` stops
|
|
311
|
+
// type-checking. A false `includes` means "not in this array", not "not of
|
|
312
|
+
// this type".
|
|
313
313
|
const dropped = node.arguments[index];
|
|
314
314
|
|
|
315
315
|
const previous = node.arguments[index - 1];
|
|
@@ -59,8 +59,8 @@ const MESSAGES = {
|
|
|
59
59
|
} as const satisfies ReadonlyRecord<MessageIds, string>;
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
|
-
* Length guards whose bound makes them redundant with a named degenerate
|
|
63
|
-
*
|
|
62
|
+
* Length guards whose bound makes them redundant with a named degenerate guard,
|
|
63
|
+
* keyed by guard name.
|
|
64
64
|
*
|
|
65
65
|
* Every entry stays inside its own family rather than crossing the
|
|
66
66
|
* branded/structural divide:
|