ts-codemod-lib 2.2.3 → 2.2.5

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 (53) hide show
  1. package/README.md +0 -12
  2. package/dist/cmd/append-as-const.mjs +1 -1
  3. package/dist/cmd/append-as-const.mjs.map +1 -1
  4. package/dist/cmd/convert-interface-to-type.mjs +1 -1
  5. package/dist/cmd/convert-interface-to-type.mjs.map +1 -1
  6. package/dist/cmd/convert-to-readonly.mjs +1 -1
  7. package/dist/cmd/convert-to-readonly.mjs.map +1 -1
  8. package/dist/cmd/replace-any-with-unknown.mjs +1 -1
  9. package/dist/cmd/replace-any-with-unknown.mjs.map +1 -1
  10. package/dist/cmd/replace-record-with-unknown-record.mjs +1 -1
  11. package/dist/cmd/replace-record-with-unknown-record.mjs.map +1 -1
  12. package/dist/cmd/run-transformer-cli.mjs +1 -1
  13. package/dist/cmd/run-transformer-cli.mjs.map +1 -1
  14. package/dist/functions/ast-transformers/append-as-const.mjs +1 -1
  15. package/dist/functions/ast-transformers/append-as-const.mjs.map +1 -1
  16. package/dist/functions/ast-transformers/convert-interface-to-type.mjs +4 -4
  17. package/dist/functions/ast-transformers/convert-interface-to-type.mjs.map +1 -1
  18. package/dist/functions/ast-transformers/convert-to-readonly.d.mts.map +1 -1
  19. package/dist/functions/ast-transformers/convert-to-readonly.mjs +12 -13
  20. package/dist/functions/ast-transformers/convert-to-readonly.mjs.map +1 -1
  21. package/dist/functions/ast-transformers/readonly-transformer-helpers/readonly-context.d.mts +3 -3
  22. package/dist/functions/ast-transformers/readonly-transformer-helpers/readonly-context.d.mts.map +1 -1
  23. package/dist/functions/ast-transformers/readonly-transformer-helpers/readonly-context.mjs.map +1 -1
  24. package/dist/functions/ast-transformers/replace-record-with-unknown-record.d.mts.map +1 -1
  25. package/dist/functions/ast-transformers/replace-record-with-unknown-record.mjs +13 -13
  26. package/dist/functions/ast-transformers/replace-record-with-unknown-record.mjs.map +1 -1
  27. package/dist/functions/ast-transformers/transform-source-code.mjs +2 -2
  28. package/dist/functions/ast-transformers/transform-source-code.mjs.map +1 -1
  29. package/dist/functions/functions/is-as-const-node.mjs +1 -1
  30. package/dist/functions/functions/is-as-const-node.mjs.map +1 -1
  31. package/dist/functions/functions/is-readonly-node.d.mts +5 -5
  32. package/dist/functions/functions/is-readonly-node.d.mts.map +1 -1
  33. package/dist/functions/functions/is-readonly-node.mjs +1 -1
  34. package/dist/functions/functions/is-readonly-node.mjs.map +1 -1
  35. package/dist/functions/functions/should-avoid-parentheses-for-readonly.d.mts.map +1 -1
  36. package/dist/functions/functions/should-avoid-parentheses-for-readonly.mjs +1 -4
  37. package/dist/functions/functions/should-avoid-parentheses-for-readonly.mjs.map +1 -1
  38. package/package.json +37 -40
  39. package/src/cmd/append-as-const.mts +1 -1
  40. package/src/cmd/convert-interface-to-type.mts +1 -1
  41. package/src/cmd/convert-to-readonly.mts +1 -1
  42. package/src/cmd/replace-any-with-unknown.mts +1 -1
  43. package/src/cmd/replace-record-with-unknown-record.mts +1 -1
  44. package/src/cmd/run-transformer-cli.mts +1 -1
  45. package/src/functions/ast-transformers/append-as-const.mts +1 -1
  46. package/src/functions/ast-transformers/convert-interface-to-type.mts +4 -4
  47. package/src/functions/ast-transformers/convert-to-readonly.mts +31 -33
  48. package/src/functions/ast-transformers/readonly-transformer-helpers/readonly-context.mts +3 -3
  49. package/src/functions/ast-transformers/replace-record-with-unknown-record.mts +18 -29
  50. package/src/functions/ast-transformers/transform-source-code.mts +2 -2
  51. package/src/functions/functions/is-as-const-node.mts +1 -1
  52. package/src/functions/functions/is-readonly-node.mts +6 -6
  53. package/src/functions/functions/should-avoid-parentheses-for-readonly.mts +1 -5
