eslint-plugin-ts-std-forge 0.1.8 → 0.1.9
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/rule-types.d.mts +2 -1
- package/dist/rule-types.d.mts.map +1 -1
- package/dist/rule-types.mjs.map +1 -1
- package/dist/rules/ast-utils.d.mts +8 -5
- package/dist/rules/ast-utils.d.mts.map +1 -1
- package/dist/rules/ast-utils.mjs +14 -13
- package/dist/rules/ast-utils.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 +4 -9
- package/dist/rules/prefer-is-record-and-has-key.mjs.map +1 -1
- package/dist/rules/prefer-safe-array-length-guard.d.mts.map +1 -1
- package/dist/rules/prefer-safe-array-length-guard.mjs +4 -10
- package/dist/rules/prefer-safe-array-length-guard.mjs.map +1 -1
- package/dist/rules/prefer-safe-number-parse-integer.mjs +4 -3
- package/dist/rules/prefer-safe-number-parse-integer.mjs.map +1 -1
- package/dist/rules/prefer-safe-number-parse.mjs +4 -3
- package/dist/rules/prefer-safe-number-parse.mjs.map +1 -1
- package/package.json +2 -2
- package/src/rule-types.mts +2 -1
- package/src/rules/ast-utils.mts +20 -21
- package/src/rules/prefer-is-record-and-has-key.mts +10 -15
- package/src/rules/prefer-is-record-and-has-key.test.mts +30 -3
- package/src/rules/prefer-safe-array-length-guard.mts +11 -16
- package/src/rules/prefer-safe-array-length-guard.test.mts +19 -5
- package/src/rules/prefer-safe-number-parse-integer.mts +8 -8
- package/src/rules/prefer-safe-number-parse-integer.test.mts +19 -5
- package/src/rules/prefer-safe-number-parse.mts +8 -8
- package/src/rules/prefer-safe-number-parse.test.mts +19 -5
package/dist/rule-types.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Linter } from 'eslint';
|
|
2
|
+
import type { ReadonlyRecord } from 'ts-type-forge';
|
|
2
3
|
/**
|
|
3
4
|
* @description Replace `typeof u === "object" && u !== null` with `isNonNullObject(u)` from ts-std-forge.
|
|
4
5
|
*
|
|
@@ -91,6 +92,6 @@ export type EslintTsStdForgeRules = Readonly<{
|
|
|
91
92
|
'ts-std-forge/prefer-safe-number-parse': PreferSafeNumberParse.RuleEntry;
|
|
92
93
|
'ts-std-forge/prefer-safe-number-parse-integer': PreferSafeNumberParseInteger.RuleEntry;
|
|
93
94
|
}>;
|
|
94
|
-
export type EslintTsStdForgeRulesOption =
|
|
95
|
+
export type EslintTsStdForgeRulesOption = ReadonlyRecord<never, never>;
|
|
95
96
|
export {};
|
|
96
97
|
//# sourceMappingURL=rule-types.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rule-types.d.mts","sourceRoot":"","sources":["../src/rule-types.mts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"rule-types.d.mts","sourceRoot":"","sources":["../src/rule-types.mts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGpD;;;;;;;;;;GAUG;AACH,kBAAU,qBAAqB,CAAC;IAC9B,KAAY,SAAS,GAAG,MAAM,CAAC,cAAc,CAAC;CAC/C;AAED;;;;;;;;;;GAUG;AACH,kBAAU,uBAAuB,CAAC;IAChC,KAAY,SAAS,GAAG,MAAM,CAAC,cAAc,CAAC;CAC/C;AAED;;;;;;;;;;GAUG;AACH,kBAAU,sBAAsB,CAAC;IAC/B,KAAY,SAAS,GAAG,MAAM,CAAC,cAAc,CAAC;CAC/C;AAED;;;;;;;;;;GAUG;AACH,kBAAU,0BAA0B,CAAC;IACnC,KAAY,SAAS,GAAG,MAAM,CAAC,cAAc,CAAC;CAC/C;AAED;;;;;;;;;;GAUG;AACH,kBAAU,qBAAqB,CAAC;IAC9B,KAAY,SAAS,GAAG,MAAM,CAAC,cAAc,CAAC;CAC/C;AAED;;;;;;;;;;GAUG;AACH,kBAAU,4BAA4B,CAAC;IACrC,KAAY,SAAS,GAAG,MAAM,CAAC,cAAc,CAAC;CAC/C;AAED,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC;IAC3C,wCAAwC,EAAE,qBAAqB,CAAC,SAAS,CAAC;IAC1E,2CAA2C,EAAE,uBAAuB,CAAC,SAAS,CAAC;IAC/E,yCAAyC,EAAE,sBAAsB,CAAC,SAAS,CAAC;IAC5E,6CAA6C,EAAE,0BAA0B,CAAC,SAAS,CAAC;IACpF,uCAAuC,EAAE,qBAAqB,CAAC,SAAS,CAAC;IACzE,+CAA+C,EAAE,4BAA4B,CAAC,SAAS,CAAC;CACzF,CAAC,CAAC;AAEH,MAAM,MAAM,2BAA2B,GAAG,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC"}
|
package/dist/rule-types.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rule-types.mjs","sourceRoot":"","sources":["../src/rule-types.mts"],"names":[],"mappings":"AAAA,oBAAoB;AAMpB,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"rule-types.mjs","sourceRoot":"","sources":["../src/rule-types.mts"],"names":[],"mappings":"AAAA,oBAAoB;AAMpB,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAyG1C,8EAA8E;AAC9E,8DAA8D;AAC9D,UAAU,CAGR,GAAG,CAAC,CAAC"}
|
|
@@ -6,10 +6,13 @@ import type * as ts from 'typescript';
|
|
|
6
6
|
*/
|
|
7
7
|
export declare const isIdentifierNamed: (node: TSESTree.Node, name: string) => boolean;
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* information. A
|
|
12
|
-
*
|
|
9
|
+
* Whether `holds` is true of the type of every layer of type wrappers around
|
|
10
|
+
* `node` (see {@link typeWrapperLayers}), outermost to innermost; `false`
|
|
11
|
+
* without type information. A widening cast (`s as string | number`) is the
|
|
12
|
+
* author saying the value may be more than it is, and is respected; a
|
|
13
|
+
* narrowing one (`s!`, `v as unknown as string`) cannot vouch for what the
|
|
14
|
+
* value's own type does not, and the fix, which keeps the wrapper, has to
|
|
15
|
+
* type-check at every layer too.
|
|
13
16
|
*/
|
|
14
|
-
export declare const
|
|
17
|
+
export declare const typeHoldsAtEveryLayer: (sourceCode: TSESLint.SourceCode, node: TSESTree.Node, holds: (type: ts.Type, typeChecker: ts.TypeChecker) => boolean) => boolean;
|
|
15
18
|
//# sourceMappingURL=ast-utils.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ast-utils.d.mts","sourceRoot":"","sources":["../../src/rules/ast-utils.mts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,QAAQ,EACb,KAAK,QAAQ,EACd,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AAEtC;;;GAGG;AACH,eAAO,MAAM,iBAAiB,SAEtB,QAAQ,CAAC,IAAI,QACb,MAAM,KACX,OAMF,CAAC;AAEF
|
|
1
|
+
{"version":3,"file":"ast-utils.d.mts","sourceRoot":"","sources":["../../src/rules/ast-utils.mts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,QAAQ,EACb,KAAK,QAAQ,EACd,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AAEtC;;;GAGG;AACH,eAAO,MAAM,iBAAiB,SAEtB,QAAQ,CAAC,IAAI,QACb,MAAM,KACX,OAMF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,eAEpB,QAAQ,CAAC,UAAU,QAEzB,QAAQ,CAAC,IAAI,SAEZ,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,CAAC,WAAW,KAAK,OAAO,KAC7D,OAgBF,CAAC"}
|
package/dist/rules/ast-utils.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AST_NODE_TYPES, } from '@typescript-eslint/utils';
|
|
2
|
-
import {
|
|
2
|
+
import { skipTypeWrappers, typeWrapperLayers } from 'eslint-rule-forge';
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
|
|
@@ -16,23 +16,24 @@ node, name) => {
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
export const typeHoldsAtEveryLayer = (
|
|
20
23
|
|
|
21
24
|
sourceCode,
|
|
22
25
|
|
|
23
|
-
node
|
|
26
|
+
node,
|
|
27
|
+
|
|
28
|
+
holds) => {
|
|
24
29
|
const services = sourceCode.parserServices;
|
|
25
30
|
const checker = services?.program?.getTypeChecker();
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return undefined;
|
|
31
|
+
if (checker === undefined) {
|
|
32
|
+
return false;
|
|
29
33
|
}
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
return typeWrapperLayers(node).every((layer) => {
|
|
35
|
+
const tsNode = services?.esTreeNodeToTSNodeMap?.get(layer);
|
|
36
|
+
return (tsNode !== undefined && holds(checker.getTypeAtLocation(tsNode), checker));
|
|
37
|
+
});
|
|
32
38
|
};
|
|
33
|
-
const hasNonNullAssertion = (
|
|
34
|
-
|
|
35
|
-
node) => isTypeWrapper(node) &&
|
|
36
|
-
(node.type === AST_NODE_TYPES.TSNonNullExpression ||
|
|
37
|
-
hasNonNullAssertion(node.expression));
|
|
38
39
|
//# sourceMappingURL=ast-utils.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ast-utils.mjs","sourceRoot":"","sources":["../../src/rules/ast-utils.mts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,GAGf,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"ast-utils.mjs","sourceRoot":"","sources":["../../src/rules/ast-utils.mts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,GAGf,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAGxE;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG;AAC/B,8EAA8E;AAC9E,IAAmB,EACnB,IAAY,EACH,EAAE;IACX,MAAM,SAAS,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAEzC,OAAO,CACL,SAAS,CAAC,IAAI,KAAK,cAAc,CAAC,UAAU,IAAI,SAAS,CAAC,IAAI,KAAK,IAAI,CACxE,CAAC;AACJ,CAAC,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG;AACnC,8EAA8E;AAC9E,UAA+B;AAC/B,8EAA8E;AAC9E,IAAmB;AACnB,8EAA8E;AAC9E,KAA8D,EACrD,EAAE;IACX,MAAM,QAAQ,GAAG,UAAU,CAAC,cAAc,CAAC;IAE3C,MAAM,OAAO,GAAG,QAAQ,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC;IAEpD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;QAC7C,MAAM,MAAM,GAAG,QAAQ,EAAE,qBAAqB,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;QAE3D,OAAO,CACL,MAAM,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,CAAC,iBAAiB,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,CAC1E,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prefer-is-record-and-has-key.d.mts","sourceRoot":"","sources":["../../src/rules/prefer-is-record-and-has-key.mts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,QAAQ,EAEd,MAAM,0BAA0B,CAAC;AAalC,KAAK,OAAO,GAAG,SAAS,EAAE,CAAC;AAE3B,KAAK,UAAU,GAAG,sBAAsB,GAAG,WAAW,CAAC;AAEvD,eAAO,MAAM,uBAAuB,EAAE,QAAQ,CAAC,UAAU,CAAC,UAAU,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"prefer-is-record-and-has-key.d.mts","sourceRoot":"","sources":["../../src/rules/prefer-is-record-and-has-key.mts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,QAAQ,EAEd,MAAM,0BAA0B,CAAC;AAalC,KAAK,OAAO,GAAG,SAAS,EAAE,CAAC;AAE3B,KAAK,UAAU,GAAG,sBAAsB,GAAG,WAAW,CAAC;AAEvD,eAAO,MAAM,uBAAuB,EAAE,QAAQ,CAAC,UAAU,CAAC,UAAU,EAAE,OAAO,CAiLjE,CAAC"}
|
|
@@ -2,7 +2,7 @@ import { AST_NODE_TYPES, } from '@typescript-eslint/utils';
|
|
|
2
2
|
import { buildNamedImportFix, getArgumentText, getImportDeclarations, getNamedImports, skipTypeWrappers, } from 'eslint-rule-forge';
|
|
3
3
|
import { SafeArray } from 'ts-std-forge';
|
|
4
4
|
import * as ts from 'typescript';
|
|
5
|
-
import {
|
|
5
|
+
import { isIdentifierNamed, typeHoldsAtEveryLayer } from './ast-utils.mjs';
|
|
6
6
|
import { TS_STD_FORGE_MODULE } from './constants.mjs';
|
|
7
7
|
export const preferIsRecordAndHasKey = {
|
|
8
8
|
meta: {
|
|
@@ -21,7 +21,8 @@ export const preferIsRecordAndHasKey = {
|
|
|
21
21
|
const sourceCode = context.sourceCode;
|
|
22
22
|
const program = sourceCode.ast;
|
|
23
23
|
const tsStdForgeImports = getImportDeclarations(program, TS_STD_FORGE_MODULE);
|
|
24
|
-
|
|
24
|
+
|
|
25
|
+
|
|
25
26
|
|
|
26
27
|
|
|
27
28
|
|
|
@@ -31,13 +32,7 @@ export const preferIsRecordAndHasKey = {
|
|
|
31
32
|
|
|
32
33
|
const isKnownRecord = (
|
|
33
34
|
|
|
34
|
-
expression) =>
|
|
35
|
-
if (checker === undefined) {
|
|
36
|
-
return false;
|
|
37
|
-
}
|
|
38
|
-
const type = getValueType(sourceCode, expression);
|
|
39
|
-
return type !== undefined && isAlreadyRecordType(checker, type);
|
|
40
|
-
};
|
|
35
|
+
expression) => typeHoldsAtEveryLayer(sourceCode, expression, (type, checker) => isAlreadyRecordType(checker, type));
|
|
41
36
|
const mut_nodesToFix = [];
|
|
42
37
|
return {
|
|
43
38
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prefer-is-record-and-has-key.mjs","sourceRoot":"","sources":["../../src/rules/prefer-is-record-and-has-key.mts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,GAGf,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,qBAAqB,EACrB,eAAe,EACf,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"prefer-is-record-and-has-key.mjs","sourceRoot":"","sources":["../../src/rules/prefer-is-record-and-has-key.mts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prefer-safe-array-length-guard.d.mts","sourceRoot":"","sources":["../../src/rules/prefer-safe-array-length-guard.mts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,QAAQ,EAEd,MAAM,0BAA0B,CAAC;AAYlC,KAAK,OAAO,GAAG,SAAS,EAAE,CAAC;AAE3B,KAAK,UAAU,GAAG,YAAY,GAAG,eAAe,CAAC;AAwCjD,eAAO,MAAM,0BAA0B,EAAE,QAAQ,CAAC,UAAU,CAC1D,UAAU,EACV,OAAO,
|
|
1
|
+
{"version":3,"file":"prefer-safe-array-length-guard.d.mts","sourceRoot":"","sources":["../../src/rules/prefer-safe-array-length-guard.mts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,QAAQ,EAEd,MAAM,0BAA0B,CAAC;AAYlC,KAAK,OAAO,GAAG,SAAS,EAAE,CAAC;AAE3B,KAAK,UAAU,GAAG,YAAY,GAAG,eAAe,CAAC;AAwCjD,eAAO,MAAM,0BAA0B,EAAE,QAAQ,CAAC,UAAU,CAC1D,UAAU,EACV,OAAO,CA2GC,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AST_NODE_TYPES, } from '@typescript-eslint/utils';
|
|
2
2
|
import { buildNamedImportFix, getArgumentText, getImportDeclarations, getNamedImports, skipTypeWrappers, } from 'eslint-rule-forge';
|
|
3
|
-
import {
|
|
3
|
+
import { typeHoldsAtEveryLayer } from './ast-utils.mjs';
|
|
4
4
|
import { TS_STD_FORGE_MODULE } from './constants.mjs';
|
|
5
5
|
|
|
6
6
|
|
|
@@ -48,7 +48,8 @@ export const preferSafeArrayLengthGuard = {
|
|
|
48
48
|
const sourceCode = context.sourceCode;
|
|
49
49
|
const program = sourceCode.ast;
|
|
50
50
|
const tsStdForgeImports = getImportDeclarations(program, TS_STD_FORGE_MODULE);
|
|
51
|
-
|
|
51
|
+
|
|
52
|
+
|
|
52
53
|
|
|
53
54
|
|
|
54
55
|
|
|
@@ -57,14 +58,7 @@ export const preferSafeArrayLengthGuard = {
|
|
|
57
58
|
|
|
58
59
|
const isArrayLike = (
|
|
59
60
|
|
|
60
|
-
expression) =>
|
|
61
|
-
if (checker === undefined) {
|
|
62
|
-
return false;
|
|
63
|
-
}
|
|
64
|
-
const type = getValueType(sourceCode, expression);
|
|
65
|
-
return (type !== undefined &&
|
|
66
|
-
(checker.isArrayType(type) || checker.isTupleType(type)));
|
|
67
|
-
};
|
|
61
|
+
expression) => typeHoldsAtEveryLayer(sourceCode, expression, (type, checker) => checker.isArrayType(type) || checker.isTupleType(type));
|
|
68
62
|
const mut_nodesToFix = [];
|
|
69
63
|
return {
|
|
70
64
|
BinaryExpression: (node) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prefer-safe-array-length-guard.mjs","sourceRoot":"","sources":["../../src/rules/prefer-safe-array-length-guard.mts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,GAGf,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,qBAAqB,EACrB,eAAe,EACf,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"prefer-safe-array-length-guard.mjs","sourceRoot":"","sources":["../../src/rules/prefer-safe-array-length-guard.mts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,GAGf,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,qBAAqB,EACrB,eAAe,EACf,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAQtD;;;;;;;;;GASG;AACH,MAAM,WAAW,GAAG;IAClB,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE;IAC/C,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE;IAClD,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE;IAChD,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE;CAK/C,CAAC;AAEL,wEAAwE;AACxE,MAAM,OAAO,GAAmC;IAC9C,KAAK,EAAE,KAAK;IACZ,KAAK,EAAE,KAAK;IACZ,GAAG,EAAE,GAAG;IACR,GAAG,EAAE,GAAG;IACR,IAAI,EAAE,IAAI;IACV,IAAI,EAAE,IAAI;CACF,CAAC;AAEX,MAAM,WAAW,GAAsC;IACrD,OAAO,EAAE,YAAY;IACrB,UAAU,EAAE,eAAe;CACnB,CAAC;AAEX,MAAM,CAAC,MAAM,0BAA0B,GAGnC;IACF,IAAI,EAAE;QACJ,IAAI,EAAE,YAAY;QAClB,IAAI,EAAE;YACJ,WAAW,EACT,+LAA+L;SAClM;QACD,OAAO,EAAE,MAAM;QACf,MAAM,EAAE,EAAE;QACV,QAAQ,EAAE;YACR,UAAU,EACR,uJAAuJ;YACzJ,aAAa,EACX,iKAAiK;SACpK;KACF;IAED,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE;QAClB,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QAEtC,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC;QAE/B,MAAM,iBAAiB,GAAG,qBAAqB,CAC7C,OAAO,EACP,mBAAmB,CACpB,CAAC;QAEF;;;;;;;WAOG;QACH,MAAM,WAAW,GAAG;QAClB,8EAA8E;QAC9E,UAA+B,EACtB,EAAE,CACX,qBAAqB,CACnB,UAAU,EACV,UAAU,EACV,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,CAChB,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CACzD,CAAC;QAEJ,MAAM,cAAc,GAId,EAAE,CAAC;QAET,OAAO;YACL,gBAAgB,EAAE,CAAC,IAAI,EAAE,EAAE;gBACzB,MAAM,OAAO,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;gBAE5C,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;oBAC1B,OAAO;gBACT,CAAC;gBAED,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,eAAe,CAAC,EAAE,CAAC;oBAC1C,OAAO;gBACT,CAAC;gBAED,cAAc,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;YAC5C,CAAC;YACD,cAAc,EAAE,GAAG,EAAE;gBACnB,MAAM,kBAAkB,GACtB,eAAe,CAAC,iBAAiB,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;gBAE3D,iDAAiD;gBACjD,6DAA6D;gBAC7D,sEAAsE;gBACtE,qEAAqE;gBACrE,WAAW;gBACX,KAAK,MAAM,CACT,KAAK,EACL,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,EACjC,IAAI,cAAc,CAAC,OAAO,EAAE,EAAE,CAAC;oBAC9B,MAAM,SAAS,GAAG,eAAe,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;oBAE/D,OAAO,CAAC,MAAM,CAAC;wBACb,IAAI;wBACJ,SAAS,EAAE,WAAW,CAAC,KAAK,CAAC;wBAC7B,IAAI,EAAE;4BACJ,QAAQ,EAAE,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC;4BAClC,SAAS,EAAE,SAAS;yBACrB;wBACD,GAAG,EAAE,CAAC,KAAK,EAAE,EAAE;4BACb,MAAM,WAAW,GAAG,aAAa,KAAK,IAAI,SAAS,GAAG,CAAC;4BAEvD,MAAM,WAAW,GACf,KAAK,KAAK,CAAC,IAAI,CAAC,kBAAkB;gCAChC,CAAC,CAAC,mBAAmB,CAAC,KAAK,EAAE,OAAO,EAAE,mBAAmB,EAAE;oCACvD,WAAW;iCACZ,CAAC;gCACJ,CAAC,CAAC,EAAE,CAAC;4BAET,OAAO,CAAC,GAAG,WAAW,EAAE,KAAK,CAAC,WAAW,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC;wBAChE,CAAC;qBACF,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;IACD,cAAc,EAAE,EAAE;CACV,CAAC;AAEX;;;;GAIG;AACH,MAAM,qBAAqB,GAAG;AAC5B,6EAA6E;AAC7E,kEAAkE;AAClE,8EAA8E;AAC9E,IAA+B,EAGnB,EAAE;IACd,2EAA2E;IAC3E,oEAAoE;IACpE,MAAM,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEzC,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAE3C,MAAM,YAAY,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IAE1C,MAAM,UAAU,GAAG,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;IAE/C,MAAM,SAAS,GAAG,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IAE9C,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,CAAC;QAChC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,uEAAuE;IACvE,8CAA8C;IAC9C,IACE,SAAS,CAAC,IAAI,KAAK,cAAc,CAAC,OAAO;QACzC,OAAO,SAAS,CAAC,KAAK,KAAK,QAAQ,EACnC,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,QAAQ,GAAG,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAEvE,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAC9B,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,KAAK,QAAQ,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,KAAK,CAC1E,CAAC;IAEF,OAAO,OAAO,KAAK,SAAS;QAC1B,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,EAAE,eAAe,EAAE,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC;AACnE,CAAC,CAAC;AAEF,MAAM,cAAc,GAAG;AACrB,8EAA8E;AAC9E,IAAmB,EACgB,EAAE,CACrC,IAAI,CAAC,IAAI,KAAK,cAAc,CAAC,gBAAgB;IAC7C,CAAC,IAAI,CAAC,QAAQ;IACd,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,cAAc,CAAC,UAAU;IAChD,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,QAAQ,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AST_NODE_TYPES, } from '@typescript-eslint/utils';
|
|
2
2
|
import { buildNamedImportFix, getArgumentText, getImportDeclarations, getNamedImports, skipTypeWrappers, } from 'eslint-rule-forge';
|
|
3
3
|
import * as ts from 'typescript';
|
|
4
|
-
import {
|
|
4
|
+
import { isIdentifierNamed, typeHoldsAtEveryLayer } from './ast-utils.mjs';
|
|
5
5
|
import { TS_STD_FORGE_MODULE } from './constants.mjs';
|
|
6
6
|
|
|
7
7
|
|
|
@@ -74,8 +74,9 @@ export const preferSafeNumberParseInteger = {
|
|
|
74
74
|
|
|
75
75
|
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
if (!typeHoldsAtEveryLayer(sourceCode, firstArg, isStringType)) {
|
|
79
80
|
return;
|
|
80
81
|
}
|
|
81
82
|
mut_nodesToFix.push({ node, argExpression: firstArg });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prefer-safe-number-parse-integer.mjs","sourceRoot":"","sources":["../../src/rules/prefer-safe-number-parse-integer.mts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,GAGf,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,qBAAqB,EACrB,eAAe,EACf,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"prefer-safe-number-parse-integer.mjs","sourceRoot":"","sources":["../../src/rules/prefer-safe-number-parse-integer.mts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AST_NODE_TYPES, } from '@typescript-eslint/utils';
|
|
2
2
|
import { buildNamedImportFix, getArgumentText, getImportDeclarations, getNamedImports, skipTypeWrappers, } from 'eslint-rule-forge';
|
|
3
3
|
import * as ts from 'typescript';
|
|
4
|
-
import {
|
|
4
|
+
import { isIdentifierNamed, typeHoldsAtEveryLayer } from './ast-utils.mjs';
|
|
5
5
|
import { TS_STD_FORGE_MODULE } from './constants.mjs';
|
|
6
6
|
|
|
7
7
|
|
|
@@ -75,8 +75,9 @@ export const preferSafeNumberParse = {
|
|
|
75
75
|
|
|
76
76
|
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
if (!typeHoldsAtEveryLayer(sourceCode, firstArg, isStringType)) {
|
|
80
81
|
return;
|
|
81
82
|
}
|
|
82
83
|
mut_nodesToFix.push({ node, argExpression: firstArg });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prefer-safe-number-parse.mjs","sourceRoot":"","sources":["../../src/rules/prefer-safe-number-parse.mts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,GAGf,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,qBAAqB,EACrB,eAAe,EACf,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"prefer-safe-number-parse.mjs","sourceRoot":"","sources":["../../src/rules/prefer-safe-number-parse.mts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-ts-std-forge",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "ESLint rules that steer code toward ts-std-forge idioms.",
|
|
6
6
|
"keywords": [
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"@typescript-eslint/parser": "8.71.1",
|
|
44
44
|
"@typescript-eslint/rule-tester": "8.71.1",
|
|
45
45
|
"dedent": "1.7.2",
|
|
46
|
-
"eslint-config-typed": "5.
|
|
46
|
+
"eslint-config-typed": "5.18.0",
|
|
47
47
|
"eslint-plugin-ts-fortress": "1.3.3",
|
|
48
48
|
"eslint-plugin-ts-type-forge": "0.8.8",
|
|
49
49
|
"prettier": "3.9.9",
|
package/src/rule-types.mts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import type { Linter } from 'eslint';
|
|
7
7
|
import { expectType } from 'ts-std-forge';
|
|
8
|
+
import type { ReadonlyRecord } from 'ts-type-forge';
|
|
8
9
|
import type { tsStdForgeRules } from './rules/index.mjs';
|
|
9
10
|
|
|
10
11
|
/**
|
|
@@ -106,7 +107,7 @@ export type EslintTsStdForgeRules = Readonly<{
|
|
|
106
107
|
'ts-std-forge/prefer-safe-number-parse-integer': PreferSafeNumberParseInteger.RuleEntry;
|
|
107
108
|
}>;
|
|
108
109
|
|
|
109
|
-
export type EslintTsStdForgeRulesOption =
|
|
110
|
+
export type EslintTsStdForgeRulesOption = ReadonlyRecord<never, never>;
|
|
110
111
|
|
|
111
112
|
// If this assertion fails to type-check, this generated file has drifted from
|
|
112
113
|
// the actual plugin rules — re-run `pnpm run gen:rule-types`.
|
package/src/rules/ast-utils.mts
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
type TSESLint,
|
|
4
4
|
type TSESTree,
|
|
5
5
|
} from '@typescript-eslint/utils';
|
|
6
|
-
import {
|
|
6
|
+
import { skipTypeWrappers, typeWrapperLayers } from 'eslint-rule-forge';
|
|
7
7
|
import type * as ts from 'typescript';
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -23,36 +23,35 @@ export const isIdentifierNamed = (
|
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* information. A
|
|
29
|
-
*
|
|
26
|
+
* Whether `holds` is true of the type of every layer of type wrappers around
|
|
27
|
+
* `node` (see {@link typeWrapperLayers}), outermost to innermost; `false`
|
|
28
|
+
* without type information. A widening cast (`s as string | number`) is the
|
|
29
|
+
* author saying the value may be more than it is, and is respected; a
|
|
30
|
+
* narrowing one (`s!`, `v as unknown as string`) cannot vouch for what the
|
|
31
|
+
* value's own type does not, and the fix, which keeps the wrapper, has to
|
|
32
|
+
* type-check at every layer too.
|
|
30
33
|
*/
|
|
31
|
-
export const
|
|
34
|
+
export const typeHoldsAtEveryLayer = (
|
|
32
35
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
33
36
|
sourceCode: TSESLint.SourceCode,
|
|
34
37
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
35
38
|
node: TSESTree.Node,
|
|
36
|
-
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
40
|
+
holds: (type: ts.Type, typeChecker: ts.TypeChecker) => boolean,
|
|
41
|
+
): boolean => {
|
|
37
42
|
const services = sourceCode.parserServices;
|
|
38
43
|
|
|
39
44
|
const checker = services?.program?.getTypeChecker();
|
|
40
45
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (checker === undefined || tsNode === undefined) {
|
|
44
|
-
return undefined;
|
|
46
|
+
if (checker === undefined) {
|
|
47
|
+
return false;
|
|
45
48
|
}
|
|
46
49
|
|
|
47
|
-
|
|
50
|
+
return typeWrapperLayers(node).every((layer) => {
|
|
51
|
+
const tsNode = services?.esTreeNodeToTSNodeMap?.get(layer);
|
|
48
52
|
|
|
49
|
-
|
|
53
|
+
return (
|
|
54
|
+
tsNode !== undefined && holds(checker.getTypeAtLocation(tsNode), checker)
|
|
55
|
+
);
|
|
56
|
+
});
|
|
50
57
|
};
|
|
51
|
-
|
|
52
|
-
const hasNonNullAssertion = (
|
|
53
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
54
|
-
node: TSESTree.Node,
|
|
55
|
-
): boolean =>
|
|
56
|
-
isTypeWrapper(node) &&
|
|
57
|
-
(node.type === AST_NODE_TYPES.TSNonNullExpression ||
|
|
58
|
-
hasNonNullAssertion(node.expression));
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from 'eslint-rule-forge';
|
|
13
13
|
import { SafeArray } from 'ts-std-forge';
|
|
14
14
|
import * as ts from 'typescript';
|
|
15
|
-
import {
|
|
15
|
+
import { isIdentifierNamed, typeHoldsAtEveryLayer } from './ast-utils.mjs';
|
|
16
16
|
import { TS_STD_FORGE_MODULE } from './constants.mjs';
|
|
17
17
|
|
|
18
18
|
type Options = readonly [];
|
|
@@ -47,27 +47,22 @@ export const preferIsRecordAndHasKey: TSESLint.RuleModule<MessageIds, Options> =
|
|
|
47
47
|
TS_STD_FORGE_MODULE,
|
|
48
48
|
);
|
|
49
49
|
|
|
50
|
-
const checker = sourceCode.parserServices?.program?.getTypeChecker();
|
|
51
|
-
|
|
52
50
|
/**
|
|
53
51
|
* Whether the `isRecord(...)` half of the rewrite would be dead code
|
|
54
52
|
* because the object already satisfies `hasKey`'s
|
|
55
|
-
* `R extends UnknownRecord` constraint
|
|
56
|
-
*
|
|
57
|
-
*
|
|
53
|
+
* `R extends UnknownRecord` constraint, at every layer of type
|
|
54
|
+
* wrappers: an array cast to a record is still rejected by `isRecord`,
|
|
55
|
+
* and a record widened to one that may be an array is the author saying
|
|
56
|
+
* it may need the guard. Without type information nothing is known, so
|
|
57
|
+
* the guard is kept.
|
|
58
58
|
*/
|
|
59
59
|
const isKnownRecord = (
|
|
60
60
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
61
61
|
expression: TSESTree.Expression,
|
|
62
|
-
): boolean =>
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const type = getValueType(sourceCode, expression);
|
|
68
|
-
|
|
69
|
-
return type !== undefined && isAlreadyRecordType(checker, type);
|
|
70
|
-
};
|
|
62
|
+
): boolean =>
|
|
63
|
+
typeHoldsAtEveryLayer(sourceCode, expression, (type, checker) =>
|
|
64
|
+
isAlreadyRecordType(checker, type),
|
|
65
|
+
);
|
|
71
66
|
|
|
72
67
|
const mut_nodesToFix: {
|
|
73
68
|
node: TSESTree.CallExpression | TSESTree.BinaryExpression;
|
|
@@ -125,15 +125,42 @@ describe('prefer-is-record-and-has-key through type wrappers', () => {
|
|
|
125
125
|
errors: [{ messageId: 'useHasKey' }],
|
|
126
126
|
},
|
|
127
127
|
{
|
|
128
|
-
|
|
128
|
+
// `Object.hasOwn(undefined, 'a')` throws; `isRecord` answers false.
|
|
129
|
+
name: 'keeps isRecord for `!` on an optional record, which leaves undefined in place',
|
|
129
130
|
code: dedent`
|
|
130
131
|
declare const obj: Readonly<Record<string, unknown>> | undefined;
|
|
131
132
|
const ok = Object.hasOwn(obj!, 'a');
|
|
132
133
|
`,
|
|
133
134
|
output: dedent`
|
|
134
|
-
import { hasKey } from 'ts-std-forge';
|
|
135
|
+
import { isRecord, hasKey } from 'ts-std-forge';
|
|
135
136
|
declare const obj: Readonly<Record<string, unknown>> | undefined;
|
|
136
|
-
const ok = hasKey(obj!, 'a');
|
|
137
|
+
const ok = (isRecord(obj!) && hasKey(obj!, 'a'));
|
|
138
|
+
`,
|
|
139
|
+
errors: [{ messageId: 'useIsRecordAndHasKey' }],
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: 'keeps isRecord for a record widened by `as` to one that may be an array',
|
|
143
|
+
code: dedent`
|
|
144
|
+
declare const obj: Readonly<Record<string, unknown>>;
|
|
145
|
+
const ok = Object.hasOwn(obj as Readonly<Record<string, unknown>> | readonly string[], 'a');
|
|
146
|
+
`,
|
|
147
|
+
output: dedent`
|
|
148
|
+
import { isRecord, hasKey } from 'ts-std-forge';
|
|
149
|
+
declare const obj: Readonly<Record<string, unknown>>;
|
|
150
|
+
const ok = (isRecord(obj as Readonly<Record<string, unknown>> | readonly string[]) && hasKey(obj as Readonly<Record<string, unknown>> | readonly string[], 'a'));
|
|
151
|
+
`,
|
|
152
|
+
errors: [{ messageId: 'useIsRecordAndHasKey' }],
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
name: 'drops isRecord for a record that is a record at every layer',
|
|
156
|
+
code: dedent`
|
|
157
|
+
declare const obj: Readonly<Record<string, unknown>>;
|
|
158
|
+
const ok = Object.hasOwn(obj! as Readonly<Record<string, unknown>>, 'a');
|
|
159
|
+
`,
|
|
160
|
+
output: dedent`
|
|
161
|
+
import { hasKey } from 'ts-std-forge';
|
|
162
|
+
declare const obj: Readonly<Record<string, unknown>>;
|
|
163
|
+
const ok = hasKey(obj! as Readonly<Record<string, unknown>>, 'a');
|
|
137
164
|
`,
|
|
138
165
|
errors: [{ messageId: 'useHasKey' }],
|
|
139
166
|
},
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
skipTypeWrappers,
|
|
12
12
|
} from 'eslint-rule-forge';
|
|
13
13
|
import type { ReadonlyRecord } from 'ts-type-forge';
|
|
14
|
-
import {
|
|
14
|
+
import { typeHoldsAtEveryLayer } from './ast-utils.mjs';
|
|
15
15
|
import { TS_STD_FORGE_MODULE } from './constants.mjs';
|
|
16
16
|
|
|
17
17
|
type Options = readonly [];
|
|
@@ -86,29 +86,24 @@ export const preferSafeArrayLengthGuard: TSESLint.RuleModule<
|
|
|
86
86
|
TS_STD_FORGE_MODULE,
|
|
87
87
|
);
|
|
88
88
|
|
|
89
|
-
const checker = sourceCode.parserServices?.program?.getTypeChecker();
|
|
90
|
-
|
|
91
89
|
/**
|
|
92
90
|
* Whether `expression` is an array or tuple. Without type information the
|
|
93
91
|
* answer is no: `.length` is also a string's, a function's and a
|
|
94
|
-
* `TypedArray`'s, and none of those can be handed to the guard.
|
|
95
|
-
*
|
|
92
|
+
* `TypedArray`'s, and none of those can be handed to the guard. It has to
|
|
93
|
+
* be one at every layer of type wrappers: a string cast to an array is
|
|
94
|
+
* still a string, and the fix, which keeps the wrapper, hands the guard
|
|
95
|
+
* the type the outermost layer states.
|
|
96
96
|
*/
|
|
97
97
|
const isArrayLike = (
|
|
98
98
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
99
99
|
expression: TSESTree.Expression,
|
|
100
|
-
): boolean =>
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
return (
|
|
108
|
-
type !== undefined &&
|
|
109
|
-
(checker.isArrayType(type) || checker.isTupleType(type))
|
|
100
|
+
): boolean =>
|
|
101
|
+
typeHoldsAtEveryLayer(
|
|
102
|
+
sourceCode,
|
|
103
|
+
expression,
|
|
104
|
+
(type, checker) =>
|
|
105
|
+
checker.isArrayType(type) || checker.isTupleType(type),
|
|
110
106
|
);
|
|
111
|
-
};
|
|
112
107
|
|
|
113
108
|
const mut_nodesToFix: {
|
|
114
109
|
node: TSESTree.BinaryExpression;
|
|
@@ -168,6 +168,20 @@ describe('prefer-safe-array-length-guard through type wrappers', () => {
|
|
|
168
168
|
const ok = xs.length > (2 as const);
|
|
169
169
|
`,
|
|
170
170
|
},
|
|
171
|
+
{
|
|
172
|
+
name: 'an array widened by `as` to one that may be a string',
|
|
173
|
+
code: dedent`
|
|
174
|
+
declare const xs: readonly string[];
|
|
175
|
+
const ok = (xs as readonly string[] | string).length > 0;
|
|
176
|
+
`,
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
name: '`!` on an optional array, which leaves undefined in place',
|
|
180
|
+
code: dedent`
|
|
181
|
+
declare const xs: readonly number[] | undefined;
|
|
182
|
+
const ok = xs!.length !== 0;
|
|
183
|
+
`,
|
|
184
|
+
},
|
|
171
185
|
],
|
|
172
186
|
invalid: [
|
|
173
187
|
{
|
|
@@ -210,15 +224,15 @@ describe('prefer-safe-array-length-guard through type wrappers', () => {
|
|
|
210
224
|
errors: [{ messageId: 'useIsEmpty' }],
|
|
211
225
|
},
|
|
212
226
|
{
|
|
213
|
-
name: 'keeps a wrapper on
|
|
227
|
+
name: 'keeps a wrapper on an array that is an array at every layer',
|
|
214
228
|
code: dedent`
|
|
215
|
-
declare const xs: readonly number[]
|
|
216
|
-
const ok = xs
|
|
229
|
+
declare const xs: readonly number[];
|
|
230
|
+
const ok = (xs! as readonly number[]).length !== 0;
|
|
217
231
|
`,
|
|
218
232
|
output: dedent`
|
|
219
233
|
import { SafeArray } from 'ts-std-forge';
|
|
220
|
-
declare const xs: readonly number[]
|
|
221
|
-
const ok = SafeArray.isNonEmpty(xs!);
|
|
234
|
+
declare const xs: readonly number[];
|
|
235
|
+
const ok = SafeArray.isNonEmpty(xs! as readonly number[]);
|
|
222
236
|
`,
|
|
223
237
|
errors: [{ messageId: 'useIsNonEmpty' }],
|
|
224
238
|
},
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
skipTypeWrappers,
|
|
12
12
|
} from 'eslint-rule-forge';
|
|
13
13
|
import * as ts from 'typescript';
|
|
14
|
-
import {
|
|
14
|
+
import { isIdentifierNamed, typeHoldsAtEveryLayer } from './ast-utils.mjs';
|
|
15
15
|
import { TS_STD_FORGE_MODULE } from './constants.mjs';
|
|
16
16
|
|
|
17
17
|
type Options = readonly [];
|
|
@@ -118,13 +118,13 @@ export const preferSafeNumberParseInteger: TSESLint.RuleModule<
|
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
// The argument must be a `string`
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
//
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (
|
|
121
|
+
// The argument must be a `string` at every layer of type wrappers:
|
|
122
|
+
// the value's own type is what `parseInt` sees at run time
|
|
123
|
+
// (`parseInt(1e21 as unknown as string)` is 1, the rewrite 1e21), and
|
|
124
|
+
// the type a wrapper states is what the fix, which keeps the wrapper,
|
|
125
|
+
// hands `SafeNumber.parseInteger(value: string)`. Without type
|
|
126
|
+
// information, skip.
|
|
127
|
+
if (!typeHoldsAtEveryLayer(sourceCode, firstArg, isStringType)) {
|
|
128
128
|
return;
|
|
129
129
|
}
|
|
130
130
|
|
|
@@ -84,6 +84,20 @@ describe('prefer-safe-number-parse-integer through type wrappers', () => {
|
|
|
84
84
|
const n = parseInt(s, 16 as const);
|
|
85
85
|
`,
|
|
86
86
|
},
|
|
87
|
+
{
|
|
88
|
+
name: 'ignores a string widened by `as` to one that may be a number',
|
|
89
|
+
code: dedent`
|
|
90
|
+
declare const s: string;
|
|
91
|
+
const n = parseInt(s as string | number as string);
|
|
92
|
+
`,
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: 'ignores `!` on an optional string, which leaves undefined in place',
|
|
96
|
+
code: dedent`
|
|
97
|
+
declare const s: string | undefined;
|
|
98
|
+
const n = parseInt(s!);
|
|
99
|
+
`,
|
|
100
|
+
},
|
|
87
101
|
],
|
|
88
102
|
invalid: [
|
|
89
103
|
{
|
|
@@ -131,15 +145,15 @@ describe('prefer-safe-number-parse-integer through type wrappers', () => {
|
|
|
131
145
|
],
|
|
132
146
|
},
|
|
133
147
|
{
|
|
134
|
-
name: '
|
|
148
|
+
name: 'rewrites a string that is a string at every layer',
|
|
135
149
|
code: dedent`
|
|
136
|
-
declare const s: string
|
|
137
|
-
const n = parseInt(s!);
|
|
150
|
+
declare const s: string;
|
|
151
|
+
const n = parseInt(s! as string);
|
|
138
152
|
`,
|
|
139
153
|
output: dedent`
|
|
140
154
|
import { Result, SafeNumber } from 'ts-std-forge';
|
|
141
|
-
declare const s: string
|
|
142
|
-
const n = Result.unwrapOkOr(SafeNumber.parseInteger(s!), Number.NaN);
|
|
155
|
+
declare const s: string;
|
|
156
|
+
const n = Result.unwrapOkOr(SafeNumber.parseInteger(s! as string), Number.NaN);
|
|
143
157
|
`,
|
|
144
158
|
errors: [{ messageId: 'useSafeNumberParseInteger' }],
|
|
145
159
|
},
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
skipTypeWrappers,
|
|
12
12
|
} from 'eslint-rule-forge';
|
|
13
13
|
import * as ts from 'typescript';
|
|
14
|
-
import {
|
|
14
|
+
import { isIdentifierNamed, typeHoldsAtEveryLayer } from './ast-utils.mjs';
|
|
15
15
|
import { TS_STD_FORGE_MODULE } from './constants.mjs';
|
|
16
16
|
|
|
17
17
|
type Options = readonly [];
|
|
@@ -115,13 +115,13 @@ export const preferSafeNumberParse: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
115
115
|
return;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
// The argument must be purely `string`
|
|
119
|
-
//
|
|
120
|
-
//
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (
|
|
118
|
+
// The argument must be purely `string` at every layer of type
|
|
119
|
+
// wrappers: the value's own type is what `Number` sees at run time
|
|
120
|
+
// (`Number(true as unknown as string)` is 1, the rewrite NaN), and the
|
|
121
|
+
// type a wrapper states is what the fix, which keeps the wrapper,
|
|
122
|
+
// hands `SafeNumber.parse(value: string)`. Without type information,
|
|
123
|
+
// skip.
|
|
124
|
+
if (!typeHoldsAtEveryLayer(sourceCode, firstArg, isStringType)) {
|
|
125
125
|
return;
|
|
126
126
|
}
|
|
127
127
|
|
|
@@ -132,6 +132,20 @@ describe('prefer-safe-number-parse through type wrappers', () => {
|
|
|
132
132
|
const n = parseFloat(x! as string);
|
|
133
133
|
`,
|
|
134
134
|
},
|
|
135
|
+
{
|
|
136
|
+
name: 'ignores a string widened by `as` to a type Number() reads otherwise',
|
|
137
|
+
code: dedent`
|
|
138
|
+
declare const s: string;
|
|
139
|
+
const n = Number(s as string | boolean);
|
|
140
|
+
`,
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
name: 'ignores `!` on an optional string, which leaves undefined in place',
|
|
144
|
+
code: dedent`
|
|
145
|
+
declare const s: string | undefined;
|
|
146
|
+
const n = parseFloat(s!);
|
|
147
|
+
`,
|
|
148
|
+
},
|
|
135
149
|
],
|
|
136
150
|
invalid: [
|
|
137
151
|
{
|
|
@@ -166,15 +180,15 @@ describe('prefer-safe-number-parse through type wrappers', () => {
|
|
|
166
180
|
],
|
|
167
181
|
},
|
|
168
182
|
{
|
|
169
|
-
name: '
|
|
183
|
+
name: 'rewrites a string that is a string at every layer',
|
|
170
184
|
code: dedent`
|
|
171
|
-
declare const s: string
|
|
172
|
-
const n = parseFloat(s!);
|
|
185
|
+
declare const s: string;
|
|
186
|
+
const n = parseFloat(s! as string);
|
|
173
187
|
`,
|
|
174
188
|
output: dedent`
|
|
175
189
|
import { Result, SafeNumber } from 'ts-std-forge';
|
|
176
|
-
declare const s: string
|
|
177
|
-
const n = Result.unwrapOkOr(SafeNumber.parse(s!), Number.NaN);
|
|
190
|
+
declare const s: string;
|
|
191
|
+
const n = Result.unwrapOkOr(SafeNumber.parse(s! as string), Number.NaN);
|
|
178
192
|
`,
|
|
179
193
|
errors: [{ messageId: 'useSafeNumberParse' }],
|
|
180
194
|
},
|