eslint-config-typed 5.6.2 → 5.8.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/dist/plugins/total-functions/rules/no-partial-division.mjs +2 -2
- package/dist/plugins/total-functions/rules/no-partial-division.mjs.map +1 -1
- package/dist/plugins/ts-restrictions/rules/no-string-spread.d.mts +32 -0
- package/dist/plugins/ts-restrictions/rules/no-string-spread.d.mts.map +1 -0
- package/dist/plugins/ts-restrictions/rules/no-string-spread.mjs +102 -0
- package/dist/plugins/ts-restrictions/rules/no-string-spread.mjs.map +1 -0
- package/dist/plugins/ts-restrictions/rules/prefer-curried-call.d.mts +22 -0
- package/dist/plugins/ts-restrictions/rules/prefer-curried-call.d.mts.map +1 -0
- package/dist/plugins/ts-restrictions/rules/prefer-curried-call.mjs +284 -0
- package/dist/plugins/ts-restrictions/rules/prefer-curried-call.mjs.map +1 -0
- package/dist/plugins/ts-restrictions/rules/rules.d.mts +2 -0
- package/dist/plugins/ts-restrictions/rules/rules.d.mts.map +1 -1
- package/dist/plugins/ts-restrictions/rules/rules.mjs +4 -0
- package/dist/plugins/ts-restrictions/rules/rules.mjs.map +1 -1
- package/dist/rules/eslint-ts-restrictions-rules.d.mts +2 -0
- package/dist/rules/eslint-ts-restrictions-rules.d.mts.map +1 -1
- package/dist/rules/eslint-ts-restrictions-rules.mjs +2 -0
- package/dist/rules/eslint-ts-restrictions-rules.mjs.map +1 -1
- package/dist/types/rules/eslint-ts-restrictions-rules.d.mts +29 -0
- package/dist/types/rules/eslint-ts-restrictions-rules.d.mts.map +1 -1
- package/package.json +1 -1
- package/src/plugins/total-functions/rules/no-partial-division.mts +2 -2
- package/src/plugins/ts-restrictions/rules/no-string-spread.mts +118 -0
- package/src/plugins/ts-restrictions/rules/no-string-spread.test.mts +175 -0
- package/src/plugins/ts-restrictions/rules/prefer-curried-call.mts +393 -0
- package/src/plugins/ts-restrictions/rules/prefer-curried-call.test.mts +213 -0
- package/src/plugins/ts-restrictions/rules/rules.mts +4 -0
- package/src/rules/eslint-ts-restrictions-rules.mts +2 -0
- package/src/types/rules/eslint-ts-restrictions-rules.mts +31 -0
|
@@ -22,10 +22,10 @@ const noPartialDivision = createRule({
|
|
|
22
22
|
if (type.isIntersection()) {
|
|
23
23
|
const numberLiteralParts = intersectionTypeParts(type).filter((t) => isBigIntLiteral(t) || t.isNumberLiteral());
|
|
24
24
|
return (numberLiteralParts.length > 0 &&
|
|
25
|
-
numberLiteralParts.every(
|
|
25
|
+
numberLiteralParts.every(isSafeDenominator));
|
|
26
26
|
}
|
|
27
27
|
if (type.isUnion()) {
|
|
28
|
-
return unionTypeParts(type).every(
|
|
28
|
+
return unionTypeParts(type).every(isSafeDenominator);
|
|
29
29
|
}
|
|
30
30
|
return ((type.isNumberLiteral() && type.value !== 0) ||
|
|
31
31
|
(isBigIntLiteral(type) && type.value.base10Value !== '0'));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-partial-division.mjs","sources":["../../../../src/plugins/total-functions/rules/no-partial-division.mts"],"sourcesContent":[null],"names":[],"mappings":";;;;AASA;AAEO,MAAM,iBAAiB,GAAG,UAAU,CAAC;AAC1C,IAAA,IAAI,EAAE,qBAAqB;AAC3B,IAAA,IAAI,EAAE;AACJ,QAAA,IAAI,EAAE,SAAS;AACf,QAAA,IAAI,EAAE;AACJ,YAAA,WAAW,EAAE,wBAAwB;AACtC,SAAA;AACD,QAAA,QAAQ,EAAE;AACR,YAAA,kBAAkB,EAChB,2GAA2G;AAC9G,SAAA;AACD,QAAA,MAAM,EAAE,EAAE;AACX,KAAA;AACD,IAAA,MAAM,EAAE,CAAC,OAAO,KAAI;QAClB,MAAM,cAAc,GAAG,WAAW,CAAC,iBAAiB,CAAC,OAAO,CAAC;QAE7D,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,cAAc,EAAE;AAEvD,QAAA,MAAM,iBAAiB,GAAG,CAAC,IAAU,KAAa;AAChD,YAAA,IAAI,IAAI,CAAC,cAAc,EAAE,EAAE;gBACzB,MAAM,kBAAkB,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC,MAAM,CAC3D,CAAC,CAAC,KAAK,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,eAAe,EAAE,CACjD;AAED,gBAAA,QACE,kBAAkB,CAAC,MAAM,GAAG,CAAC;AAC7B,oBAAA,kBAAkB,CAAC,KAAK,CAAC,
|
|
1
|
+
{"version":3,"file":"no-partial-division.mjs","sources":["../../../../src/plugins/total-functions/rules/no-partial-division.mts"],"sourcesContent":[null],"names":[],"mappings":";;;;AASA;AAEO,MAAM,iBAAiB,GAAG,UAAU,CAAC;AAC1C,IAAA,IAAI,EAAE,qBAAqB;AAC3B,IAAA,IAAI,EAAE;AACJ,QAAA,IAAI,EAAE,SAAS;AACf,QAAA,IAAI,EAAE;AACJ,YAAA,WAAW,EAAE,wBAAwB;AACtC,SAAA;AACD,QAAA,QAAQ,EAAE;AACR,YAAA,kBAAkB,EAChB,2GAA2G;AAC9G,SAAA;AACD,QAAA,MAAM,EAAE,EAAE;AACX,KAAA;AACD,IAAA,MAAM,EAAE,CAAC,OAAO,KAAI;QAClB,MAAM,cAAc,GAAG,WAAW,CAAC,iBAAiB,CAAC,OAAO,CAAC;QAE7D,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,cAAc,EAAE;AAEvD,QAAA,MAAM,iBAAiB,GAAG,CAAC,IAAU,KAAa;AAChD,YAAA,IAAI,IAAI,CAAC,cAAc,EAAE,EAAE;gBACzB,MAAM,kBAAkB,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC,MAAM,CAC3D,CAAC,CAAC,KAAK,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,eAAe,EAAE,CACjD;AAED,gBAAA,QACE,kBAAkB,CAAC,MAAM,GAAG,CAAC;AAC7B,oBAAA,kBAAkB,CAAC,KAAK,CAAC,iBAAiB,CAAC;YAE/C;AAEA,YAAA,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE;gBAClB,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC;YACtD;AAEA,YAAA,QACE,CAAC,IAAI,CAAC,eAAe,EAAE,IAAI,IAAI,CAAC,KAAK,KAAK,CAAC;AAC3C,iBAAC,eAAe,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,WAAW,KAAK,GAAG,CAAC;AAE7D,QAAA,CAAC;QAED,OAAO;AACL,YAAA,gBAAgB,EAAE,CAAC,IAAI,KAAI;AACzB,gBAAA,IAAI,IAAI,CAAC,QAAQ,KAAK,GAAG,EAAE;;oBAEzB;gBACF;gBAEA,IACE,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,cAAc,CAAC,OAAO;AAC1C,oBAAA,IAAI,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC;AACtB,oBAAA,IAAI,CAAC,KAAK,CAAC,KAAK,KAAK,EAAE,EACvB;;oBAEA;gBACF;gBAEA,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,cAAc,CAAC,UAAU,EAAE;AACjD,oBAAA,MAAM,eAAe,GAAG,cAAc,CAAC,qBAAqB,CAAC,GAAG,CAC9D,IAAI,CAAC,KAAK,CACX;oBAED,MAAM,mBAAmB,GACvB,OAAO,CAAC,iBAAiB,CAAC,eAAe,CAAC;AAE5C,oBAAA,IAAI,iBAAiB,CAAC,mBAAmB,CAAC,EAAE;wBAC1C;oBACF;gBACF;;gBAIA,OAAO,CAAC,MAAM,CAAC;oBACb,IAAI;AACJ,oBAAA,SAAS,EAAE,oBAAoB;AACvB,iBAAA,CAAC;YACb,CAAC;SACF;IACH,CAAC;AACD,IAAA,cAAc,EAAE,EAAE;AACV,CAAA;AAEV;AACA;AACA;AACA,MAAM,cAAc,GAAG,CAAC,GAAY,KAAyB;IAC3D,MAAM,iBAAiB,GACrB,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK;AACjC,UAAG;UACD,SAAS;IAEf,QACE,iBAAiB,KAAK,SAAS;AAC/B,QAAA,OAAO,iBAAiB,CAAC,WAAW,KAAK,QAAQ;AACjD,QAAA,OAAO,iBAAiB,CAAC,QAAQ,KAAK,SAAS;AAEnD,CAAC;AAED,MAAM,eAAe,GAAG,CAAC,IAAU;AACjC;AACA,cAAc,CAAE,IAAmC,EAAE,KAAK,CAAC;;;;"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type TSESLint } from '@typescript-eslint/utils';
|
|
2
|
+
type Options = readonly [];
|
|
3
|
+
type MessageIds = 'noStringSpread';
|
|
4
|
+
/**
|
|
5
|
+
* Disallow spreading a `string` value with the spread operator `...`.
|
|
6
|
+
*
|
|
7
|
+
* ## Why
|
|
8
|
+
*
|
|
9
|
+
* Spreading a string iterates it into its individual characters, so
|
|
10
|
+
* `[...someString]` (or `f(...someString)`) evaluates to a `string[]` — the
|
|
11
|
+
* *exact same result type* as spreading an actual `string[]`. That makes an
|
|
12
|
+
* accidental string spread silent: neither the reader nor the type checker can
|
|
13
|
+
* tell `[...anArray]` from `[...aString]` by the result type alone, so a bug
|
|
14
|
+
* where an array was expected but a `string` slipped in goes unnoticed.
|
|
15
|
+
*
|
|
16
|
+
* ## Why not simply "allow array / object types only"
|
|
17
|
+
*
|
|
18
|
+
* The naive framing — "only permit `...` on arrays or objects" — would also
|
|
19
|
+
* reject perfectly idiomatic spreads of other iterables (`Set`, `Map`,
|
|
20
|
+
* `Map.prototype.keys()`, generators, `NodeList`, typed arrays, `arguments`,
|
|
21
|
+
* …), which have no equivalent silent hazard. And TypeScript *already* rejects
|
|
22
|
+
* spreading a non-iterable primitive such as `number` / `boolean` in an
|
|
23
|
+
* iterable position. The only case TypeScript accepts yet is genuinely
|
|
24
|
+
* error-prone is `string`, so that is exactly what this rule targets.
|
|
25
|
+
*
|
|
26
|
+
* If a character split is really intended, spell it out with
|
|
27
|
+
* `Array.from(str)` (identical to the spread, code-point aware) or
|
|
28
|
+
* `str.split('')`.
|
|
29
|
+
*/
|
|
30
|
+
export declare const noStringSpread: TSESLint.RuleModule<MessageIds, Options>;
|
|
31
|
+
export {};
|
|
32
|
+
//# sourceMappingURL=no-string-spread.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-string-spread.d.mts","sourceRoot":"","sources":["../../../../src/plugins/ts-restrictions/rules/no-string-spread.mts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAGtE,KAAK,OAAO,GAAG,SAAS,EAAE,CAAC;AAE3B,KAAK,UAAU,GAAG,gBAAgB,CAAC;AAanC;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,UAAU,CAAC,UAAU,EAAE,OAAO,CAyE1D,CAAC"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
import * as ts from 'typescript';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Deferred (non-concrete) type flags whose actual members are only known once
|
|
6
|
+
* the type is instantiated. They are reduced to their base constraint before
|
|
7
|
+
* checking whether they are string-like.
|
|
8
|
+
*/
|
|
9
|
+
const DEFERRED_TYPE_FLAGS = ts.TypeFlags.TypeParameter |
|
|
10
|
+
ts.TypeFlags.IndexedAccess |
|
|
11
|
+
ts.TypeFlags.Conditional |
|
|
12
|
+
ts.TypeFlags.Substitution;
|
|
13
|
+
/**
|
|
14
|
+
* Disallow spreading a `string` value with the spread operator `...`.
|
|
15
|
+
*
|
|
16
|
+
* ## Why
|
|
17
|
+
*
|
|
18
|
+
* Spreading a string iterates it into its individual characters, so
|
|
19
|
+
* `[...someString]` (or `f(...someString)`) evaluates to a `string[]` — the
|
|
20
|
+
* *exact same result type* as spreading an actual `string[]`. That makes an
|
|
21
|
+
* accidental string spread silent: neither the reader nor the type checker can
|
|
22
|
+
* tell `[...anArray]` from `[...aString]` by the result type alone, so a bug
|
|
23
|
+
* where an array was expected but a `string` slipped in goes unnoticed.
|
|
24
|
+
*
|
|
25
|
+
* ## Why not simply "allow array / object types only"
|
|
26
|
+
*
|
|
27
|
+
* The naive framing — "only permit `...` on arrays or objects" — would also
|
|
28
|
+
* reject perfectly idiomatic spreads of other iterables (`Set`, `Map`,
|
|
29
|
+
* `Map.prototype.keys()`, generators, `NodeList`, typed arrays, `arguments`,
|
|
30
|
+
* …), which have no equivalent silent hazard. And TypeScript *already* rejects
|
|
31
|
+
* spreading a non-iterable primitive such as `number` / `boolean` in an
|
|
32
|
+
* iterable position. The only case TypeScript accepts yet is genuinely
|
|
33
|
+
* error-prone is `string`, so that is exactly what this rule targets.
|
|
34
|
+
*
|
|
35
|
+
* If a character split is really intended, spell it out with
|
|
36
|
+
* `Array.from(str)` (identical to the spread, code-point aware) or
|
|
37
|
+
* `str.split('')`.
|
|
38
|
+
*/
|
|
39
|
+
const noStringSpread = {
|
|
40
|
+
meta: {
|
|
41
|
+
type: 'problem',
|
|
42
|
+
docs: {
|
|
43
|
+
description: 'Disallow spreading a `string` (e.g. `[...str]`), which silently splits it into characters and yields a `string[]` indistinguishable from spreading an array',
|
|
44
|
+
},
|
|
45
|
+
schema: [],
|
|
46
|
+
messages: {
|
|
47
|
+
noStringSpread: 'Spreading a `string` splits it into individual characters and yields a `string[]` indistinguishable from spreading an array, so accidental string spreads go unnoticed. Spread an array or object instead; if a character split is intended, make it explicit with `Array.from(str)`.',
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
create: (context) => {
|
|
51
|
+
const parserServices = ESLintUtils.getParserServices(context);
|
|
52
|
+
const checker = parserServices.program.getTypeChecker();
|
|
53
|
+
/**
|
|
54
|
+
* Returns `true` if the given type could be spread as a string (it is
|
|
55
|
+
* string-like, or a union / intersection with a string-like constituent).
|
|
56
|
+
*
|
|
57
|
+
* - `any` / `unknown` are treated conservatively as *not* string-like:
|
|
58
|
+
* they cannot be proven to be a string, and flagging them would fire on
|
|
59
|
+
* every untyped spread.
|
|
60
|
+
* - Unions are flagged when *any* branch is string-like, because such a
|
|
61
|
+
* branch reintroduces the silent char-split hazard.
|
|
62
|
+
* - Intersections are flagged when *any* constituent is string-like: a
|
|
63
|
+
* branded string (`string & Brand`) is still a `string` at runtime.
|
|
64
|
+
* - A deferred type (type parameter, indexed access, conditional, …) is
|
|
65
|
+
* reduced to its base constraint; one with no resolvable constraint is
|
|
66
|
+
* left alone to avoid false positives on unconstrained generics.
|
|
67
|
+
*/
|
|
68
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
69
|
+
const isStringLikeType = (type) => {
|
|
70
|
+
if ((type.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !== 0) {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
if (type.isUnion() || type.isIntersection()) {
|
|
74
|
+
return type.types.some(isStringLikeType);
|
|
75
|
+
}
|
|
76
|
+
if ((type.flags & DEFERRED_TYPE_FLAGS) !== 0) {
|
|
77
|
+
const constraint = checker.getBaseConstraintOfType(type);
|
|
78
|
+
return constraint !== undefined && isStringLikeType(constraint);
|
|
79
|
+
}
|
|
80
|
+
return (type.flags & ts.TypeFlags.StringLike) !== 0;
|
|
81
|
+
};
|
|
82
|
+
return {
|
|
83
|
+
// `SpreadElement` covers every value-spread position: array literals
|
|
84
|
+
// (`[...x]`), call / new arguments (`f(...x)`), and object literals
|
|
85
|
+
// (`{...x}`). Destructuring rest (`[a, ...rest]`, `{ ...rest }`) is a
|
|
86
|
+
// `RestElement` / `RestType` and is intentionally not matched.
|
|
87
|
+
SpreadElement: (node) => {
|
|
88
|
+
const argTsNode = parserServices.esTreeNodeToTSNodeMap.get(node.argument);
|
|
89
|
+
if (!isStringLikeType(checker.getTypeAtLocation(argTsNode)))
|
|
90
|
+
return;
|
|
91
|
+
context.report({
|
|
92
|
+
node,
|
|
93
|
+
messageId: 'noStringSpread',
|
|
94
|
+
});
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
},
|
|
98
|
+
defaultOptions: [],
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export { noStringSpread };
|
|
102
|
+
//# sourceMappingURL=no-string-spread.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-string-spread.mjs","sources":["../../../../src/plugins/ts-restrictions/rules/no-string-spread.mts"],"sourcesContent":[null],"names":[],"mappings":";;;AAOA;;;;AAIG;AACH,MAAM,mBAAmB,GACvB,EAAE,CAAC,SAAS,CAAC,aAAa;IAC1B,EAAE,CAAC,SAAS,CAAC,aAAa;IAC1B,EAAE,CAAC,SAAS,CAAC,WAAW;AACxB,IAAA,EAAE,CAAC,SAAS,CAAC,YAAY;AAE3B;;;;;;;;;;;;;;;;;;;;;;;;;AAyBG;AACI,MAAM,cAAc,GAA6C;AACtE,IAAA,IAAI,EAAE;AACJ,QAAA,IAAI,EAAE,SAAS;AACf,QAAA,IAAI,EAAE;AACJ,YAAA,WAAW,EACT,6JAA6J;AAChK,SAAA;AACD,QAAA,MAAM,EAAE,EAAE;AACV,QAAA,QAAQ,EAAE;AACR,YAAA,cAAc,EACZ,uRAAuR;AAC1R,SAAA;AACF,KAAA;AAED,IAAA,MAAM,EAAE,CAAC,OAAO,KAAI;QAClB,MAAM,cAAc,GAAG,WAAW,CAAC,iBAAiB,CAAC,OAAO,CAAC;QAE7D,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,cAAc,EAAE;AAEvD;;;;;;;;;;;;;;AAcG;;AAEH,QAAA,MAAM,gBAAgB,GAAG,CAAC,IAAa,KAAa;YAClD,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,SAAS,CAAC,GAAG,GAAG,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;AAClE,gBAAA,OAAO,KAAK;YACd;YAEA,IAAI,IAAI,CAAC,OAAO,EAAE,IAAI,IAAI,CAAC,cAAc,EAAE,EAAE;gBAC3C,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC;YAC1C;YAEA,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,mBAAmB,MAAM,CAAC,EAAE;gBAC5C,MAAM,UAAU,GAAG,OAAO,CAAC,uBAAuB,CAAC,IAAI,CAAC;gBAExD,OAAO,UAAU,KAAK,SAAS,IAAI,gBAAgB,CAAC,UAAU,CAAC;YACjE;AAEA,YAAA,OAAO,CAAC,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC,SAAS,CAAC,UAAU,MAAM,CAAC;AACrD,QAAA,CAAC;QAED,OAAO;;;;;AAKL,YAAA,aAAa,EAAE,CAAC,IAAI,KAAI;AACtB,gBAAA,MAAM,SAAS,GAAG,cAAc,CAAC,qBAAqB,CAAC,GAAG,CACxD,IAAI,CAAC,QAAQ,CACd;gBAED,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;oBAAE;gBAE7D,OAAO,CAAC,MAAM,CAAC;oBACb,IAAI;AACJ,oBAAA,SAAS,EAAE,gBAAgB;AAC5B,iBAAA,CAAC;YACJ,CAAC;SACF;IACH,CAAC;AACD,IAAA,cAAc,EAAE,EAAE;;;;;"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type TSESLint } from '@typescript-eslint/utils';
|
|
2
|
+
type Options = readonly [];
|
|
3
|
+
type MessageIds = 'useFunctionDirectly' | 'useCurriedForm';
|
|
4
|
+
/**
|
|
5
|
+
* Replaces a redundant arrow wrapper around a function call with a point-free
|
|
6
|
+
* form, using the callee's curried version when one exists:
|
|
7
|
+
*
|
|
8
|
+
* - `(a) => f(a)` → `f` — only when `f` is effectively unary, so passing it
|
|
9
|
+
* directly cannot change behavior even when the wrapper is used as a
|
|
10
|
+
* multi-argument callback (`arr.map((a) => f(a))` → `arr.map(f)`). This is
|
|
11
|
+
* what keeps the classic `arr.map((s) => parseInt(s))` case (where `parseInt`
|
|
12
|
+
* also reads the index) from being rewritten.
|
|
13
|
+
* - `(a) => f(a, ...rest)` → `f(...rest)` — only when `f` has a curried call
|
|
14
|
+
* signature accepting `...rest` and returning a one-argument function, i.e.
|
|
15
|
+
* a genuine curried version (`(a) => toPushed(a, x)` → `toPushed(x)`).
|
|
16
|
+
*
|
|
17
|
+
* The arrow's single parameter must appear exactly once, as the first argument
|
|
18
|
+
* of the call, so dropping the wrapper is behavior-preserving.
|
|
19
|
+
*/
|
|
20
|
+
export declare const preferCurriedCall: TSESLint.RuleModule<MessageIds, Options>;
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=prefer-curried-call.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prefer-curried-call.d.mts","sourceRoot":"","sources":["../../../../src/plugins/ts-restrictions/rules/prefer-curried-call.mts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,QAAQ,EAEd,MAAM,0BAA0B,CAAC;AAGlC,KAAK,OAAO,GAAG,SAAS,EAAE,CAAC;AAE3B,KAAK,UAAU,GAAG,qBAAqB,GAAG,gBAAgB,CAAC;AAE3D;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,iBAAiB,EAAE,QAAQ,CAAC,UAAU,CAAC,UAAU,EAAE,OAAO,CA+L7D,CAAC"}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { ESLintUtils, AST_NODE_TYPES } from '@typescript-eslint/utils';
|
|
2
|
+
import * as ts from 'typescript';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Replaces a redundant arrow wrapper around a function call with a point-free
|
|
6
|
+
* form, using the callee's curried version when one exists:
|
|
7
|
+
*
|
|
8
|
+
* - `(a) => f(a)` → `f` — only when `f` is effectively unary, so passing it
|
|
9
|
+
* directly cannot change behavior even when the wrapper is used as a
|
|
10
|
+
* multi-argument callback (`arr.map((a) => f(a))` → `arr.map(f)`). This is
|
|
11
|
+
* what keeps the classic `arr.map((s) => parseInt(s))` case (where `parseInt`
|
|
12
|
+
* also reads the index) from being rewritten.
|
|
13
|
+
* - `(a) => f(a, ...rest)` → `f(...rest)` — only when `f` has a curried call
|
|
14
|
+
* signature accepting `...rest` and returning a one-argument function, i.e.
|
|
15
|
+
* a genuine curried version (`(a) => toPushed(a, x)` → `toPushed(x)`).
|
|
16
|
+
*
|
|
17
|
+
* The arrow's single parameter must appear exactly once, as the first argument
|
|
18
|
+
* of the call, so dropping the wrapper is behavior-preserving.
|
|
19
|
+
*/
|
|
20
|
+
const preferCurriedCall = {
|
|
21
|
+
meta: {
|
|
22
|
+
type: 'suggestion',
|
|
23
|
+
docs: {
|
|
24
|
+
description: 'Replace a redundant arrow wrapper with the function itself (`(a) => f(a)` → `f`) or, when the callee has a curried version, its curried form (`(a) => f(a, ...rest)` → `f(...rest)`).',
|
|
25
|
+
},
|
|
26
|
+
fixable: 'code',
|
|
27
|
+
schema: [],
|
|
28
|
+
messages: {
|
|
29
|
+
useFunctionDirectly: 'Replace the redundant arrow wrapper `{{original}}` with `{{replacement}}`.',
|
|
30
|
+
useCurriedForm: 'Replace the arrow wrapper `{{original}}` with the curried call `{{replacement}}`.',
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
create: (context) => {
|
|
34
|
+
const sourceCode = context.sourceCode;
|
|
35
|
+
const services = ESLintUtils.getParserServices(context);
|
|
36
|
+
const checker = services.program.getTypeChecker();
|
|
37
|
+
const getType = (
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
39
|
+
node) => checker.getTypeAtLocation(services.esTreeNodeToTSNodeMap.get(node));
|
|
40
|
+
/**
|
|
41
|
+
* `true` when `node` refers to a module/namespace object (e.g. a namespace
|
|
42
|
+
* imported as `Arr`). Referencing `Namespace.fn` as a value is safe because
|
|
43
|
+
* namespace members do not depend on a dynamic `this`, unlike instance
|
|
44
|
+
* methods (`obj.method`), which would lose their receiver.
|
|
45
|
+
*/
|
|
46
|
+
const isNamespaceObject = (
|
|
47
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
48
|
+
node) => {
|
|
49
|
+
const symbol = checker.getSymbolAtLocation(services.esTreeNodeToTSNodeMap.get(node));
|
|
50
|
+
if (symbol === undefined)
|
|
51
|
+
return false;
|
|
52
|
+
const resolved = (symbol.flags & ts.SymbolFlags.Alias) !== 0
|
|
53
|
+
? checker.getAliasedSymbol(symbol)
|
|
54
|
+
: symbol;
|
|
55
|
+
return ((resolved.flags &
|
|
56
|
+
(ts.SymbolFlags.ValueModule | ts.SymbolFlags.NamespaceModule)) !==
|
|
57
|
+
0);
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* For the eta reduction `(a) => f(a)` → `f`, the callee must be safe to
|
|
61
|
+
* reference as a bare value: a plain identifier (a standalone function) or
|
|
62
|
+
* a namespace member (`Arr.isNonEmpty`). Instance-method callees are
|
|
63
|
+
* rejected because the point-free form would drop their `this`.
|
|
64
|
+
*/
|
|
65
|
+
const isEtaSafeCallee = (
|
|
66
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
67
|
+
callee) => {
|
|
68
|
+
if (callee.type === AST_NODE_TYPES.Identifier)
|
|
69
|
+
return true;
|
|
70
|
+
if (callee.type === AST_NODE_TYPES.MemberExpression) {
|
|
71
|
+
return !callee.optional && isNamespaceObject(callee.object);
|
|
72
|
+
}
|
|
73
|
+
return false;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* `true` when the arrow's single parameter is referenced exactly once, at
|
|
77
|
+
* `argument` — so it appears neither in the callee nor in any other
|
|
78
|
+
* argument, and dropping the wrapper cannot change behavior.
|
|
79
|
+
*/
|
|
80
|
+
const paramUsedOnlyAt = (
|
|
81
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
82
|
+
node, paramName,
|
|
83
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
84
|
+
argument) => {
|
|
85
|
+
const scope = sourceCode.getScope(node);
|
|
86
|
+
const variable = scope.variables.find((v) => v.name === paramName);
|
|
87
|
+
if (variable === undefined)
|
|
88
|
+
return false;
|
|
89
|
+
const [reference] = variable.references;
|
|
90
|
+
return (variable.references.length === 1 && reference?.identifier === argument);
|
|
91
|
+
};
|
|
92
|
+
return {
|
|
93
|
+
ArrowFunctionExpression: (node) => {
|
|
94
|
+
if (node.async || node.params.length !== 1)
|
|
95
|
+
return;
|
|
96
|
+
const [param] = node.params;
|
|
97
|
+
if (param?.type !== AST_NODE_TYPES.Identifier ||
|
|
98
|
+
param.typeAnnotation !== undefined) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (node.body.type !== AST_NODE_TYPES.CallExpression)
|
|
102
|
+
return;
|
|
103
|
+
const call = node.body;
|
|
104
|
+
if (call.optional)
|
|
105
|
+
return;
|
|
106
|
+
const [firstArg, ...restArgs] = call.arguments;
|
|
107
|
+
if (firstArg?.type !== AST_NODE_TYPES.Identifier ||
|
|
108
|
+
firstArg.name !== param.name) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
// A spread in the remaining arguments breaks positional arg counting.
|
|
112
|
+
if (restArgs.some((arg) => arg.type === AST_NODE_TYPES.SpreadElement)) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (!paramUsedOnlyAt(node, param.name, firstArg))
|
|
116
|
+
return;
|
|
117
|
+
const calleeType = getType(call.callee);
|
|
118
|
+
const calleeText = sourceCode.getText(call.callee);
|
|
119
|
+
if (restArgs.length === 0) {
|
|
120
|
+
// `(a) => f(a)` → `f`
|
|
121
|
+
if (!isEtaSafeCallee(call.callee) ||
|
|
122
|
+
!isEffectivelyUnary(calleeType)) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
context.report({
|
|
126
|
+
node,
|
|
127
|
+
messageId: 'useFunctionDirectly',
|
|
128
|
+
data: {
|
|
129
|
+
original: sourceCode.getText(node),
|
|
130
|
+
replacement: calleeText,
|
|
131
|
+
},
|
|
132
|
+
fix: (fixer) => fixer.replaceText(node, calleeText),
|
|
133
|
+
});
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
// `(a) => f(a, ...rest)` → `f(...rest)`
|
|
137
|
+
// Re-evaluating a side-effecting argument once (at currying time)
|
|
138
|
+
// instead of on every call would change behavior, so require the
|
|
139
|
+
// remaining arguments to be free of observable side effects.
|
|
140
|
+
if (!restArgs.every(isPureExpression) ||
|
|
141
|
+
!hasCurriedSignature(calleeType, restArgs.length)) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const replacement = `${calleeText}(${restArgs
|
|
145
|
+
.map((arg) => sourceCode.getText(arg))
|
|
146
|
+
.join(', ')})`;
|
|
147
|
+
context.report({
|
|
148
|
+
node,
|
|
149
|
+
messageId: 'useCurriedForm',
|
|
150
|
+
data: {
|
|
151
|
+
original: sourceCode.getText(node),
|
|
152
|
+
replacement,
|
|
153
|
+
},
|
|
154
|
+
fix: (fixer) => fixer.replaceText(node, replacement),
|
|
155
|
+
});
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
},
|
|
159
|
+
defaultOptions: [],
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* `true` when every call signature of `type` accepts at most one argument, so
|
|
163
|
+
* `(a) => f(a)` is equivalent to `f` even when the wrapper is used as a
|
|
164
|
+
* multi-argument callback (the extra positional arguments are ignored either
|
|
165
|
+
* way). Requires at least one call signature.
|
|
166
|
+
*/
|
|
167
|
+
const isEffectivelyUnary = (
|
|
168
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
169
|
+
type) => {
|
|
170
|
+
const signatures = type.getCallSignatures();
|
|
171
|
+
return (signatures.length > 0 &&
|
|
172
|
+
signatures.every((signature) => signatureMaxArgs(signature) <= 1));
|
|
173
|
+
};
|
|
174
|
+
/**
|
|
175
|
+
* `true` when `type` has a curried call signature accepting exactly
|
|
176
|
+
* `restCount` arguments and returning a function that accepts one argument —
|
|
177
|
+
* i.e. `f(...rest)` is the curried form of `f(a, ...rest)`.
|
|
178
|
+
*/
|
|
179
|
+
const hasCurriedSignature = (
|
|
180
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
181
|
+
type, restCount) => type.getCallSignatures().some((signature) => {
|
|
182
|
+
if (!isCallableWith(signature, restCount))
|
|
183
|
+
return false;
|
|
184
|
+
return signature
|
|
185
|
+
.getReturnType()
|
|
186
|
+
.getCallSignatures()
|
|
187
|
+
.some((inner) => isCallableWith(inner, 1));
|
|
188
|
+
});
|
|
189
|
+
const isCallableWith = (
|
|
190
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
191
|
+
signature, argCount) => signatureMinArgs(signature) <= argCount &&
|
|
192
|
+
argCount <= signatureMaxArgs(signature);
|
|
193
|
+
/** The largest number of positional arguments the signature accepts. */
|
|
194
|
+
const signatureMaxArgs = (
|
|
195
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
196
|
+
signature) => {
|
|
197
|
+
const params = signature.getParameters();
|
|
198
|
+
const last = params.at(-1);
|
|
199
|
+
return last !== undefined && isRestParameter(last)
|
|
200
|
+
? Number.POSITIVE_INFINITY
|
|
201
|
+
: params.length;
|
|
202
|
+
};
|
|
203
|
+
/** The number of leading required parameters (before any optional/rest). */
|
|
204
|
+
const signatureMinArgs = (
|
|
205
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
206
|
+
signature) => {
|
|
207
|
+
const firstOptional = signature
|
|
208
|
+
.getParameters()
|
|
209
|
+
.findIndex(isOptionalParameter);
|
|
210
|
+
return firstOptional === -1
|
|
211
|
+
? signature.getParameters().length
|
|
212
|
+
: firstOptional;
|
|
213
|
+
};
|
|
214
|
+
/** A parameter that collects the rest of the arguments (`...args`). */
|
|
215
|
+
const isRestParameter = (
|
|
216
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
217
|
+
symbol) => {
|
|
218
|
+
const declaration = symbol.valueDeclaration ?? symbol.declarations?.[0];
|
|
219
|
+
return (declaration !== undefined &&
|
|
220
|
+
ts.isParameter(declaration) &&
|
|
221
|
+
declaration.dotDotDotToken !== undefined);
|
|
222
|
+
};
|
|
223
|
+
/** A parameter that may be omitted (`x?`, `x = ...`) or is a rest parameter. */
|
|
224
|
+
const isOptionalParameter = (
|
|
225
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
226
|
+
symbol) => {
|
|
227
|
+
const declaration = symbol.valueDeclaration ?? symbol.declarations?.[0];
|
|
228
|
+
return (declaration !== undefined &&
|
|
229
|
+
ts.isParameter(declaration) &&
|
|
230
|
+
(declaration.questionToken !== undefined ||
|
|
231
|
+
declaration.initializer !== undefined ||
|
|
232
|
+
declaration.dotDotDotToken !== undefined));
|
|
233
|
+
};
|
|
234
|
+
/**
|
|
235
|
+
* A conservative purity approximation: `true` only for expressions whose
|
|
236
|
+
* evaluation has no observable side effects and does not depend on evaluation
|
|
237
|
+
* count (so hoisting it out of the wrapper is safe). Anything not recognized
|
|
238
|
+
* (calls, `new`, `await`, assignments, updates) is treated as impure.
|
|
239
|
+
*/
|
|
240
|
+
const isPureExpression = (
|
|
241
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
242
|
+
node) => {
|
|
243
|
+
if (node.type === AST_NODE_TYPES.Literal ||
|
|
244
|
+
node.type === AST_NODE_TYPES.Identifier ||
|
|
245
|
+
node.type === AST_NODE_TYPES.ThisExpression ||
|
|
246
|
+
node.type === AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
247
|
+
node.type === AST_NODE_TYPES.FunctionExpression) {
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
if (node.type === AST_NODE_TYPES.TemplateLiteral) {
|
|
251
|
+
return node.expressions.every(isPureExpression);
|
|
252
|
+
}
|
|
253
|
+
if (node.type === AST_NODE_TYPES.MemberExpression) {
|
|
254
|
+
return node.computed
|
|
255
|
+
? isPureExpression(node.object) && isPureExpression(node.property)
|
|
256
|
+
: isPureExpression(node.object);
|
|
257
|
+
}
|
|
258
|
+
if (node.type === AST_NODE_TYPES.UnaryExpression) {
|
|
259
|
+
return node.operator !== 'delete' && isPureExpression(node.argument);
|
|
260
|
+
}
|
|
261
|
+
if (node.type === AST_NODE_TYPES.BinaryExpression ||
|
|
262
|
+
node.type === AST_NODE_TYPES.LogicalExpression) {
|
|
263
|
+
return isPureExpression(node.left) && isPureExpression(node.right);
|
|
264
|
+
}
|
|
265
|
+
if (node.type === AST_NODE_TYPES.ConditionalExpression) {
|
|
266
|
+
return (isPureExpression(node.test) &&
|
|
267
|
+
isPureExpression(node.consequent) &&
|
|
268
|
+
isPureExpression(node.alternate));
|
|
269
|
+
}
|
|
270
|
+
if (node.type === AST_NODE_TYPES.ArrayExpression) {
|
|
271
|
+
return node.elements.every((element) => element === null ||
|
|
272
|
+
(element.type !== AST_NODE_TYPES.SpreadElement &&
|
|
273
|
+
isPureExpression(element)));
|
|
274
|
+
}
|
|
275
|
+
if (node.type === AST_NODE_TYPES.ObjectExpression) {
|
|
276
|
+
return node.properties.every((property) => property.type === AST_NODE_TYPES.Property &&
|
|
277
|
+
!property.computed &&
|
|
278
|
+
isPureExpression(property.value));
|
|
279
|
+
}
|
|
280
|
+
return false;
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
export { preferCurriedCall };
|
|
284
|
+
//# sourceMappingURL=prefer-curried-call.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prefer-curried-call.mjs","sources":["../../../../src/plugins/ts-restrictions/rules/prefer-curried-call.mts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
@@ -14,8 +14,10 @@ export declare const tsRestrictionsRules: {
|
|
|
14
14
|
}>;
|
|
15
15
|
}>)[], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
16
16
|
readonly 'no-restricted-syntax': import("eslint").Rule.RuleModule;
|
|
17
|
+
readonly 'no-string-spread': import("@typescript-eslint/utils/ts-eslint").RuleModule<"noStringSpread", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
17
18
|
readonly 'no-unnecessary-array-from': import("@typescript-eslint/utils/ts-eslint").RuleModule<"unnecessaryArrayFrom", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
18
19
|
readonly 'no-unnecessary-coalesce-undefined': import("@typescript-eslint/utils/ts-eslint").RuleModule<"unnecessaryCoalesceUndefined", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
20
|
+
readonly 'prefer-curried-call': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useFunctionDirectly" | "useCurriedForm", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
19
21
|
readonly 'prefer-non-mutating-array-method': import("@typescript-eslint/utils/ts-eslint").RuleModule<"preferNonMutatingMethod", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
20
22
|
};
|
|
21
23
|
//# sourceMappingURL=rules.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rules.d.mts","sourceRoot":"","sources":["../../../../src/plugins/ts-restrictions/rules/rules.mts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rules.d.mts","sourceRoot":"","sources":["../../../../src/plugins/ts-restrictions/rules/rules.mts"],"names":[],"mappings":"AAUA,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;CASU,CAAC"}
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
import { checkDestructuringCompleteness } from './check-destructuring-completeness.mjs';
|
|
2
2
|
import { noRestrictedCastName } from './no-restricted-cast-name.mjs';
|
|
3
3
|
import { noRestrictedSyntax } from './no-restricted-syntax.mjs';
|
|
4
|
+
import { noStringSpread } from './no-string-spread.mjs';
|
|
4
5
|
import { noUnnecessaryArrayFrom } from './no-unnecessary-array-from.mjs';
|
|
5
6
|
import { noUnnecessaryCoalesceUndefined } from './no-unnecessary-coalesce-undefined.mjs';
|
|
7
|
+
import { preferCurriedCall } from './prefer-curried-call.mjs';
|
|
6
8
|
import { preferNonMutatingArrayMethod } from './prefer-non-mutating-array-method.mjs';
|
|
7
9
|
|
|
8
10
|
const tsRestrictionsRules = {
|
|
9
11
|
'check-destructuring-completeness': checkDestructuringCompleteness,
|
|
10
12
|
'no-restricted-cast-name': noRestrictedCastName,
|
|
11
13
|
'no-restricted-syntax': noRestrictedSyntax,
|
|
14
|
+
'no-string-spread': noStringSpread,
|
|
12
15
|
'no-unnecessary-array-from': noUnnecessaryArrayFrom,
|
|
13
16
|
'no-unnecessary-coalesce-undefined': noUnnecessaryCoalesceUndefined,
|
|
17
|
+
'prefer-curried-call': preferCurriedCall,
|
|
14
18
|
'prefer-non-mutating-array-method': preferNonMutatingArrayMethod,
|
|
15
19
|
};
|
|
16
20
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rules.mjs","sources":["../../../../src/plugins/ts-restrictions/rules/rules.mts"],"sourcesContent":[null],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rules.mjs","sources":["../../../../src/plugins/ts-restrictions/rules/rules.mts"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;;;AAUO,MAAM,mBAAmB,GAAG;AACjC,IAAA,kCAAkC,EAAE,8BAA8B;AAClE,IAAA,yBAAyB,EAAE,oBAAoB;AAC/C,IAAA,sBAAsB,EAAE,kBAAkB;AAC1C,IAAA,kBAAkB,EAAE,cAAc;AAClC,IAAA,2BAA2B,EAAE,sBAAsB;AACnD,IAAA,mCAAmC,EAAE,8BAA8B;AACnE,IAAA,qBAAqB,EAAE,iBAAiB;AACxC,IAAA,kCAAkC,EAAE,4BAA4B;;;;;"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export declare const eslintTsRestrictionsRules: {
|
|
2
2
|
readonly 'ts-restrictions/no-restricted-syntax': "off";
|
|
3
3
|
readonly 'ts-restrictions/no-restricted-cast-name': "off";
|
|
4
|
+
readonly 'ts-restrictions/no-string-spread': "error";
|
|
4
5
|
readonly 'ts-restrictions/no-unnecessary-array-from': "error";
|
|
5
6
|
readonly 'ts-restrictions/no-unnecessary-coalesce-undefined': "error";
|
|
7
|
+
readonly 'ts-restrictions/prefer-curried-call': "error";
|
|
6
8
|
readonly 'ts-restrictions/prefer-non-mutating-array-method': "error";
|
|
7
9
|
readonly 'ts-restrictions/check-destructuring-completeness': 2 | 1;
|
|
8
10
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eslint-ts-restrictions-rules.d.mts","sourceRoot":"","sources":["../../src/rules/eslint-ts-restrictions-rules.mts"],"names":[],"mappings":"AAKA,eAAO,MAAM,yBAAyB
|
|
1
|
+
{"version":3,"file":"eslint-ts-restrictions-rules.d.mts","sourceRoot":"","sources":["../../src/rules/eslint-ts-restrictions-rules.mts"],"names":[],"mappings":"AAKA,eAAO,MAAM,yBAAyB;;;;;;;;;CAUQ,CAAC"}
|
|
@@ -3,8 +3,10 @@ import { withDefaultOption } from '../types/rule-severity-with-default-option.mj
|
|
|
3
3
|
const eslintTsRestrictionsRules = {
|
|
4
4
|
'ts-restrictions/no-restricted-syntax': 'off',
|
|
5
5
|
'ts-restrictions/no-restricted-cast-name': 'off',
|
|
6
|
+
'ts-restrictions/no-string-spread': 'error',
|
|
6
7
|
'ts-restrictions/no-unnecessary-array-from': 'error',
|
|
7
8
|
'ts-restrictions/no-unnecessary-coalesce-undefined': 'error',
|
|
9
|
+
'ts-restrictions/prefer-curried-call': 'error',
|
|
8
10
|
'ts-restrictions/prefer-non-mutating-array-method': 'error',
|
|
9
11
|
'ts-restrictions/check-destructuring-completeness': withDefaultOption('error'),
|
|
10
12
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eslint-ts-restrictions-rules.mjs","sources":["../../src/rules/eslint-ts-restrictions-rules.mts"],"sourcesContent":[null],"names":[],"mappings":";;AAKO,MAAM,yBAAyB,GAAG;AACvC,IAAA,sCAAsC,EAAE,KAAK;AAC7C,IAAA,yCAAyC,EAAE,KAAK;AAChD,IAAA,2CAA2C,EAAE,OAAO;AACpD,IAAA,mDAAmD,EAAE,OAAO;AAC5D,IAAA,kDAAkD,EAAE,OAAO;AAC3D,IAAA,kDAAkD,EAChD,iBAAiB,CAAC,OAAO,CAAC;;;;;"}
|
|
1
|
+
{"version":3,"file":"eslint-ts-restrictions-rules.mjs","sources":["../../src/rules/eslint-ts-restrictions-rules.mts"],"sourcesContent":[null],"names":[],"mappings":";;AAKO,MAAM,yBAAyB,GAAG;AACvC,IAAA,sCAAsC,EAAE,KAAK;AAC7C,IAAA,yCAAyC,EAAE,KAAK;AAChD,IAAA,kCAAkC,EAAE,OAAO;AAC3C,IAAA,2CAA2C,EAAE,OAAO;AACpD,IAAA,mDAAmD,EAAE,OAAO;AAC5D,IAAA,qCAAqC,EAAE,OAAO;AAC9C,IAAA,kDAAkD,EAAE,OAAO;AAC3D,IAAA,kDAAkD,EAChD,iBAAiB,CAAC,OAAO,CAAC;;;;;"}
|
|
@@ -204,6 +204,19 @@ declare namespace NoRestrictedSyntax {
|
|
|
204
204
|
}>)[];
|
|
205
205
|
type RuleEntry = 'off' | Linter.Severity | SpreadOptionsIfIsArray<readonly [Linter.StringSeverity, Options]>;
|
|
206
206
|
}
|
|
207
|
+
/**
|
|
208
|
+
* @description Disallow spreading a `string` (e.g. `[...str]`), which silently splits it into characters and yields a `string[]` indistinguishable from spreading an array
|
|
209
|
+
*
|
|
210
|
+
* ```md
|
|
211
|
+
* | key | value |
|
|
212
|
+
* | :--------- | :------ |
|
|
213
|
+
* | type | problem |
|
|
214
|
+
* | deprecated | false |
|
|
215
|
+
* ```
|
|
216
|
+
*/
|
|
217
|
+
declare namespace NoStringSpread {
|
|
218
|
+
type RuleEntry = Linter.StringSeverity;
|
|
219
|
+
}
|
|
207
220
|
/**
|
|
208
221
|
* @description Disallow wrapping an array in `Array.from()` before a non-mutating array method (e.g. `Array.from(x).toSorted()`), since the method already returns a new array
|
|
209
222
|
*
|
|
@@ -232,6 +245,20 @@ declare namespace NoUnnecessaryArrayFrom {
|
|
|
232
245
|
declare namespace NoUnnecessaryCoalesceUndefined {
|
|
233
246
|
type RuleEntry = Linter.StringSeverity;
|
|
234
247
|
}
|
|
248
|
+
/**
|
|
249
|
+
* @description Replace a redundant arrow wrapper with the function itself (`(a) => f(a)` → `f`) or, when the callee has a curried version, its curried form (`(a) => f(a, ...rest)` → `f(...rest)`).
|
|
250
|
+
*
|
|
251
|
+
* ```md
|
|
252
|
+
* | key | value |
|
|
253
|
+
* | :--------- | :--------- |
|
|
254
|
+
* | type | suggestion |
|
|
255
|
+
* | deprecated | false |
|
|
256
|
+
* | fixable | code |
|
|
257
|
+
* ```
|
|
258
|
+
*/
|
|
259
|
+
declare namespace PreferCurriedCall {
|
|
260
|
+
type RuleEntry = Linter.StringSeverity;
|
|
261
|
+
}
|
|
235
262
|
/**
|
|
236
263
|
* @description Disallow calling a mutating array method on a defensive `Array.from()` copy of an array (e.g. `Array.from(x).sort()`); use the non-mutating counterpart on the original array instead (e.g. `x.toSorted()`)
|
|
237
264
|
*
|
|
@@ -250,8 +277,10 @@ export type EslintTsRestrictionsRules = Readonly<{
|
|
|
250
277
|
'ts-restrictions/check-destructuring-completeness': CheckDestructuringCompleteness.RuleEntry;
|
|
251
278
|
'ts-restrictions/no-restricted-cast-name': NoRestrictedCastName.RuleEntry;
|
|
252
279
|
'ts-restrictions/no-restricted-syntax': NoRestrictedSyntax.RuleEntry;
|
|
280
|
+
'ts-restrictions/no-string-spread': NoStringSpread.RuleEntry;
|
|
253
281
|
'ts-restrictions/no-unnecessary-array-from': NoUnnecessaryArrayFrom.RuleEntry;
|
|
254
282
|
'ts-restrictions/no-unnecessary-coalesce-undefined': NoUnnecessaryCoalesceUndefined.RuleEntry;
|
|
283
|
+
'ts-restrictions/prefer-curried-call': PreferCurriedCall.RuleEntry;
|
|
255
284
|
'ts-restrictions/prefer-non-mutating-array-method': PreferNonMutatingArrayMethod.RuleEntry;
|
|
256
285
|
}>;
|
|
257
286
|
export type EslintTsRestrictionsRulesOption = Readonly<{
|