@@ -8,7 +8,12 @@ import {
8
8
  pipe,
9
9
  } from 'ts-data-forge';
10
10
  import * as tsm from 'ts-morph';
11
- import { type DeepReadonly, type SafeUintWithSmallInt } from 'ts-type-forge';
11
+ import {
12
+ type DeepReadonly,
13
+ type SafeUintWithSmallInt,
14
+ type StrictExclude,
15
+ type StrictExtract,
16
+ } from 'ts-type-forge';
12
17
  import {
13
18
  hasDisableNextLineComment,
14
19
  isAtomicTypeNode,
@@ -397,7 +402,7 @@ const transformNode = (
397
402
  const transformTypeReferenceNode = (
398
403
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
399
404
  node: tsm.TypeReferenceNode,
400
- readonlyContext: Exclude<
405
+ readonlyContext: StrictExclude<
401
406
  ReadonlyContext,
402
407
  Readonly<{ type: 'readonly'; indexedAccessDepth: SafeUintWithSmallInt }>
403
408
  >,
@@ -409,7 +414,7 @@ const transformTypeReferenceNode = (
409
414
 
410
415
  // Array<T> / ReadonlyArray<T> to readonly T[]
411
416
  if (typeNameStr === 'Array' || typeNameStr === 'ReadonlyArray') {
412
- if (!Arr.isArrayOfLength(typeArguments, 1)) {
417
+ if (!Arr.isFixedLengthArray(1, typeArguments)) {
413
418
  throw new Error(
414
419
  `Unexpected number of type arguments "${typeArguments.length}" for ${typeNameStr}.`,
415
420
  );
@@ -442,7 +447,7 @@ const transformTypeReferenceNode = (
442
447
 
443
448
  // Set<T> to ReadonlySet<T>
444
449
  if (typeNameStr === 'Set') {
445
- if (!Arr.isArrayOfLength(typeArguments, 1)) {
450
+ if (!Arr.isFixedLengthArray(1, typeArguments)) {
446
451
  throw new Error(
447
452
  `Unexpected number of type arguments "${typeArguments.length}" for Set.`,
448
453
  );
@@ -468,7 +473,7 @@ const transformTypeReferenceNode = (
468
473
 
469
474
  // Map<T> to ReadonlyMap<T>
470
475
  if (typeNameStr === 'Map') {
471
- if (!Arr.isArrayOfLength(typeArguments, 2)) {
476
+ if (!Arr.isFixedLengthArray(2, typeArguments)) {
472
477
  throw new Error(
473
478
  `Unexpected number of type arguments "${typeArguments.length}" for Map.`,
474
479
  );
@@ -498,7 +503,7 @@ const transformTypeReferenceNode = (
498
503
 
499
504
  // remove unnecessary `Readonly` wrapper or convert to readonly operator
500
505
  if (typeNameStr === 'Readonly') {
501
- if (!Arr.isArrayOfLength(typeArguments, 1)) {
506
+ if (!Arr.isFixedLengthArray(1, typeArguments)) {
502
507
  throw new Error(
503
508
  `Unexpected number of type arguments "${typeArguments.length}" for Readonly.`,
504
509
  );
@@ -592,7 +597,7 @@ const transformTypeReferenceNode = (
592
597
 
593
598
  // DeepReadonly
594
599
  if (typeNameStr === options.DeepReadonly.typeName) {
595
- if (!Arr.isArrayOfLength(typeArguments, 1)) {
600
+ if (!Arr.isFixedLengthArray(1, typeArguments)) {
596
601
  throw new Error(
597
602
  `Unexpected number of type arguments "${typeArguments.length}" for Readonly.`,
598
603
  );
@@ -813,7 +818,7 @@ const transformRestTypeNode = (
813
818
  const transformTypeOperatorNode = (
814
819
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
815
820
  node: tsm.TypeOperatorTypeNode,
816
- readonlyContext: Exclude<
821
+ readonlyContext: StrictExclude<
817
822
  ReadonlyContext,
818
823
  Readonly<{ type: 'readonly'; indexedAccessDepth: SafeUintWithSmallInt }>
819
824
  >,
@@ -852,16 +857,13 @@ const transformTypeOperatorNode = (
852
857
  const transformTypeLiteralNode = (
853
858
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
854
859
  node: tsm.TypeLiteralNode,
855
- readonlyContext: Exclude<
860
+ readonlyContext: StrictExclude<
856
861
  ReadonlyContext,
857
862
  Readonly<{ type: 'readonly'; indexedAccessDepth: SafeUintWithSmallInt }>
858
863
  >,
859
864
  options: ReadonlyTransformerOptionsInternal,
860
865
  ): void => {
861
- if (
862
- options.ignoreEmptyObjectTypes &&
863
- Arr.isArrayOfLength(node.getMembers(), 0)
864
- ) {
866
+ if (options.ignoreEmptyObjectTypes && Arr.isEmpty(node.getMembers())) {
865
867
  return;
866
868
  }
867
869
 
@@ -930,7 +932,7 @@ const transformIndexedAccessTypeNode = (
930
932
  const transformMappedTypeNode = (
931
933
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
932
934
  node: tsm.MappedTypeNode,
933
- readonlyContext: Exclude<
935
+ readonlyContext: StrictExclude<
934
936
  ReadonlyContext,
935
937
  Readonly<{ type: 'readonly'; indexedAccessDepth: SafeUintWithSmallInt }>
936
938
  >,
@@ -1112,7 +1114,7 @@ const transformConstructorParameter = (
1112
1114
  const transformIntersectionTypeNode = (
1113
1115
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
1114
1116
  node: tsm.IntersectionTypeNode,
1115
- readonlyContext: Exclude<
1117
+ readonlyContext: StrictExclude<
1116
1118
  ReadonlyContext,
1117
1119
  Readonly<{ type: 'readonly'; indexedAccessDepth: SafeUintWithSmallInt }>
1118
1120
  >,
@@ -1128,7 +1130,7 @@ const transformIntersectionTypeNode = (
1128
1130
  const transformUnionTypeNode = (
1129
1131
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
1130
1132
  node: tsm.UnionTypeNode,
1131
- readonlyContext: Exclude<
1133
+ readonlyContext: StrictExclude<
1132
1134
  ReadonlyContext,
1133
1135
  Readonly<{ type: 'readonly'; indexedAccessDepth: SafeUintWithSmallInt }>
1134
1136
  >,
@@ -1140,7 +1142,7 @@ const transformUnionTypeNode = (
1140
1142
  const transformUnionOrIntersectionTypeNodeImpl = (
1141
1143
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
1142
1144
  node: tsm.IntersectionTypeNode | tsm.UnionTypeNode,
1143
- readonlyContext: Exclude<
1145
+ readonlyContext: StrictExclude<
1144
1146
  ReadonlyContext,
1145
1147
  Readonly<{ type: 'readonly'; indexedAccessDepth: SafeUintWithSmallInt }>
1146
1148
  >,
@@ -1170,8 +1172,7 @@ const transformUnionOrIntersectionTypeNodeImpl = (
1170
1172
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
1171
1173
  n: tsm.TypeLiteralNode | ReadonlyTypeReferenceNode,
1172
1174
  ): boolean =>
1173
- n.isKind(tsm.SyntaxKind.TypeLiteral) &&
1174
- Arr.isArrayOfLength(n.getMembers(), 0);
1175
+ n.isKind(tsm.SyntaxKind.TypeLiteral) && Arr.isEmpty(n.getMembers());
1175
1176
 
1176
1177
  const nonEmptyTypeLiterals =
1177
1178
  typeLiterals === undefined
@@ -1183,11 +1184,10 @@ const transformUnionOrIntersectionTypeNodeImpl = (
1183
1184
  const emptyTypeLiterals =
1184
1185
  typeLiterals === undefined || !options.ignoreEmptyObjectTypes
1185
1186
  ? undefined
1186
- : typeLiterals.nodes.filter((n) => isEmptyTypeLiteral(n));
1187
+ : typeLiterals.nodes.filter(isEmptyTypeLiteral);
1187
1188
 
1188
1189
  const typeLiteralsWrappedWithReadonly: readonly [] | readonly [string] =
1189
- nonEmptyTypeLiterals === undefined ||
1190
- Arr.isArrayOfLength(nonEmptyTypeLiterals, 0)
1190
+ nonEmptyTypeLiterals === undefined || Arr.isEmpty(nonEmptyTypeLiterals)
1191
1191
  ? ([] as const)
1192
1192
  : ([
1193
1193
  unionToString({
@@ -1282,9 +1282,9 @@ const unionToString = ({
1282
1282
  op: '&' | '|';
1283
1283
  wrapWithReadonly: boolean | string;
1284
1284
  }>): string =>
1285
- Arr.isArrayOfLength(types, 0)
1285
+ Arr.isEmpty(types)
1286
1286
  ? 'never'
1287
- : Arr.isArrayOfLength(types, 1)
1287
+ : Arr.isFixedLengthArray(1, types)
1288
1288
  ? wrapWithReadonly === false
1289
1289
  ? wrapWithParentheses(types[0])
1290
1290
  : (`${isString(wrapWithReadonly) ? wrapWithReadonly : 'Readonly'}<${types[0]}>` as const)
@@ -1351,7 +1351,7 @@ const transformMembers = (
1351
1351
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
1352
1352
  members: readonly tsm.TypeElementTypes[],
1353
1353
  readonlyModifier: 'add' | 'remove',
1354
- readonlyContext: Extract<
1354
+ readonlyContext: StrictExtract<
1355
1355
  ReadonlyContext,
1356
1356
  Readonly<{
1357
1357
  type: 'DeepReadonly' | 'none';
@@ -1392,8 +1392,6 @@ const transformMembers = (
1392
1392
  }
1393
1393
 
1394
1394
  transformNode(mb, readonlyContext, options);
1395
-
1396
- continue;
1397
1395
  }
1398
1396
  };
1399
1397
 
@@ -1401,7 +1399,7 @@ const transformPropertySignature = (
1401
1399
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
1402
1400
  node: tsm.PropertySignature,
1403
1401
  readonlyModifier: 'add' | 'remove',
1404
- readonlyContext: Extract<
1402
+ readonlyContext: StrictExtract<
1405
1403
  ReadonlyContext,
1406
1404
  Readonly<{
1407
1405
  type: 'DeepReadonly' | 'none';
@@ -1411,8 +1409,8 @@ const transformPropertySignature = (
1411
1409
  options: ReadonlyTransformerOptionsInternal,
1412
1410
  ): void => {
1413
1411
  if (
1414
- readonlyContext.type === 'DeepReadonly' ||
1415
- readonlyModifier === 'remove'
1412
+ readonlyModifier === 'remove' ||
1413
+ readonlyContext.type === 'DeepReadonly'
1416
1414
  ) {
1417
1415
  node.setIsReadonly(false);
1418
1416
  } else {
@@ -1432,7 +1430,7 @@ const transformIndexSignatureDeclaration = (
1432
1430
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
1433
1431
  node: tsm.IndexSignatureDeclaration,
1434
1432
  readonlyModifier: 'add' | 'remove',
1435
- readonlyContext: Extract<
1433
+ readonlyContext: StrictExtract<
1436
1434
  ReadonlyContext,
1437
1435
  Readonly<{
1438
1436
  type: 'DeepReadonly' | 'none';
@@ -1448,8 +1446,8 @@ const transformIndexSignatureDeclaration = (
1448
1446
  }
1449
1447
 
1450
1448
  if (
1451
- readonlyContext.type === 'DeepReadonly' ||
1452
- readonlyModifier === 'remove'
1449
+ readonlyModifier === 'remove' ||
1450
+ readonlyContext.type === 'DeepReadonly'
1453
1451
  ) {
1454
1452
  // node.setIsReadonly(false);
1455
1453
  node.toggleModifier('readonly', false);
@@ -1,5 +1,5 @@
1
1
  import { SafeUint, match } from 'ts-data-forge';
2
- import { type SafeUintWithSmallInt } from 'ts-type-forge';
2
+ import { type SafeUintWithSmallInt, type StrictExtract } from 'ts-type-forge';
3
3
 
4
4
  /**
5
5
  * Controls whether to make a layer mutable during recursive transformation
@@ -37,7 +37,7 @@ export const nextReadonlyContext = <
37
37
  indexedAccessDepthChange: 'decr' | 'incr' | 'keep' | 'infinity';
38
38
  }
39
39
  | {
40
- nextReadonlyContextType: Extract<
40
+ nextReadonlyContextType: StrictExtract<
41
41
  ReadonlyContext['type'],
42
42
  'DeepReadonly'
43
43
  >;
@@ -47,7 +47,7 @@ export const nextReadonlyContext = <
47
47
  currentReadonlyContext: Curr;
48
48
  }
49
49
  >): Readonly<{
50
- type: Extract<ReadonlyContext['type'], 'DeepReadonly'> | Next;
50
+ type: Next | StrictExtract<ReadonlyContext['type'], 'DeepReadonly'>;
51
51
  indexedAccessDepth: SafeUintWithSmallInt;
52
52
  }> =>
53
53
  curr.type === 'DeepReadonly'
@@ -24,9 +24,7 @@ export const replaceRecordWithUnknownRecordTransformer =
24
24
  name: TRANSFORMER_NAME,
25
25
  transform: (sourceAst) => {
26
26
  const containerModifications = Arr.toUnshifted(
27
- sourceAst
28
- .getModules()
29
- .map((namespace) => processDeclarations(namespace)),
27
+ sourceAst.getModules().map(processDeclarations),
30
28
  processDeclarations(sourceAst),
31
29
  );
32
30
 
@@ -53,7 +51,7 @@ const processDeclarations = (
53
51
 
54
52
  const interfaceModifications = container
55
53
  .getInterfaces()
56
- .map((interfaceDecl) => processInterfaceDeclaration(interfaceDecl));
54
+ .map(processInterfaceDeclaration);
57
55
 
58
56
  return [...typeAliasModifications, ...interfaceModifications].includes(true);
59
57
  };
@@ -81,10 +79,10 @@ const processInterfaceDeclaration = (
81
79
  // (type parameters, extends clauses, declare modifier, default export).
82
80
  if (
83
81
  hasStringUnknownSignature &&
84
- Arr.isArrayOfLength(interfaceDecl.getProperties(), 0) &&
85
- Arr.isArrayOfLength(indexSignatures, 1) &&
86
- Arr.isArrayOfLength(interfaceDecl.getTypeParameters(), 0) &&
87
- Arr.isArrayOfLength(interfaceDecl.getExtends(), 0) &&
82
+ Arr.isEmpty(interfaceDecl.getProperties()) &&
83
+ Arr.isFixedLengthArray(1, indexSignatures) &&
84
+ Arr.isEmpty(interfaceDecl.getTypeParameters()) &&
85
+ Arr.isEmpty(interfaceDecl.getExtends()) &&
88
86
  !interfaceDecl.hasDeclareKeyword() &&
89
87
  !interfaceDecl.hasDefaultKeyword()
90
88
  ) {
@@ -118,7 +116,7 @@ const visitTypeNode = (node: tsm.TypeNode): boolean => {
118
116
  if (node.getTypeName().getText() === 'Readonly') {
119
117
  const typeArgs = node.getTypeArguments();
120
118
 
121
- if (Arr.isArrayOfLength(typeArgs, 1)) {
119
+ if (Arr.isFixedLengthArray(1, typeArgs)) {
122
120
  const typeArg = typeArgs[0];
123
121
 
124
122
  if (tsm.Node.isTypeLiteral(typeArg)) {
@@ -130,8 +128,8 @@ const visitTypeNode = (node: tsm.TypeNode): boolean => {
130
128
 
131
129
  // Check if it has only one index signature [k: string]: unknown
132
130
  if (
133
- Arr.isArrayOfLength(members, 1) &&
134
- Arr.isArrayOfLength(indexSigs, 1) &&
131
+ Arr.isFixedLengthArray(1, members) &&
132
+ Arr.isFixedLengthArray(1, indexSigs) &&
135
133
  isStringUnknownIndexSignature(indexSigs[0])
136
134
  ) {
137
135
  node.replaceWithText('UnknownRecord');
@@ -158,8 +156,8 @@ const visitTypeNode = (node: tsm.TypeNode): boolean => {
158
156
 
159
157
  // Check if it has only one index signature [k: string]: unknown
160
158
  if (
161
- Arr.isArrayOfLength(members, 1) &&
162
- Arr.isArrayOfLength(indexSigs, 1) &&
159
+ Arr.isFixedLengthArray(1, members) &&
160
+ Arr.isFixedLengthArray(1, indexSigs) &&
163
161
  isStringUnknownIndexSignature(indexSigs[0])
164
162
  ) {
165
163
  node.replaceWithText('UnknownRecord');
@@ -188,10 +186,7 @@ const visitTypeNode = (node: tsm.TypeNode): boolean => {
188
186
 
189
187
  // Recursively visit child type nodes
190
188
  if (tsm.Node.isUnionTypeNode(node) || tsm.Node.isIntersectionTypeNode(node)) {
191
- return node
192
- .getTypeNodes()
193
- .map((typeNode) => visitTypeNode(typeNode))
194
- .includes(true);
189
+ return node.getTypeNodes().map(visitTypeNode).includes(true);
195
190
  }
196
191
 
197
192
  if (tsm.Node.isArrayTypeNode(node)) {
@@ -199,10 +194,7 @@ const visitTypeNode = (node: tsm.TypeNode): boolean => {
199
194
  }
200
195
 
201
196
  if (tsm.Node.isTupleTypeNode(node)) {
202
- return node
203
- .getElements()
204
- .map((element) => visitTypeNode(element))
205
- .includes(true);
197
+ return node.getElements().map(visitTypeNode).includes(true);
206
198
  }
207
199
 
208
200
  if (tsm.Node.isParenthesizedTypeNode(node)) {
@@ -244,7 +236,7 @@ const replaceIfRecordUnknown = (node: tsm.TypeReferenceNode): boolean => {
244
236
  const typeArgs = node.getTypeArguments();
245
237
 
246
238
  if (
247
- Arr.isArrayOfLength(typeArgs, 2) &&
239
+ Arr.isFixedLengthArray(2, typeArgs) &&
248
240
  typeArgs[0].getText() === 'string' &&
249
241
  typeArgs[1].getText() === 'unknown'
250
242
  ) {
@@ -254,13 +246,13 @@ const replaceIfRecordUnknown = (node: tsm.TypeReferenceNode): boolean => {
254
246
  }
255
247
 
256
248
  // Recurse into type arguments (e.g. Record<string, Record<string, unknown>>)
257
- return typeArgs.map((typeArg) => visitTypeNode(typeArg)).includes(true);
249
+ return typeArgs.map(visitTypeNode).includes(true);
258
250
  }
259
251
  case 'Readonly': {
260
252
  // Check if it's Readonly<Record<string, unknown>>
261
253
  const typeArgs = node.getTypeArguments();
262
254
 
263
- if (Arr.isArrayOfLength(typeArgs, 1)) {
255
+ if (Arr.isFixedLengthArray(1, typeArgs)) {
264
256
  const innerType = typeArgs[0];
265
257
 
266
258
  if (tsm.Node.isTypeReference(innerType)) {
@@ -270,7 +262,7 @@ const replaceIfRecordUnknown = (node: tsm.TypeReferenceNode): boolean => {
270
262
  const innerTypeArgs = innerType.getTypeArguments();
271
263
 
272
264
  if (
273
- Arr.isArrayOfLength(innerTypeArgs, 2) &&
265
+ Arr.isFixedLengthArray(2, innerTypeArgs) &&
274
266
  innerTypeArgs[0].getText() === 'string' &&
275
267
  innerTypeArgs[1].getText() === 'unknown'
276
268
  ) {
@@ -290,10 +282,7 @@ const replaceIfRecordUnknown = (node: tsm.TypeReferenceNode): boolean => {
290
282
 
291
283
  default: {
292
284
  // Recurse into type arguments for any other generic type
293
- return node
294
- .getTypeArguments()
295
- .map((typeArg) => visitTypeNode(typeArg))
296
- .includes(true);
285
+ return node.getTypeArguments().map(visitTypeNode).includes(true);
297
286
  }
298
287
  }
299
288
  };
@@ -58,12 +58,12 @@ const shouldSkipFile = (
58
58
 
59
59
  const hasFileIgnoreComment = patterns.some((regex) => regex.test(code));
60
60
 
61
- if (Arr.isArrayOfLength(ignoredTransformers, 0) && !hasFileIgnoreComment) {
61
+ if (!hasFileIgnoreComment && Arr.isEmpty(ignoredTransformers)) {
62
62
  return false;
63
63
  }
64
64
 
65
65
  // Empty array means ignore all transformers (file-level ignore without specific transformers)
66
- if (Arr.isArrayOfLength(ignoredTransformers, 0)) {
66
+ if (Arr.isEmpty(ignoredTransformers)) {
67
67
  return true;
68
68
  }
69
69
 
@@ -43,6 +43,6 @@ export const isAsConstNode = (
43
43
  // and that there are no type arguments (as const doesn't have them)
44
44
  return (
45
45
  typeNameNode.getText() === 'const' &&
46
- Arr.isArrayOfLength(typeNode.getTypeArguments(), 0)
46
+ Arr.isEmpty(typeNode.getTypeArguments())
47
47
  );
48
48
  };
@@ -1,6 +1,6 @@
1
1
  import { Arr, expectType } from 'ts-data-forge';
2
2
  import * as tsm from 'ts-morph';
3
- import { type DeepReadonly } from 'ts-type-forge';
3
+ import { type DeepReadonly, type StrictOmit } from 'ts-type-forge';
4
4
  import { isAtomicTypeNode } from './is-atomic-type-node.mjs';
5
5
 
6
6
  export const isShallowReadonlyTypeNode = (node: tsm.Node): boolean =>
@@ -17,7 +17,7 @@ export const isReadonlyTupleOrArrayTypeNode = (
17
17
  node.getTypeNode().isKind(tsm.SyntaxKind.TupleType));
18
18
 
19
19
  export type ReadonlyArrayTypeNode = tsm.TypeNode &
20
- Omit<tsm.TypeOperatorTypeNode, 'getOperator' | 'getTypeNode'> &
20
+ StrictOmit<tsm.TypeOperatorTypeNode, 'getOperator' | 'getTypeNode'> &
21
21
  Readonly<{
22
22
  getOperator: () => tsm.SyntaxKind.ReadonlyKeyword;
23
23
  getTypeNode: () => tsm.ArrayTypeNode;
@@ -91,7 +91,7 @@ if (import.meta.vitest !== undefined) {
91
91
  }
92
92
 
93
93
  export type ReadonlyTupleTypeNode = tsm.TypeNode &
94
- Omit<tsm.TypeOperatorTypeNode, 'getOperator' | 'getTypeNode'> &
94
+ StrictOmit<tsm.TypeOperatorTypeNode, 'getOperator' | 'getTypeNode'> &
95
95
  Readonly<{
96
96
  getOperator: () => tsm.SyntaxKind.ReadonlyKeyword;
97
97
  getTypeNode: () => tsm.TupleTypeNode;
@@ -153,9 +153,9 @@ if (import.meta.vitest !== undefined) {
153
153
  }
154
154
 
155
155
  export type ReadonlyTypeReferenceNode = tsm.TypeNode &
156
- Omit<tsm.TypeReferenceNode, 'getTypeName' | 'getTypeArguments'> &
156
+ StrictOmit<tsm.TypeReferenceNode, 'getTypeName' | 'getTypeArguments'> &
157
157
  Readonly<{
158
- getTypeName: () => Omit<tsm.Identifier, 'getText'> &
158
+ getTypeName: () => StrictOmit<tsm.Identifier, 'getText'> &
159
159
  Readonly<{
160
160
  getText: () => 'Readonly';
161
161
  }>;
@@ -176,7 +176,7 @@ export const isReadonlyTypeReferenceNode = (
176
176
  return (
177
177
  typeName.isKind(tsm.SyntaxKind.Identifier) &&
178
178
  typeName.getText() === 'Readonly' &&
179
- Arr.isArrayOfLength(typeArguments, 1)
179
+ Arr.isFixedLengthArray(1, typeArguments)
180
180
  );
181
181
  };
182
182
 
@@ -23,10 +23,6 @@ export const shouldAvoidParenthesesForReadonly = (node: tsm.Node): boolean => {
23
23
  // Check if parent is TypePredicate: x is T
24
24
  // In this case, we MUST avoid parentheses: x is readonly T (not x is (readonly T))
25
25
  // because `x is (readonly T)` is a syntax error
26
- if (parent.isKind(tsm.SyntaxKind.TypePredicate)) {
27
- return true;
28
- }
29
-
30
26
  // For all other cases, keep parentheses for safety
31
- return false;
27
+ return parent.isKind(tsm.SyntaxKind.TypePredicate);
32
28
  };