eslint-plugin-ts-std-forge 0.1.10 → 0.1.12

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.
Files changed (40) hide show
  1. package/dist/plugin.d.mts +3 -3
  2. package/dist/rules/prefer-is-non-null-object.d.mts +1 -5
  3. package/dist/rules/prefer-is-non-null-object.d.mts.map +1 -1
  4. package/dist/rules/prefer-is-non-null-object.mjs +4 -184
  5. package/dist/rules/prefer-is-non-null-object.mjs.map +1 -1
  6. package/dist/rules/prefer-is-record-and-has-key.d.mts +1 -5
  7. package/dist/rules/prefer-is-record-and-has-key.d.mts.map +1 -1
  8. package/dist/rules/prefer-is-record-and-has-key.mjs +4 -219
  9. package/dist/rules/prefer-is-record-and-has-key.mjs.map +1 -1
  10. package/dist/rules/prefer-safe-array-is-array.d.mts +1 -5
  11. package/dist/rules/prefer-safe-array-is-array.d.mts.map +1 -1
  12. package/dist/rules/prefer-safe-array-is-array.mjs +5 -78
  13. package/dist/rules/prefer-safe-array-is-array.mjs.map +1 -1
  14. package/dist/rules/prefer-safe-array-length-guard.mjs +1 -2
  15. package/dist/rules/prefer-safe-array-length-guard.mjs.map +1 -1
  16. package/dist/rules/prefer-safe-number-parse-integer.d.mts +1 -12
  17. package/dist/rules/prefer-safe-number-parse-integer.d.mts.map +1 -1
  18. package/dist/rules/prefer-safe-number-parse-integer.mjs +6 -108
  19. package/dist/rules/prefer-safe-number-parse-integer.mjs.map +1 -1
  20. package/dist/rules/prefer-safe-number-parse.d.mts +1 -12
  21. package/dist/rules/prefer-safe-number-parse.d.mts.map +1 -1
  22. package/dist/rules/prefer-safe-number-parse.mjs +6 -103
  23. package/dist/rules/prefer-safe-number-parse.mjs.map +1 -1
  24. package/dist/rules/rules.d.mts +3 -3
  25. package/package.json +6 -6
  26. package/src/rules/prefer-is-non-null-object.mts +4 -274
  27. package/src/rules/prefer-is-record-and-has-key.mts +4 -322
  28. package/src/rules/prefer-is-record-and-has-key.test.mts +48 -0
  29. package/src/rules/prefer-safe-array-is-array.mts +5 -119
  30. package/src/rules/prefer-safe-array-is-array.test.mts +18 -14
  31. package/src/rules/prefer-safe-array-length-guard.mts +1 -1
  32. package/src/rules/prefer-safe-number-parse-integer.mts +6 -172
  33. package/src/rules/prefer-safe-number-parse-integer.test.mts +22 -8
  34. package/src/rules/prefer-safe-number-parse.mts +6 -163
  35. package/src/rules/prefer-safe-number-parse.test.mts +30 -9
  36. package/dist/rules/ast-utils.d.mts +0 -18
  37. package/dist/rules/ast-utils.d.mts.map +0 -1
  38. package/dist/rules/ast-utils.mjs +0 -39
  39. package/dist/rules/ast-utils.mjs.map +0 -1
  40. package/src/rules/ast-utils.mts +0 -57
@@ -1,324 +1,6 @@
1
- import {
2
- AST_NODE_TYPES,
3
- type TSESLint,
4
- type TSESTree,
5
- } from '@typescript-eslint/utils';
6
- import {
7
- buildNamedImportFix,
8
- getArgumentText,
9
- getImportDeclarations,
10
- getNamedImports,
11
- skipTypeWrappers,
12
- } from 'eslint-rule-forge';
13
- import { SafeArray } from 'ts-std-forge';
14
- import * as ts from 'typescript';
15
- import { isIdentifierNamed, typeHoldsAtEveryLayer } from './ast-utils.mjs';
1
+ import { createPreferIsRecordAndHasKey } from 'eslint-rule-forge';
16
2
  import { TS_STD_FORGE_MODULE } from './constants.mjs';
