eslint-plugin-ts-std-forge 0.1.16 → 0.1.18

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # eslint-plugin-ts-std-forge
2
2
 
3
- ESLint rules that steer TypeScript code toward [`ts-std-forge`](https://www.npmjs.com/package/ts-std-forge) idioms — e.g. preferring `SafeArray.isNonEmpty(xs)` over `xs.length > 0`, `isRecord(x) && hasKey(x, k)` over `Object.hasOwn(x, k)`, or `SafeNumber.parseInteger` over `parseInt`. Every rule is auto-fixable.
3
+ ESLint rules that steer TypeScript code toward [`ts-std-forge`](https://www.npmjs.com/package/ts-std-forge) idioms — e.g. preferring `SafeArray.isNonEmpty(xs)` over `xs.length > 0`, `isRecord(x) && hasKey(x, k)` over `Object.hasOwn(x, k)`, or `SafeNumber.parseInteger` over `parseInt`. Most rules are auto-fixable; where the rewrite can change what the code does, the rule suggests it instead.
4
4
 
5
5
  ## Relation to `eslint-plugin-ts-data-forge`
6
6
 
@@ -19,8 +19,8 @@ plugin covers the part that moved, and names the new home:
19
19
  | `prefer-is-non-null-object` | same name | same rule, pointing at `ts-std-forge` |
20
20
 
21
21
  **Do not enable both plugins on the same files.** Every pair above would
22
- report the same code twice with conflicting autofixes. Pick the package the
23
- code actually depends on: `ts-std-forge` alone, or `ts-data-forge` (which
22
+ report the same code twice with conflicting fixes or suggestions. Pick the
23
+ package the code actually depends on: `ts-std-forge` alone, or `ts-data-forge` (which
24
24
  brings `ts-std-forge` with it, and whose rules point at the branded API).
25
25
 
26
26
  The rest of `eslint-plugin-ts-data-forge` — the branded number and array
@@ -90,14 +90,14 @@ export default [
90
90
 
91
91
  ## Rules
92
92
 
93
- | Rule | Description |
94
- | :--------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------- |
95
- | `prefer-is-non-null-object` | Replace `typeof u === 'object' && u !== null` with `isNonNullObject(u)`. |
96
- | `prefer-is-record-and-has-key` | Replace `Object.hasOwn(obj, key)` / `key in obj` with `isRecord(obj) && hasKey(obj, key)`. Drops the `isRecord` half when the type already makes it true. |
97
- | `prefer-safe-array-is-array` | Replace `Array.isArray(x)` with `SafeArray.isArray(x)`, which keeps the array members of a union instead of widening to `any[]`. |
98
- | `prefer-safe-array-length-guard` | Replace an emptiness check on an array (`xs.length === 0`, `> 0`, `>= 1`, `!== 0`) with `SafeArray.isEmpty(xs)` / `SafeArray.isNonEmpty(xs)`, which narrow. |
99
- | `prefer-safe-number-parse` | Replace `parseFloat(x)` / `Number.parseFloat(x)` / `Number(x)` (string argument) with `Result.unwrapOkOr(SafeNumber.parse(x), Number.NaN)`. |
100
- | `prefer-safe-number-parse-integer` | Replace `parseInt(x)` / `Number.parseInt(x, 10)` with `Result.unwrapOkOr(SafeNumber.parseInteger(x), Number.NaN)`. |
93
+ | Rule | Description |
94
+ | :--------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
95
+ | `prefer-is-non-null-object` | Replace `typeof u === 'object' && u !== null` with `isNonNullObject(u)`. |
96
+ | `prefer-is-record-and-has-key` | Replace `Object.hasOwn(obj, key)` / `key in obj` with `isRecord(obj) && hasKey(obj, key)`. Drops the `isRecord` half when the type already makes it true. |
97
+ | `prefer-safe-array-is-array` | Replace `Array.isArray(x)` with `SafeArray.isArray(x)`, which keeps the array members of a union instead of widening to `any[]`. |
98
+ | `prefer-safe-array-length-guard` | Replace an emptiness check on an array (`xs.length === 0`, `> 0`, `>= 1`, `!== 0`) with `SafeArray.isEmpty(xs)` / `SafeArray.isNonEmpty(xs)`, which narrow. |
99
+ | `prefer-safe-number-parse` | Replace `parseFloat(x)` / `Number.parseFloat(x)` / `Number(x)` (string argument) with `Result.unwrapOkOr(SafeNumber.parse(x), Number.NaN)`. Always a suggestion, since some strings parse differently. |
100
+ | `prefer-safe-number-parse-integer` | Replace `parseInt(x)` / `Number.parseInt(x, 10)` with `Result.unwrapOkOr(SafeNumber.parseInteger(x), Number.NaN)`. Always a suggestion, since some strings parse differently. |
101
101
 
102
102
  ## License
103
103
 
package/dist/plugin.d.mts CHANGED
@@ -8,8 +8,8 @@ export declare const eslintPluginTsStdForge: {
8
8
  readonly 'prefer-is-record-and-has-key': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useHasKey" | "useIsRecordAndHasKey", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
9
9
  readonly 'prefer-safe-array-is-array': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsArray", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
10
10
  readonly 'prefer-safe-array-length-guard': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsEmpty" | "useIsNonEmpty", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
11
- readonly 'prefer-safe-number-parse': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useSafeParseFloat", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
12
- readonly 'prefer-safe-number-parse-integer': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useSafeParseInt", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
11
+ readonly 'prefer-safe-number-parse': import("@typescript-eslint/utils/ts-eslint").RuleModule<"replaceWithSafeParse" | "useSafeParseFloat", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
12
+ readonly 'prefer-safe-number-parse-integer': import("@typescript-eslint/utils/ts-eslint").RuleModule<"replaceWithSafeParse" | "useSafeParseInt", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
13
13
  };
14
14
  readonly configs: {
15
15
  /** Enables every rule of this plugin at `error`. */
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.mjs","sourceRoot":"","sources":["../src/plugin.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGpD;;;;;;GAMG;AACH,MAAM,gBAAgB,GAAG;IACvB,wCAAwC,EAAE,OAAO;IACjD,2CAA2C,EAAE,OAAO;IACpD,yCAAyC,EAAE,OAAO;IAClD,6CAA6C,EAAE,OAAO;IACtD,uCAAuC,EAAE,OAAO;IAChD,+CAA+C,EAAE,OAAO;CAIzD,CAAC;AAEF,MAAM,iBAAiB,GAAG;IACxB,IAAI,EAAE,0BAA0B;IAChC,OAAO,EAAE;QACP,uEAAuE;QACvE,wEAAwE;QACxE,wEAAwE;QACxE,mDAAmD;QACnD,wEAAwE;QACxE,wDAAwD;QACxD,IAAI,cAAc;YAChB,OAAO,sBAAsB,CAAC;QAChC,CAAC;KACF;IACD,KAAK,EAAE,gBAAgB;CACY,CAAC;AAEtC,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,IAAI,EAAE;QACJ,IAAI,EAAE,4BAA4B;KACnC;IACD,KAAK,EAAE,eAAe;IACtB,OAAO,EAAE;QACP,oDAAoD;QACpD,WAAW,EAAE,iBAAiB;KAC/B;CAC8B,CAAC"}
1
+ {"version":3,"file":"plugin.mjs","sourceRoot":"","sources":["../src/plugin.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGpD;;;;;;GAMG;AACH,MAAM,gBAAgB,GAAG;IACvB,wCAAwC,EAAE,OAAO;IACjD,2CAA2C,EAAE,OAAO;IACpD,yCAAyC,EAAE,OAAO;IAClD,6CAA6C,EAAE,OAAO;IACtD,uCAAuC,EAAE,OAAO;IAChD,+CAA+C,EAAE,OAAO;CAIzD,CAAC;AAEF,MAAM,iBAAiB,GAAG;IACxB,IAAI,EAAE,0BAA0B;IAChC,OAAO,EAAE;QACP,uEAAuE;QACvE,wEAAwE;QACxE,wEAAwE;QACxE,mDAAmD;QACnD,2EAA2E;QAC3E,qDAAqD;QACrD,IAAI,cAAc;YAChB,OAAO,sBAAsB,CAAC;QAChC,CAAC;KACF;IACD,KAAK,EAAE,gBAAgB;CACY,CAAC;AAEtC,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,IAAI,EAAE;QACJ,IAAI,EAAE,4BAA4B;KACnC;IACD,KAAK,EAAE,eAAe;IACtB,OAAO,EAAE;QACP,oDAAoD;QACpD,WAAW,EAAE,iBAAiB;KAC/B;CAC8B,CAAC"}
@@ -64,7 +64,7 @@ declare namespace PreferSafeArrayLengthGuard {
64
64
  * | :--------- | :--------- |
65
65
  * | type | suggestion |
66
66
  * | deprecated | false |
67
- * | fixable | code |
67
+ * | fixable | false |
68
68
  * ```
69
69
  */
70
70
  declare namespace PreferSafeNumberParse {
@@ -78,7 +78,7 @@ declare namespace PreferSafeNumberParse {
78
78
  * | :--------- | :--------- |
79
79
  * | type | suggestion |
80
80
  * | deprecated | false |
81
- * | fixable | code |
81
+ * | fixable | false |
82
82
  * ```
83
83
  */
84
84
  declare namespace PreferSafeNumberParseInteger {
@@ -1 +1 @@
1
- {"version":3,"file":"prefer-safe-array-length-guard.mjs","sourceRoot":"","sources":["../../src/rules/prefer-safe-array-length-guard.mts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"prefer-safe-array-length-guard.mjs","sourceRoot":"","sources":["../../src/rules/prefer-safe-array-length-guard.mts"],"names":[],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- export declare const preferSafeNumberParseInteger: import("@typescript-eslint/utils/ts-eslint").RuleModule<"useSafeParseInt", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
1
+ export declare const preferSafeNumberParseInteger: import("@typescript-eslint/utils/ts-eslint").RuleModule<"replaceWithSafeParse" | "useSafeParseInt", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
2
2
  //# sourceMappingURL=prefer-safe-number-parse-integer.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"prefer-safe-number-parse-integer.d.mts","sourceRoot":"","sources":["../../src/rules/prefer-safe-number-parse-integer.mts"],"names":[],"mappings":"AAGA,eAAO,MAAM,4BAA4B,6JAIvC,CAAC"}
1
+ {"version":3,"file":"prefer-safe-number-parse-integer.d.mts","sourceRoot":"","sources":["../../src/rules/prefer-safe-number-parse-integer.mts"],"names":[],"mappings":"AAGA,eAAO,MAAM,4BAA4B,sLAIvC,CAAC"}
@@ -4,5 +4,5 @@
4
4
  * result in a `FiniteNumber` brand. This one names the implementation, for
5
5
  * callers who do not want the brand or the extra dependency.
6
6
  */
7
- export declare const preferSafeNumberParse: import("@typescript-eslint/utils/ts-eslint").RuleModule<"useSafeParseFloat", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
7
+ export declare const preferSafeNumberParse: import("@typescript-eslint/utils/ts-eslint").RuleModule<"replaceWithSafeParse" | "useSafeParseFloat", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
8
8
  //# sourceMappingURL=prefer-safe-number-parse.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"prefer-safe-number-parse.d.mts","sourceRoot":"","sources":["../../src/rules/prefer-safe-number-parse.mts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,+JAIhC,CAAC"}
1
+ {"version":3,"file":"prefer-safe-number-parse.d.mts","sourceRoot":"","sources":["../../src/rules/prefer-safe-number-parse.mts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,wLAIhC,CAAC"}
@@ -3,7 +3,7 @@ export declare const tsStdForgeRules: {
3
3
  readonly 'prefer-is-record-and-has-key': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useHasKey" | "useIsRecordAndHasKey", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
4
4
  readonly 'prefer-safe-array-is-array': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsArray", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
5
5
  readonly 'prefer-safe-array-length-guard': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsEmpty" | "useIsNonEmpty", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
6
- readonly 'prefer-safe-number-parse': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useSafeParseFloat", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
7
- readonly 'prefer-safe-number-parse-integer': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useSafeParseInt", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
6
+ readonly 'prefer-safe-number-parse': import("@typescript-eslint/utils/ts-eslint").RuleModule<"replaceWithSafeParse" | "useSafeParseFloat", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
7
+ readonly 'prefer-safe-number-parse-integer': import("@typescript-eslint/utils/ts-eslint").RuleModule<"replaceWithSafeParse" | "useSafeParseInt", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
8
8
  };
9
9
  //# sourceMappingURL=rules.d.mts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-ts-std-forge",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "private": false,
5
5
  "description": "ESLint rules that steer code toward ts-std-forge idioms.",
6
6
  "keywords": [
@@ -35,21 +35,21 @@
35
35
  ],
36
36
  "dependencies": {
37
37
  "@typescript-eslint/utils": "^8.72.0",
38
- "eslint-rule-forge": "^0.4.0",
39
- "ts-std-forge": "0.5.2",
38
+ "eslint-rule-forge": "^0.5.1",
39
+ "ts-std-forge": "0.5.3",
40
40
  "ts-type-forge": "~9.2.2"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@typescript-eslint/parser": "8.72.0",
44
44
  "@typescript-eslint/rule-tester": "8.72.0",
45
45
  "dedent": "1.7.2",
46
- "eslint-config-typed": "7.2.0",
47
- "eslint-plugin-ts-fortress": "1.3.11",
48
- "eslint-plugin-ts-type-forge": "0.8.15",
46
+ "eslint-config-typed": "7.4.0",
47
+ "eslint-plugin-ts-fortress": "1.3.13",
48
+ "eslint-plugin-ts-type-forge": "0.8.18",
49
49
  "prettier": "3.9.10",
50
50
  "prettier-plugin-organize-imports": "4.3.0",
51
51
  "prettier-plugin-packagejson": "3.0.2",
52
- "ts-repo-utils": "10.7.1"
52
+ "ts-repo-utils": "10.7.2"
53
53
  },
54
54
  "peerDependencies": {
55
55
  "eslint": ">=9.0.0",
package/src/plugin.mts CHANGED
@@ -5,9 +5,9 @@ import type { ESLintFlatConfig, ESLintPlugin } from './types.mjs';
5
5
  /**
6
6
  * Every rule this plugin ships, at `error`.
7
7
  *
8
- * The `satisfies` clause below is keyed off {@link tsStdForgeRules}, so leaving a
9
- * rule out, naming one that does not exist, or setting one to anything
10
- * but `error` fails to type-check. That is the whole check; no test repeats it.
8
+ * The `satisfies` clause below is keyed off {@link tsStdForgeRules}, so leaving
9
+ * a rule out, naming one that does not exist, or setting one to anything but
10
+ * `error` fails to type-check. That is the whole check; no test repeats it.
11
11
  */
12
12
  const recommendedRules = {
13
13
  'ts-std-forge/prefer-is-non-null-object': 'error',
@@ -28,8 +28,8 @@ const recommendedConfig = {
28
28
  // `eslintPluginTsStdForge` refers to. ESLint rejects a plugin name that
29
29
  // maps to two different objects with `Cannot redefine plugin`, which is
30
30
  // what a user combining this preset with their own
31
- // `plugins: { 'ts-std-forge': eslintPluginTsStdForge }` entry would hit
32
- // if the preset embedded a separate copy of the plugin.
31
+ // `plugins: { 'ts-std-forge': eslintPluginTsStdForge }` entry would hit if
32
+ // the preset embedded a separate copy of the plugin.
33
33
  get 'ts-std-forge'(): ESLintPlugin {
34
34
  return eslintPluginTsStdForge;
35
35
  },
@@ -76,7 +76,7 @@ namespace PreferSafeArrayLengthGuard {
76
76
  * | :--------- | :--------- |
77
77
  * | type | suggestion |
78
78
  * | deprecated | false |
79
- * | fixable | code |
79
+ * | fixable | false |
80
80
  * ```
81
81
  */
82
82
  namespace PreferSafeNumberParse {
@@ -91,7 +91,7 @@ namespace PreferSafeNumberParse {
91
91
  * | :--------- | :--------- |
92
92
  * | type | suggestion |
93
93
  * | deprecated | false |
94
- * | fixable | code |
94
+ * | fixable | false |
95
95
  * ```
96
96
  */
97
97
  namespace PreferSafeNumberParseInteger {
@@ -53,9 +53,9 @@ ruleTester.run('prefer-is-non-null-object', preferIsNonNullObject, {
53
53
  ],
54
54
  });
55
55
 
56
- // The replacement names the bare identifier: that is the reference both
57
- // halves narrow, and a wrapper around the argument would stop the guard from
58
- // narrowing it.
56
+ // The replacement names the bare identifier: that is the reference both halves
57
+ // narrow, and a wrapper around the argument would stop the guard from narrowing
58
+ // it.
59
59
  describe('prefer-is-non-null-object through type wrappers', () => {
60
60
  ruleTester.run('prefer-is-non-null-object', preferIsNonNullObject, {
61
61
  valid: [
@@ -84,8 +84,8 @@ export const preferSafeArrayLengthGuard: TSESLint.RuleModule<
84
84
  * answer is no: `.length` is also a string's, a function's and a
85
85
  * `TypedArray`'s, and none of those can be handed to the guard. It has to
86
86
  * be one at every layer of type wrappers: a string cast to an array is
87
- * still a string, and the fix, which keeps the wrapper, hands the guard
88
- * the type the outermost layer states.
87
+ * still a string, and the fix, which keeps the wrapper, hands the guard the
88
+ * type the outermost layer states.
89
89
  */
90
90
  const isArrayLike = (
91
91
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
@@ -207,8 +207,8 @@ const matchLengthComparison = (
207
207
  return undefined;
208
208
  }
209
209
 
210
- // `xs.length === ys.length` has `.length` on both sides and no literal
211
- // bound, so the bound check below rejects it.
210
+ // `xs.length === ys.length` has `.length` on both sides and no literal bound,
211
+ // so the bound check below rejects it.
212
212
  if (
213
213
  boundSide.type !== AST_NODE_TYPES.Literal ||
214
214
  typeof boundSide.value !== 'number'
@@ -399,6 +399,42 @@ describe('prefer-safe-array-length-guard and the import it adds', () => {
399
399
  output: null,
400
400
  errors: [{ messageId: 'useIsNonEmpty' }],
401
401
  },
402
+ {
403
+ name: 'a type parameter of the name does not take the value',
404
+ code: dedent`
405
+ declare const xs: readonly number[];
406
+ function f<SafeArray>() {
407
+ return xs.length > 0;
408
+ }
409
+ `,
410
+ output: dedent`
411
+ import { SafeArray } from 'ts-std-forge';
412
+ declare const xs: readonly number[];
413
+ function f<SafeArray>() {
414
+ return SafeArray.isNonEmpty(xs);
415
+ }
416
+ `,
417
+ errors: [{ messageId: 'useIsNonEmpty' }],
418
+ },
419
+ {
420
+ name: 'a local type alias of the name does not take the value',
421
+ code: dedent`
422
+ declare const xs: readonly number[];
423
+ function f() {
424
+ type SafeArray = number;
425
+ return xs.length > 0;
426
+ }
427
+ `,
428
+ output: dedent`
429
+ import { SafeArray } from 'ts-std-forge';
430
+ declare const xs: readonly number[];
431
+ function f() {
432
+ type SafeArray = number;
433
+ return SafeArray.isNonEmpty(xs);
434
+ }
435
+ `,
436
+ errors: [{ messageId: 'useIsNonEmpty' }],
437
+ },
402
438
  ],
403
439
  });
404
440
  });
@@ -51,17 +51,75 @@ describe('prefer-safe-number-parse-integer', () => {
51
51
  ],
52
52
  invalid: [
53
53
  {
54
- name: 'rewrites parseInt without a radix',
54
+ name: 'offers no suggestion when `Result` is bound to another module',
55
55
  code: dedent`
56
+ import { Result } from 'other-lib';
57
+
56
58
  declare const s: string;
57
59
  const n = parseInt(s);
60
+ export const r = Result;
61
+ `,
62
+ errors: [{ messageId: 'useSafeParseInt', suggestions: [] }],
63
+ },
64
+ {
65
+ // The replacement reads these strings differently ('1e3' is 1000, not
66
+ // 1; '12px' is NaN, not 12), so it is offered, never applied by
67
+ // --fix
68
+ name: 'a string the replacement parses differently is a suggestion, not a fix',
69
+ code: dedent`
70
+ const a = parseInt('1e3');
71
+ const b = parseInt('12px', 10);
58
72
  `,
59
- output: dedent`
60
- import { Result, SafeNumber } from 'ts-std-forge';
73
+ errors: [
74
+ {
75
+ messageId: 'useSafeParseInt',
76
+ suggestions: [
77
+ {
78
+ messageId: 'replaceWithSafeParse',
79
+ output: dedent`
80
+ import { Result, SafeNumber } from 'ts-std-forge';
81
+ const a = Result.unwrapOkOr(SafeNumber.parseInteger('1e3'), Number.NaN);
82
+ const b = parseInt('12px', 10);
83
+ `,
84
+ },
85
+ ],
86
+ },
87
+ {
88
+ messageId: 'useSafeParseInt',
89
+ suggestions: [
90
+ {
91
+ messageId: 'replaceWithSafeParse',
92
+ output: dedent`
93
+ import { Result, SafeNumber } from 'ts-std-forge';
94
+ const a = parseInt('1e3');
95
+ const b = Result.unwrapOkOr(SafeNumber.parseInteger('12px'), Number.NaN);
96
+ `,
97
+ },
98
+ ],
99
+ },
100
+ ],
101
+ },
102
+ {
103
+ name: 'rewrites parseInt without a radix',
104
+ code: dedent`
61
105
  declare const s: string;
62
- const n = Result.unwrapOkOr(SafeNumber.parseInteger(s), Number.NaN);
106
+ const n = parseInt(s);
63
107
  `,
64
- errors: [{ messageId: 'useSafeParseInt' }],
108
+ errors: [
109
+ {
110
+ messageId: 'useSafeParseInt',
111
+ suggestions: [
112
+ {
113
+ messageId: 'replaceWithSafeParse',
114
+ output: dedent`
115
+ import { Result, SafeNumber } from 'ts-std-forge';
116
+ declare const s: string;
117
+ const n = Result.unwrapOkOr(SafeNumber.parseInteger(s), Number.NaN);
118
+ `,
119
+ },
120
+ ],
121
+ },
122
+ ],
65
123
  },
66
124
  {
67
125
  name: 'rewrites Number.parseInt(x, 10)',
@@ -69,12 +127,21 @@ describe('prefer-safe-number-parse-integer', () => {
69
127
  declare const s: string;
70
128
  const n = Number.parseInt(s, 10);
71
129
  `,
72
- output: dedent`
73
- import { Result, SafeNumber } from 'ts-std-forge';
74
- declare const s: string;
75
- const n = Result.unwrapOkOr(SafeNumber.parseInteger(s), Number.NaN);
76
- `,
77
- errors: [{ messageId: 'useSafeParseInt' }],
130
+ errors: [
131
+ {
132
+ messageId: 'useSafeParseInt',
133
+ suggestions: [
134
+ {
135
+ messageId: 'replaceWithSafeParse',
136
+ output: dedent`
137
+ import { Result, SafeNumber } from 'ts-std-forge';
138
+ declare const s: string;
139
+ const n = Result.unwrapOkOr(SafeNumber.parseInteger(s), Number.NaN);
140
+ `,
141
+ },
142
+ ],
143
+ },
144
+ ],
78
145
  },
79
146
  ],
80
147
  });
@@ -120,12 +187,21 @@ describe('prefer-safe-number-parse-integer through type wrappers', () => {
120
187
  declare const s: string;
121
188
  const n = parseInt(s, 10 as const);
122
189
  `,
123
- output: dedent`
124
- import { Result, SafeNumber } from 'ts-std-forge';
125
- declare const s: string;
126
- const n = Result.unwrapOkOr(SafeNumber.parseInteger(s), Number.NaN);
127
- `,
128
- errors: [{ messageId: 'useSafeParseInt' }],
190
+ errors: [
191
+ {
192
+ messageId: 'useSafeParseInt',
193
+ suggestions: [
194
+ {
195
+ messageId: 'replaceWithSafeParse',
196
+ output: dedent`
197
+ import { Result, SafeNumber } from 'ts-std-forge';
198
+ declare const s: string;
199
+ const n = Result.unwrapOkOr(SafeNumber.parseInteger(s), Number.NaN);
200
+ `,
201
+ },
202
+ ],
203
+ },
204
+ ],
129
205
  },
130
206
  {
131
207
  name: 'reads a radix of 10 through `satisfies` and keeps `satisfies` on the argument',
@@ -133,12 +209,21 @@ describe('prefer-safe-number-parse-integer through type wrappers', () => {
133
209
  declare const s: string;
134
210
  const n = Number.parseInt(s satisfies string, 10 satisfies number);
135
211
  `,
136
- output: dedent`
137
- import { Result, SafeNumber } from 'ts-std-forge';
138
- declare const s: string;
139
- const n = Result.unwrapOkOr(SafeNumber.parseInteger(s satisfies string), Number.NaN);
140
- `,
141
- errors: [{ messageId: 'useSafeParseInt' }],
212
+ errors: [
213
+ {
214
+ messageId: 'useSafeParseInt',
215
+ suggestions: [
216
+ {
217
+ messageId: 'replaceWithSafeParse',
218
+ output: dedent`
219
+ import { Result, SafeNumber } from 'ts-std-forge';
220
+ declare const s: string;
221
+ const n = Result.unwrapOkOr(SafeNumber.parseInteger(s satisfies string), Number.NaN);
222
+ `,
223
+ },
224
+ ],
225
+ },
226
+ ],
142
227
  },
143
228
  {
144
229
  name: 'reads the callee through `as` and `!`',
@@ -147,23 +232,35 @@ describe('prefer-safe-number-parse-integer through type wrappers', () => {
147
232
  const a = (parseInt as typeof parseInt)(s);
148
233
  const b = (Number as NumberConstructor)!.parseInt(s, 10);
149
234
  `,
150
- output: [
151
- dedent`
152
- import { Result, SafeNumber } from 'ts-std-forge';
153
- declare const s: string;
154
- const a = Result.unwrapOkOr(SafeNumber.parseInteger(s), Number.NaN);
155
- const b = (Number as NumberConstructor)!.parseInt(s, 10);
156
- `,
157
- dedent`
158
- import { Result, SafeNumber } from 'ts-std-forge';
159
- declare const s: string;
160
- const a = Result.unwrapOkOr(SafeNumber.parseInteger(s), Number.NaN);
161
- const b = Result.unwrapOkOr(SafeNumber.parseInteger(s), Number.NaN);
162
- `,
163
- ],
164
235
  errors: [
165
- { messageId: 'useSafeParseInt' },
166
- { messageId: 'useSafeParseInt' },
236
+ {
237
+ messageId: 'useSafeParseInt',
238
+ suggestions: [
239
+ {
240
+ messageId: 'replaceWithSafeParse',
241
+ output: dedent`
242
+ import { Result, SafeNumber } from 'ts-std-forge';
243
+ declare const s: string;
244
+ const a = Result.unwrapOkOr(SafeNumber.parseInteger(s), Number.NaN);
245
+ const b = (Number as NumberConstructor)!.parseInt(s, 10);
246
+ `,
247
+ },
248
+ ],
249
+ },
250
+ {
251
+ messageId: 'useSafeParseInt',
252
+ suggestions: [
253
+ {
254
+ messageId: 'replaceWithSafeParse',
255
+ output: dedent`
256
+ import { Result, SafeNumber } from 'ts-std-forge';
257
+ declare const s: string;
258
+ const a = (parseInt as typeof parseInt)(s);
259
+ const b = Result.unwrapOkOr(SafeNumber.parseInteger(s), Number.NaN);
260
+ `,
261
+ },
262
+ ],
263
+ },
167
264
  ],
168
265
  },
169
266
  {
@@ -172,12 +269,21 @@ describe('prefer-safe-number-parse-integer through type wrappers', () => {
172
269
  declare const s: string;
173
270
  const n = parseInt(s! as string);
174
271
  `,
175
- output: dedent`
176
- import { Result, SafeNumber } from 'ts-std-forge';
177
- declare const s: string;
178
- const n = Result.unwrapOkOr(SafeNumber.parseInteger(s! as string), Number.NaN);
179
- `,
180
- errors: [{ messageId: 'useSafeParseInt' }],
272
+ errors: [
273
+ {
274
+ messageId: 'useSafeParseInt',
275
+ suggestions: [
276
+ {
277
+ messageId: 'replaceWithSafeParse',
278
+ output: dedent`
279
+ import { Result, SafeNumber } from 'ts-std-forge';
280
+ declare const s: string;
281
+ const n = Result.unwrapOkOr(SafeNumber.parseInteger(s! as string), Number.NaN);
282
+ `,
283
+ },
284
+ ],
285
+ },
286
+ ],
181
287
  },
182
288
  ],
183
289
  });
@@ -194,13 +300,22 @@ describe('prefer-safe-number-parse-integer with parenthesized operands', () => {
194
300
  declare const s: string;
195
301
  const n = parseInt((a, s), (10));
196
302
  `,
197
- output: dedent`
198
- import { Result, SafeNumber } from 'ts-std-forge';
199
- declare const a: number;
200
- declare const s: string;
201
- const n = Result.unwrapOkOr(SafeNumber.parseInteger((a, s)), Number.NaN);
202
- `,
203
- errors: [{ messageId: 'useSafeParseInt' }],
303
+ errors: [
304
+ {
305
+ messageId: 'useSafeParseInt',
306
+ suggestions: [
307
+ {
308
+ messageId: 'replaceWithSafeParse',
309
+ output: dedent`
310
+ import { Result, SafeNumber } from 'ts-std-forge';
311
+ declare const a: number;
312
+ declare const s: string;
313
+ const n = Result.unwrapOkOr(SafeNumber.parseInteger((a, s)), Number.NaN);
314
+ `,
315
+ },
316
+ ],
317
+ },
318
+ ],
204
319
  },
205
320
  ],
206
321
  });
@@ -21,7 +21,7 @@ describe('prefer-safe-number-parse', () => {
21
21
  tester.run('prefer-safe-number-parse', preferSafeNumberParse, {
22
22
  valid: [
23
23
  {
24
- name: 'ignores number arguments to Number() (autofix would be unsafe)',
24
+ name: 'ignores number arguments to Number() (the rewrite would be unsafe)',
25
25
  code: dedent`
26
26
  const x = 42;
27
27
  const n = Number(x);
@@ -72,17 +72,74 @@ describe('prefer-safe-number-parse', () => {
72
72
  ],
73
73
  invalid: [
74
74
  {
75
- name: 'rewrites parseFloat on a string',
75
+ name: 'offers no suggestion when `Result` is bound to another module',
76
76
  code: dedent`
77
+ import { Result } from 'other-lib';
78
+
77
79
  declare const s: string;
78
80
  const n = parseFloat(s);
81
+ export const r = Result;
82
+ `,
83
+ errors: [{ messageId: 'useSafeParseFloat', suggestions: [] }],
84
+ },
85
+ {
86
+ // The replacement reads these strings differently ('12px' is NaN, not
87
+ // 12; '' is NaN, not 0), so it is offered, never applied by --fix
88
+ name: 'a string the replacement parses differently is a suggestion, not a fix',
89
+ code: dedent`
90
+ const a = parseFloat('12px');
91
+ const b = Number('');
79
92
  `,
80
- output: dedent`
81
- import { Result, SafeNumber } from 'ts-std-forge';
93
+ errors: [
94
+ {
95
+ messageId: 'useSafeParseFloat',
96
+ suggestions: [
97
+ {
98
+ messageId: 'replaceWithSafeParse',
99
+ output: dedent`
100
+ import { Result, SafeNumber } from 'ts-std-forge';
101
+ const a = Result.unwrapOkOr(SafeNumber.parse('12px'), Number.NaN);
102
+ const b = Number('');
103
+ `,
104
+ },
105
+ ],
106
+ },
107
+ {
108
+ messageId: 'useSafeParseFloat',
109
+ suggestions: [
110
+ {
111
+ messageId: 'replaceWithSafeParse',
112
+ output: dedent`
113
+ import { Result, SafeNumber } from 'ts-std-forge';
114
+ const a = parseFloat('12px');
115
+ const b = Result.unwrapOkOr(SafeNumber.parse(''), Number.NaN);
116
+ `,
117
+ },
118
+ ],
119
+ },
120
+ ],
121
+ },
122
+ {
123
+ name: 'rewrites parseFloat on a string',
124
+ code: dedent`
82
125
  declare const s: string;
83
- const n = Result.unwrapOkOr(SafeNumber.parse(s), Number.NaN);
126
+ const n = parseFloat(s);
84
127
  `,
85
- errors: [{ messageId: 'useSafeParseFloat' }],
128
+ errors: [
129
+ {
130
+ messageId: 'useSafeParseFloat',
131
+ suggestions: [
132
+ {
133
+ messageId: 'replaceWithSafeParse',
134
+ output: dedent`
135
+ import { Result, SafeNumber } from 'ts-std-forge';
136
+ declare const s: string;
137
+ const n = Result.unwrapOkOr(SafeNumber.parse(s), Number.NaN);
138
+ `,
139
+ },
140
+ ],
141
+ },
142
+ ],
86
143
  },
87
144
  {
88
145
  name: 'rewrites Number.parseFloat on a string',
@@ -90,12 +147,21 @@ describe('prefer-safe-number-parse', () => {
90
147
  declare const s: string;
91
148
  const n = Number.parseFloat(s);
92
149
  `,
93
- output: dedent`
94
- import { Result, SafeNumber } from 'ts-std-forge';
95
- declare const s: string;
96
- const n = Result.unwrapOkOr(SafeNumber.parse(s), Number.NaN);
97
- `,
98
- errors: [{ messageId: 'useSafeParseFloat' }],
150
+ errors: [
151
+ {
152
+ messageId: 'useSafeParseFloat',
153
+ suggestions: [
154
+ {
155
+ messageId: 'replaceWithSafeParse',
156
+ output: dedent`
157
+ import { Result, SafeNumber } from 'ts-std-forge';
158
+ declare const s: string;
159
+ const n = Result.unwrapOkOr(SafeNumber.parse(s), Number.NaN);
160
+ `,
161
+ },
162
+ ],
163
+ },
164
+ ],
99
165
  },
100
166
  {
101
167
  name: 'rewrites Number(x) when x is a string',
@@ -103,12 +169,21 @@ describe('prefer-safe-number-parse', () => {
103
169
  declare const s: string;
104
170
  const n = Number(s);
105
171
  `,
106
- output: dedent`
107
- import { Result, SafeNumber } from 'ts-std-forge';
108
- declare const s: string;
109
- const n = Result.unwrapOkOr(SafeNumber.parse(s), Number.NaN);
110
- `,
111
- errors: [{ messageId: 'useSafeParseFloat' }],
172
+ errors: [
173
+ {
174
+ messageId: 'useSafeParseFloat',
175
+ suggestions: [
176
+ {
177
+ messageId: 'replaceWithSafeParse',
178
+ output: dedent`
179
+ import { Result, SafeNumber } from 'ts-std-forge';
180
+ declare const s: string;
181
+ const n = Result.unwrapOkOr(SafeNumber.parse(s), Number.NaN);
182
+ `,
183
+ },
184
+ ],
185
+ },
186
+ ],
112
187
  },
113
188
  {
114
189
  name: 'adds only the missing import',
@@ -117,12 +192,21 @@ describe('prefer-safe-number-parse', () => {
117
192
  declare const s: string;
118
193
  const n = parseFloat(s);
119
194
  `,
120
- output: dedent`
121
- import { Result, SafeNumber } from 'ts-std-forge';
122
- declare const s: string;
123
- const n = Result.unwrapOkOr(SafeNumber.parse(s), Number.NaN);
124
- `,
125
- errors: [{ messageId: 'useSafeParseFloat' }],
195
+ errors: [
196
+ {
197
+ messageId: 'useSafeParseFloat',
198
+ suggestions: [
199
+ {
200
+ messageId: 'replaceWithSafeParse',
201
+ output: dedent`
202
+ import { Result, SafeNumber } from 'ts-std-forge';
203
+ declare const s: string;
204
+ const n = Result.unwrapOkOr(SafeNumber.parse(s), Number.NaN);
205
+ `,
206
+ },
207
+ ],
208
+ },
209
+ ],
126
210
  },
127
211
  ],
128
212
  });
@@ -174,12 +258,21 @@ describe('prefer-safe-number-parse through type wrappers', () => {
174
258
  declare const s: string;
175
259
  const n = Number(s satisfies string);
176
260
  `,
177
- output: dedent`
178
- import { Result, SafeNumber } from 'ts-std-forge';
179
- declare const s: string;
180
- const n = Result.unwrapOkOr(SafeNumber.parse(s satisfies string), Number.NaN);
181
- `,
182
- errors: [{ messageId: 'useSafeParseFloat' }],
261
+ errors: [
262
+ {
263
+ messageId: 'useSafeParseFloat',
264
+ suggestions: [
265
+ {
266
+ messageId: 'replaceWithSafeParse',
267
+ output: dedent`
268
+ import { Result, SafeNumber } from 'ts-std-forge';
269
+ declare const s: string;
270
+ const n = Result.unwrapOkOr(SafeNumber.parse(s satisfies string), Number.NaN);
271
+ `,
272
+ },
273
+ ],
274
+ },
275
+ ],
183
276
  },
184
277
  {
185
278
  name: 'reads the callee through `as` and `!`',
@@ -188,23 +281,35 @@ describe('prefer-safe-number-parse through type wrappers', () => {
188
281
  const a = (Number as NumberConstructor)(s);
189
282
  const b = Number!.parseFloat(s);
190
283
  `,
191
- output: [
192
- dedent`
193
- import { Result, SafeNumber } from 'ts-std-forge';
194
- declare const s: string;
195
- const a = Result.unwrapOkOr(SafeNumber.parse(s), Number.NaN);
196
- const b = Number!.parseFloat(s);
197
- `,
198
- dedent`
199
- import { Result, SafeNumber } from 'ts-std-forge';
200
- declare const s: string;
201
- const a = Result.unwrapOkOr(SafeNumber.parse(s), Number.NaN);
202
- const b = Result.unwrapOkOr(SafeNumber.parse(s), Number.NaN);
203
- `,
204
- ],
205
284
  errors: [
206
- { messageId: 'useSafeParseFloat' },
207
- { messageId: 'useSafeParseFloat' },
285
+ {
286
+ messageId: 'useSafeParseFloat',
287
+ suggestions: [
288
+ {
289
+ messageId: 'replaceWithSafeParse',
290
+ output: dedent`
291
+ import { Result, SafeNumber } from 'ts-std-forge';
292
+ declare const s: string;
293
+ const a = Result.unwrapOkOr(SafeNumber.parse(s), Number.NaN);
294
+ const b = Number!.parseFloat(s);
295
+ `,
296
+ },
297
+ ],
298
+ },
299
+ {
300
+ messageId: 'useSafeParseFloat',
301
+ suggestions: [
302
+ {
303
+ messageId: 'replaceWithSafeParse',
304
+ output: dedent`
305
+ import { Result, SafeNumber } from 'ts-std-forge';
306
+ declare const s: string;
307
+ const a = (Number as NumberConstructor)(s);
308
+ const b = Result.unwrapOkOr(SafeNumber.parse(s), Number.NaN);
309
+ `,
310
+ },
311
+ ],
312
+ },
208
313
  ],
209
314
  },
210
315
  {
@@ -213,12 +318,21 @@ describe('prefer-safe-number-parse through type wrappers', () => {
213
318
  declare const s: string;
214
319
  const n = parseFloat(s! as string);
215
320
  `,
216
- output: dedent`
217
- import { Result, SafeNumber } from 'ts-std-forge';
218
- declare const s: string;
219
- const n = Result.unwrapOkOr(SafeNumber.parse(s! as string), Number.NaN);
220
- `,
221
- errors: [{ messageId: 'useSafeParseFloat' }],
321
+ errors: [
322
+ {
323
+ messageId: 'useSafeParseFloat',
324
+ suggestions: [
325
+ {
326
+ messageId: 'replaceWithSafeParse',
327
+ output: dedent`
328
+ import { Result, SafeNumber } from 'ts-std-forge';
329
+ declare const s: string;
330
+ const n = Result.unwrapOkOr(SafeNumber.parse(s! as string), Number.NaN);
331
+ `,
332
+ },
333
+ ],
334
+ },
335
+ ],
222
336
  },
223
337
  ],
224
338
  });
@@ -235,13 +349,22 @@ describe('prefer-safe-number-parse with parenthesized operands', () => {
235
349
  declare const s: string;
236
350
  const n = parseFloat((a, s));
237
351
  `,
238
- output: dedent`
239
- import { Result, SafeNumber } from 'ts-std-forge';
240
- declare const a: number;
241
- declare const s: string;
242
- const n = Result.unwrapOkOr(SafeNumber.parse((a, s)), Number.NaN);
243
- `,
244
- errors: [{ messageId: 'useSafeParseFloat' }],
352
+ errors: [
353
+ {
354
+ messageId: 'useSafeParseFloat',
355
+ suggestions: [
356
+ {
357
+ messageId: 'replaceWithSafeParse',
358
+ output: dedent`
359
+ import { Result, SafeNumber } from 'ts-std-forge';
360
+ declare const a: number;
361
+ declare const s: string;
362
+ const n = Result.unwrapOkOr(SafeNumber.parse((a, s)), Number.NaN);
363
+ `,
364
+ },
365
+ ],
366
+ },
367
+ ],
245
368
  },
246
369
  ],
247
370
  });