17
3
 
18
- type Options = readonly [];
19
-
20
- type MessageIds = 'useIsRecordAndHasKey' | 'useHasKey';
21
-
22
- export const preferIsRecordAndHasKey: TSESLint.RuleModule<MessageIds, Options> =
23
- {
24
- meta: {
25
- type: 'suggestion',
26
- docs: {
27
- description:
28
- 'Replace `Object.hasOwn(obj, key)` or `key in obj` with `isRecord(obj) && hasKey(obj, key)` from ts-std-forge',
29
- },
30
- fixable: 'code',
31
- schema: [],
32
- messages: {
33
- useIsRecordAndHasKey:
34
- 'Replace `{{original}}` with `isRecord({{objName}}) && hasKey({{objName}}, {{keyName}})` from ts-std-forge.',
35
- useHasKey:
36
- 'Replace `{{original}}` with `hasKey({{objName}}, {{keyName}})` from ts-std-forge.',
37
- },
38
- },
39
-
40
- create: (context) => {
41
- const sourceCode = context.sourceCode;
42
-
43
- const program = sourceCode.ast;
44
-
45
- const tsStdForgeImports = getImportDeclarations(
46
- program,
47
- TS_STD_FORGE_MODULE,
48
- );
49
-
50
- /**
51
- * Whether the `isRecord(...)` half of the rewrite would be dead code
52
- * because the object already satisfies `hasKey`'s
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
- */
59
- const isKnownRecord = (
60
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
61
- expression: TSESTree.Expression,
62
- ): boolean =>
63
- typeHoldsAtEveryLayer(sourceCode, expression, (type, checker) =>
64
- isAlreadyRecordType(checker, type),
65
- );
66
-
67
- /**
68
- * Whether the object may be an array at some layer of type wrappers.
69
- * `isRecord` rejects an array, so `'0' in xs` would turn from `true` to
70
- * `false`; a widening cast to a type that may be an array is the author
71
- * saying it may be one. Without type information nothing is known, and
72
- * the rule reports as it always has.
73
- */
74
- const mayBeArray = (
75
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
76
- expression: TSESTree.Expression,
77
- ): boolean =>
78
- sourceCode.parserServices?.program != null &&
79
- !typeHoldsAtEveryLayer(
80
- sourceCode,
81
- expression,
82
- (type, checker) => !mayBeArrayType(checker, type),
83
- );
84
-
85
- const mut_nodesToFix: {
86
- node: TSESTree.CallExpression | TSESTree.BinaryExpression;
87
- objExpression: TSESTree.Expression;
88
- keyExpression: TSESTree.Expression;
89
- }[] = [];
90
-
91
- return {
92
- // Handle Object.hasOwn(obj, key)
93
- CallExpression: (node) => {
94
- const callee = skipTypeWrappers(node.callee);
95
-
96
- if (
97
- callee.type !== AST_NODE_TYPES.MemberExpression ||
98
- !isIdentifierNamed(callee.object, 'Object') ||
99
- callee.property.type !== AST_NODE_TYPES.Identifier ||
100
- callee.property.name !== 'hasOwn'
101
- ) {
102
- return;
103
- }
104
-
105
- // Check arguments: Object.hasOwn(obj, key)
106
- if (node.arguments.length !== 2) {
107
- return;
108
- }
109
-
110
- const objArg = node.arguments[0];
111
-
112
- const keyArg = node.arguments[1];
113
-
114
- if (objArg === undefined || keyArg === undefined) {
115
- return;
116
- }
117
-
118
- // Type guard: ensure arguments are Expressions (not SpreadElement)
119
- if (
120
- objArg.type === AST_NODE_TYPES.SpreadElement ||
121
- keyArg.type === AST_NODE_TYPES.SpreadElement ||
122
- mayBeArray(objArg)
123
- ) {
124
- return;
125
- }
126
-
127
- mut_nodesToFix.push({
128
- node,
129
- objExpression: objArg,
130
- keyExpression: keyArg,
131
- });
132
- },
133
-
134
- // Handle key in obj
135
- BinaryExpression: (node) => {
136
- if (node.operator !== 'in') {
137
- return;
138
- }
139
-
140
- const keyExpression = node.left;
141
-
142
- const objExpression = node.right;
143
-
144
- // Type guard: ensure keyExpression is Expression (not PrivateIdentifier)
145
- if (
146
- keyExpression.type === AST_NODE_TYPES.PrivateIdentifier ||
147
- mayBeArray(objExpression)
148
- ) {
149
- return;
150
- }
151
-
152
- mut_nodesToFix.push({
153
- node,
154
- objExpression,
155
- keyExpression,
156
- });
157
- },
158
-
159
- 'Program:exit': () => {
160
- const namedImports = getNamedImports(tsStdForgeImports);
161
-
162
- const rewrites = mut_nodesToFix.map((entry) => {
163
- // A record-typed object makes the `isRecord(...)` conjunct always
164
- // true, so it is left out of the replacement entirely.
165
- const needsIsRecord = !isKnownRecord(entry.objExpression);
166
-
167
- return {
168
- ...entry,
169
- needsIsRecord,
170
- // `isRecord(obj) && hasKey(obj, key)` evaluates `obj` twice.
171
- fixable: !needsIsRecord || isSideEffectFree(entry.objExpression),
172
- };
173
- });
174
-
175
- const fixableRewrites = rewrites.filter(({ fixable }) => fixable);
176
-
177
- const importsToAdd: readonly string[] = SafeArray.isEmpty(
178
- fixableRewrites,
179
- )
180
- ? []
181
- : [
182
- ...(fixableRewrites.some(
183
- ({ needsIsRecord }) => needsIsRecord,
184
- ) && !namedImports.includes('isRecord')
185
- ? ['isRecord']
186
- : []),
187
- ...(namedImports.includes('hasKey') ? [] : ['hasKey']),
188
- ];
189
-
190
- const importCarrier = fixableRewrites[0];
191
-
192
- for (const rewrite of rewrites) {
193
- const { node, objExpression, keyExpression, needsIsRecord } =
194
- rewrite;
195
-
196
- const objText = getArgumentText(sourceCode, objExpression);
197
-
198
- const keyText = getArgumentText(sourceCode, keyExpression);
199
-
200
- const originalText = sourceCode.getText(node);
201
-
202
- context.report({
203
- node,
204
- messageId: needsIsRecord ? 'useIsRecordAndHasKey' : 'useHasKey',
205
- data: {
206
- original: originalText,
207
- objName: objText,
208
- keyName: keyText,
209
- },
210
- fix: !rewrite.fixable
211
- ? undefined
212
- : (fixer) => {
213
- const replacement = needsIsRecord
214
- ? `(isRecord(${objText}) && hasKey(${objText}, ${keyText}))`
215
- : `hasKey(${objText}, ${keyText})`;
216
-
217
- // All import fixes have to live on the first fixable
218
- // report: `insertTextBefore(program, …)` produces
219
- // overlapping ranges otherwise and only one of them would
220
- // survive a pass.
221
- const importFixes =
222
- rewrite === importCarrier && importsToAdd.length > 0
223
- ? buildNamedImportFix(
224
- fixer,
225
- program,
226
- TS_STD_FORGE_MODULE,
227
- importsToAdd,
228
- )
229
- : [];
230
-
231
- return [
232
- ...importFixes,
233
- fixer.replaceText(node, replacement),
234
- ];
235
- },
236
- });
237
- }
238
- },
239
- };
240
- },
241
- defaultOptions: [],
242
- } as const;
243
-
244
- /**
245
- * Whether reading `node` again has no side effects: a variable, `this`, or a
246
- * non-computed property chain on one, type wrappers aside.
247
- */
248
- const isSideEffectFree = (
249
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
250
- wrapped: TSESTree.Node,
251
- ): boolean => {
252
- const node = skipTypeWrappers(wrapped);
253
-
254
- return (
255
- node.type === AST_NODE_TYPES.Identifier ||
256
- node.type === AST_NODE_TYPES.ThisExpression ||
257
- (node.type === AST_NODE_TYPES.MemberExpression &&
258
- !node.computed &&
259
- isSideEffectFree(node.object))
260
- );
261
- };
262
-
263
- /**
264
- * Whether a value of `type` may be an array or a tuple: some member of a union
265
- * or an intersection is one, or the constraint of a deferred type is.
266
- * `object` and `unknown` say nothing either way and are not counted.
267
- */
268
- const mayBeArrayType = (
269
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
270
- checker: ts.TypeChecker,
271
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
272
- type: ts.Type,
273
- ): boolean => {
274
- if (type.isUnionOrIntersection()) {
275
- return type.types.some((member) => mayBeArrayType(checker, member));
276
- }
277
-
278
- if (
279
- (type.flags &
280
- (ts.TypeFlags.TypeParameter |
281
- ts.TypeFlags.IndexedAccess |
282
- ts.TypeFlags.Conditional |
283
- ts.TypeFlags.Substitution)) !==
284
- 0
285
- ) {
286
- const constraint = checker.getBaseConstraintOfType(type);
287
-
288
- return constraint !== undefined && mayBeArrayType(checker, constraint);
289
- }
290
-
291
- return checker.isArrayType(type) || checker.isTupleType(type);
292
- };
293
-
294
- /**
295
- * Whether `type` already satisfies `hasKey`'s `R extends UnknownRecord`
296
- * constraint, so that guarding the call with `isRecord` adds nothing.
297
- *
298
- * `UnknownRecord` is `ReadonlyRecord<string, unknown>`, so a string index
299
- * signature is exactly what it asks for and its value type is `unknown`, which
300
- * anything satisfies. Callables and arrays are excluded because `isRecord`
301
- * rejects them at runtime (`typeof fn === 'object'` is false, `Array.isArray`
302
- * is true), so dropping the guard there would change behavior.
303
- *
304
- * Deliberately conservative: a type TypeScript would accept through an
305
- * *implicit* index signature (an object type alias without one of its own)
306
- * keeps the guard, because the checker does not report such a signature here.
307
- */
308
- const isAlreadyRecordType = (
309
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
310
- checker: ts.TypeChecker,
311
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
312
- type: ts.Type,
313
- ): boolean =>
314
- type.isUnion()
315
- ? type.types.every((member) => isAlreadyRecordType(checker, member))
316
- : checker.getIndexInfoOfType(type, ts.IndexKind.String) !== undefined &&
317
- SafeArray.isEmpty(
318
- checker.getSignaturesOfType(type, ts.SignatureKind.Call),
319
- ) &&
320
- SafeArray.isEmpty(
321
- checker.getSignaturesOfType(type, ts.SignatureKind.Construct),
322
- ) &&
323
- !checker.isArrayType(type) &&
324
- !checker.isTupleType(type);
4
+ export const preferIsRecordAndHasKey = createPreferIsRecordAndHasKey({
5
+ moduleName: TS_STD_FORGE_MODULE,
6
+ });
@@ -62,6 +62,54 @@ describe('prefer-is-record-and-has-key', () => {
62
62
  `,
63
63
  errors: [{ messageId: 'useHasKey' }],
64
64
  },
65
+ {
66
+ name: 'keeps isRecord for an index-signature-carrying callable',
67
+ code: dedent`
68
+ type Callable = { (): void; [key: string]: unknown };
69
+ declare const obj: Callable;
70
+ const ok = Object.hasOwn(obj, 'a');
71
+ `,
72
+ output: dedent`
73
+ import { isRecord, hasKey } from 'ts-std-forge';
74
+ type Callable = { (): void; [key: string]: unknown };
75
+ declare const obj: Callable;
76
+ const ok = (isRecord(obj) && hasKey(obj, 'a'));
77
+ `,
78
+ errors: [{ messageId: 'useIsRecordAndHasKey' }],
79
+ },
80
+ {
81
+ name: 'imports isRecord once when both forms appear in a file',
82
+ code: dedent`
83
+ declare const rec: Record<string, unknown>;
84
+ declare const unk: unknown;
85
+ const a = Object.hasOwn(rec, 'a');
86
+ const b = Object.hasOwn(unk, 'b');
87
+ `,
88
+ output: dedent`
89
+ import { isRecord, hasKey } from 'ts-std-forge';
90
+ declare const rec: Record<string, unknown>;
91
+ declare const unk: unknown;
92
+ const a = hasKey(rec, 'a');
93
+ const b = (isRecord(unk) && hasKey(unk, 'b'));
94
+ `,
95
+ errors: [
96
+ { messageId: 'useHasKey' },
97
+ { messageId: 'useIsRecordAndHasKey' },
98
+ ],
99
+ },
100
+ {
101
+ name: 'wraps a negated Object.hasOwn in parentheses',
102
+ code: dedent`
103
+ const obj = { a: 1 };
104
+ const ok = !Object.hasOwn(obj, 'a');
105
+ `,
106
+ output: dedent`
107
+ import { isRecord, hasKey } from 'ts-std-forge';
108
+ const obj = { a: 1 };
109
+ const ok = !(isRecord(obj) && hasKey(obj, 'a'));
110
+ `,
111
+ errors: [{ messageId: 'useIsRecordAndHasKey' }],
112
+ },
65
113
  {
66
114
  name: 'rewrites `key in obj` too',
67
115
  code: dedent`
@@ -1,121 +1,7 @@
1
- import {
2
- AST_NODE_TYPES,
3
- type TSESLint,
4
- type TSESTree,
5
- } from '@typescript-eslint/utils';
6
- import {
7
- buildNamedImportFix,
8
- getImportDeclarations,
9
- getNamedImports,
10
- getRangeWithParens,
11
- skipTypeWrappers,
12
- } from 'eslint-rule-forge';
13
- import { isIdentifierNamed } from './ast-utils.mjs';
1
+ import { createPreferArrayIsArray } from 'eslint-rule-forge';
14
2
  import { TS_STD_FORGE_MODULE } from './constants.mjs';
15
3
 
16
- type Options = readonly [];
17
-
18
- type MessageIds = 'useSafeArrayIsArray';
19
-
20
- export const preferSafeArrayIsArray: TSESLint.RuleModule<MessageIds, Options> =
21
- {
22
- meta: {
23
- type: 'suggestion',
24
- docs: {
25
- description:
26
- 'Replace `Array.isArray` with `SafeArray.isArray` from ts-std-forge.',
27
- },
28
- fixable: 'code',
29
- schema: [],
30
- messages: {
31
- useSafeArrayIsArray:
32
- 'Replace `Array.isArray` with `SafeArray.isArray` from ts-std-forge.',
33
- },
34
- },
35
-
36
- create: (context) => {
37
- const sourceCode = context.sourceCode;
38
-
39
- const program = sourceCode.ast;
40
-
41
- const tsStdForgeImports = getImportDeclarations(
42
- program,
43
- TS_STD_FORGE_MODULE,
44
- );
45
-
46
- const mut_nodesToFix: TSESTree.CallExpression[] = [];
47
-
48
- return {
49
- CallExpression: (node) => {
50
- if (!isArrayIsArrayCall(node)) {
51
- return;
52
- }
53
-
54
- mut_nodesToFix.push(node);
55
- },
56
- 'Program:exit': () => {
57
- const hasSafeArrayImport =
58
- getNamedImports(tsStdForgeImports).includes('SafeArray');
59
-
60
- // The import fix lives on the first report only:
61
- // `insertTextBefore(program, …)` produces overlapping ranges
62
- // otherwise and only one of them would survive a pass. A first
63
- // report silenced by an `eslint-disable` comment therefore takes
64
- // the import with it.
65
- for (const [index, node] of mut_nodesToFix.entries()) {
66
- context.report({
67
- node,
68
- messageId: 'useSafeArrayIsArray',
69
- fix: (fixer) => {
70
- const importFixes =
71
- index === 0 && !hasSafeArrayImport
72
- ? buildNamedImportFix(fixer, program, TS_STD_FORGE_MODULE, [
73
- 'SafeArray',
74
- ])
75
- : [];
76
-
77
- // Only the callee is replaced, type wrappers included, along
78
- // with the parentheses around it, which its range leaves out;
79
- // the arguments stay as written.
80
- return [
81
- ...importFixes,
82
- fixer.replaceTextRange(
83
- getRangeWithParens(sourceCode, node.callee),
84
- 'SafeArray.isArray',
85
- ),
86
- ];
87
- },
88
- });
89
- }
90
- },
91
- };
92
- },
93
- defaultOptions: [],
94
- } as const;
95
-
96
- const isArrayIsArrayCall = (
97
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
98
- node: TSESTree.CallExpression,
99
- ): boolean => {
100
- const callee = skipTypeWrappers(node.callee);
101
-
102
- if (callee.type !== AST_NODE_TYPES.MemberExpression) {
103
- return false;
104
- }
105
-
106
- const { object, property } = callee;
107
-
108
- if (!isIdentifierNamed(object, 'Array')) {
109
- return false;
110
- }
111
-
112
- if (property.type !== AST_NODE_TYPES.Identifier) {
113
- return false;
114
- }
115
-
116
- if (property.name !== 'isArray') {
117
- return false;
118
- }
119
-
120
- return true;
121
- };
4
+ export const preferSafeArrayIsArray = createPreferArrayIsArray({
5
+ moduleName: TS_STD_FORGE_MODULE,
6
+ namespace: 'SafeArray',
7
+ });
@@ -20,6 +20,13 @@ ruleTester.run('prefer-safe-array-is-array', preferSafeArrayIsArray, {
20
20
  const result = obj.isArray(value);
21
21
  `,
22
22
  },
23
+ {
24
+ name: 'A computed member named by a variable is not `isArray`',
25
+ code: dedent`
26
+ declare const isArray: 'from';
27
+ const result = Array[isArray](value);
28
+ `,
29
+ },
23
30
  ],
24
31
  invalid: [
25
32
  {
@@ -31,7 +38,7 @@ ruleTester.run('prefer-safe-array-is-array', preferSafeArrayIsArray, {
31
38
  import { SafeArray } from 'ts-std-forge';
32
39
  const result = SafeArray.isArray(value);
33
40
  `,
34
- errors: [{ messageId: 'useSafeArrayIsArray' }],
41
+ errors: [{ messageId: 'useIsArray' }],
35
42
  },
36
43
  {
37
44
  name: 'Replace Array.isArray when SafeArray is already imported',
@@ -43,7 +50,7 @@ ruleTester.run('prefer-safe-array-is-array', preferSafeArrayIsArray, {
43
50
  import { SafeArray } from 'ts-std-forge';
44
51
  const result = SafeArray.isArray(value);
45
52
  `,
46
- errors: [{ messageId: 'useSafeArrayIsArray' }],
53
+ errors: [{ messageId: 'useIsArray' }],
47
54
  },
48
55
  {
49
56
  name: 'Sees SafeArray imported by a second ts-std-forge declaration',
@@ -57,7 +64,7 @@ ruleTester.run('prefer-safe-array-is-array', preferSafeArrayIsArray, {
57
64
  import { SafeArray } from 'ts-std-forge';
58
65
  const result = SafeArray.isArray(value);
59
66
  `,
60
- errors: [{ messageId: 'useSafeArrayIsArray' }],
67
+ errors: [{ messageId: 'useIsArray' }],
61
68
  },
62
69
  {
63
70
  name: 'Replace multiple Array.isArray calls, importing once',
@@ -70,10 +77,7 @@ ruleTester.run('prefer-safe-array-is-array', preferSafeArrayIsArray, {
70
77
  const result1 = SafeArray.isArray(value1);
71
78
  const result2 = SafeArray.isArray(value2);
72
79
  `,
73
- errors: [
74
- { messageId: 'useSafeArrayIsArray' },
75
- { messageId: 'useSafeArrayIsArray' },
76
- ],
80
+ errors: [{ messageId: 'useIsArray' }, { messageId: 'useIsArray' }],
77
81
  },
78
82
  ],
79
83
  });
@@ -91,7 +95,7 @@ describe('prefer-safe-array-is-array through type wrappers', () => {
91
95
  import { SafeArray } from 'ts-std-forge';
92
96
  const result = SafeArray.isArray(value);
93
97
  `,
94
- errors: [{ messageId: 'useSafeArrayIsArray' }],
98
+ errors: [{ messageId: 'useIsArray' }],
95
99
  },
96
100
  {
97
101
  name: 'reads `Array` through `!`',
@@ -102,7 +106,7 @@ describe('prefer-safe-array-is-array through type wrappers', () => {
102
106
  import { SafeArray } from 'ts-std-forge';
103
107
  const result = SafeArray.isArray(value);
104
108
  `,
105
- errors: [{ messageId: 'useSafeArrayIsArray' }],
109
+ errors: [{ messageId: 'useIsArray' }],
106
110
  },
107
111
  {
108
112
  name: 'reads the callee through `satisfies`',
@@ -113,7 +117,7 @@ describe('prefer-safe-array-is-array through type wrappers', () => {
113
117
  import { SafeArray } from 'ts-std-forge';
114
118
  const result = SafeArray.isArray(value);
115
119
  `,
116
- errors: [{ messageId: 'useSafeArrayIsArray' }],
120
+ errors: [{ messageId: 'useIsArray' }],
117
121
  },
118
122
  {
119
123
  name: 'keeps a wrapper on the argument',
@@ -124,7 +128,7 @@ describe('prefer-safe-array-is-array through type wrappers', () => {
124
128
  import { SafeArray } from 'ts-std-forge';
125
129
  const result = SafeArray.isArray(value satisfies unknown);
126
130
  `,
127
- errors: [{ messageId: 'useSafeArrayIsArray' }],
131
+ errors: [{ messageId: 'useIsArray' }],
128
132
  },
129
133
  ],
130
134
  });
@@ -143,7 +147,7 @@ describe('prefer-safe-array-is-array with parenthesized operands', () => {
143
147
  import { SafeArray } from 'ts-std-forge';
144
148
  const result = SafeArray.isArray(x);
145
149
  `,
146
- errors: [{ messageId: 'useSafeArrayIsArray' }],
150
+ errors: [{ messageId: 'useIsArray' }],
147
151
  },
148
152
  {
149
153
  name: 'replaces a doubly parenthesized callee of an optional call',
@@ -154,7 +158,7 @@ describe('prefer-safe-array-is-array with parenthesized operands', () => {
154
158
  import { SafeArray } from 'ts-std-forge';
155
159
  const result = SafeArray.isArray?.(x);
156
160
  `,
157
- errors: [{ messageId: 'useSafeArrayIsArray' }],
161
+ errors: [{ messageId: 'useIsArray' }],
158
162
  },
159
163
  {
160
164
  name: 'keeps a parenthesized argument',
@@ -165,7 +169,7 @@ describe('prefer-safe-array-is-array with parenthesized operands', () => {
165
169
  import { SafeArray } from 'ts-std-forge';
166
170
  const result = SafeArray.isArray((a, x));
167
171
  `,
168
- errors: [{ messageId: 'useSafeArrayIsArray' }],
172
+ errors: [{ messageId: 'useIsArray' }],
169
173
  },
170
174
  ],
171
175
  });
@@ -9,9 +9,9 @@ import {
9
9
  getImportDeclarations,
10
10
  getNamedImports,
11
11
  skipTypeWrappers,
12
+ typeHoldsAtEveryLayer,
12
13
  } from 'eslint-rule-forge';
13
14
  import type { ReadonlyRecord } from 'ts-type-forge';
14
- import { typeHoldsAtEveryLayer } from './ast-utils.mjs';
15
15
  import { TS_STD_FORGE_MODULE } from './constants.mjs';
16
16
 
17
17
  type Options = readonly [];