ts-codemod-lib 3.2.0 → 3.3.1

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 (76) hide show
  1. package/README.md +68 -0
  2. package/dist/entry-point.mjs +2 -23
  3. package/dist/entry-point.mjs.map +1 -1
  4. package/dist/functions/ast-transformers/append-as-const.mjs +330 -292
  5. package/dist/functions/ast-transformers/append-as-const.mjs.map +1 -1
  6. package/dist/functions/ast-transformers/convert-interface-to-type.mjs +85 -70
  7. package/dist/functions/ast-transformers/convert-interface-to-type.mjs.map +1 -1
  8. package/dist/functions/ast-transformers/convert-to-readonly.mjs +969 -840
  9. package/dist/functions/ast-transformers/convert-to-readonly.mjs.map +1 -1
  10. package/dist/functions/ast-transformers/enable-no-unchecked-indexed-access.d.mts +65 -0
  11. package/dist/functions/ast-transformers/enable-no-unchecked-indexed-access.d.mts.map +1 -0
  12. package/dist/functions/ast-transformers/enable-no-unchecked-indexed-access.mjs +297 -0
  13. package/dist/functions/ast-transformers/enable-no-unchecked-indexed-access.mjs.map +1 -0
  14. package/dist/functions/ast-transformers/index.d.mts +1 -0
  15. package/dist/functions/ast-transformers/index.d.mts.map +1 -1
  16. package/dist/functions/ast-transformers/index.mjs +10 -11
  17. package/dist/functions/ast-transformers/index.mjs.map +1 -1
  18. package/dist/functions/ast-transformers/readonly-transformer-helpers/compare-union-types.mjs +14 -7
  19. package/dist/functions/ast-transformers/readonly-transformer-helpers/compare-union-types.mjs.map +1 -1
  20. package/dist/functions/ast-transformers/readonly-transformer-helpers/constants.mjs +12 -15
  21. package/dist/functions/ast-transformers/readonly-transformer-helpers/constants.mjs.map +1 -1
  22. package/dist/functions/ast-transformers/readonly-transformer-helpers/group-union-types.mjs +63 -58
  23. package/dist/functions/ast-transformers/readonly-transformer-helpers/group-union-types.mjs.map +1 -1
  24. package/dist/functions/ast-transformers/readonly-transformer-helpers/index.mjs +5 -5
  25. package/dist/functions/ast-transformers/readonly-transformer-helpers/index.mjs.map +1 -1
  26. package/dist/functions/ast-transformers/readonly-transformer-helpers/readonly-context.mjs +15 -20
  27. package/dist/functions/ast-transformers/readonly-transformer-helpers/readonly-context.mjs.map +1 -1
  28. package/dist/functions/ast-transformers/replace-any-with-unknown.mjs +30 -34
  29. package/dist/functions/ast-transformers/replace-any-with-unknown.mjs.map +1 -1
  30. package/dist/functions/ast-transformers/replace-record-with-unknown-record.mjs +197 -156
  31. package/dist/functions/ast-transformers/replace-record-with-unknown-record.mjs.map +1 -1
  32. package/dist/functions/ast-transformers/transform-source-code.mjs +74 -75
  33. package/dist/functions/ast-transformers/transform-source-code.mjs.map +1 -1
  34. package/dist/functions/ast-transformers/types.mjs +2 -2
  35. package/dist/functions/ast-transformers/types.mjs.map +1 -1
  36. package/dist/functions/constants/ignore-comment-text.mjs +11 -13
  37. package/dist/functions/constants/ignore-comment-text.mjs.map +1 -1
  38. package/dist/functions/constants/index.mjs +2 -2
  39. package/dist/functions/constants/index.mjs.map +1 -1
  40. package/dist/functions/functions/has-disable-next-line-comment.mjs +67 -38
  41. package/dist/functions/functions/has-disable-next-line-comment.mjs.map +1 -1
  42. package/dist/functions/functions/index.mjs +11 -11
  43. package/dist/functions/functions/index.mjs.map +1 -1
  44. package/dist/functions/functions/is-as-const-node.mjs +25 -20
  45. package/dist/functions/functions/is-as-const-node.mjs.map +1 -1
  46. package/dist/functions/functions/is-atomic-type-node.mjs +245 -25
  47. package/dist/functions/functions/is-atomic-type-node.mjs.map +1 -1
  48. package/dist/functions/functions/is-conditional-type-distribution-guard.mjs +34 -13
  49. package/dist/functions/functions/is-conditional-type-distribution-guard.mjs.map +1 -1
  50. package/dist/functions/functions/is-readonly-node.mjs +120 -14
  51. package/dist/functions/functions/is-readonly-node.mjs.map +1 -1
  52. package/dist/functions/functions/is-spread-parameter-node.mjs +5 -6
  53. package/dist/functions/functions/is-spread-parameter-node.mjs.map +1 -1
  54. package/dist/functions/functions/remove-parentheses.mjs +4 -4
  55. package/dist/functions/functions/remove-parentheses.mjs.map +1 -1
  56. package/dist/functions/functions/should-avoid-parentheses-for-readonly.mjs +23 -9
  57. package/dist/functions/functions/should-avoid-parentheses-for-readonly.mjs.map +1 -1
  58. package/dist/functions/functions/unwrap-readonly.mjs +3 -4
  59. package/dist/functions/functions/unwrap-readonly.mjs.map +1 -1
  60. package/dist/functions/functions/wrap-with-parentheses.d.mts.map +1 -1
  61. package/dist/functions/functions/wrap-with-parentheses.mjs +54 -29
  62. package/dist/functions/functions/wrap-with-parentheses.mjs.map +1 -1
  63. package/dist/functions/index.mjs +5 -23
  64. package/dist/functions/index.mjs.map +1 -1
  65. package/dist/functions/utils/index.mjs +2 -2
  66. package/dist/functions/utils/index.mjs.map +1 -1
  67. package/dist/functions/utils/replace-with-debug.mjs +4 -6
  68. package/dist/functions/utils/replace-with-debug.mjs.map +1 -1
  69. package/dist/index.mjs +2 -23
  70. package/dist/index.mjs.map +1 -1
  71. package/package.json +11 -12
  72. package/src/functions/ast-transformers/convert-to-readonly.test.mts +10 -1
  73. package/src/functions/ast-transformers/enable-no-unchecked-indexed-access.mts +438 -0
  74. package/src/functions/ast-transformers/enable-no-unchecked-indexed-access.test.mts +619 -0
  75. package/src/functions/ast-transformers/index.mts +1 -0
  76. package/src/functions/functions/wrap-with-parentheses.mts +5 -2
package/README.md CHANGED
@@ -178,6 +178,74 @@ type ReadonlyConfig2 = UnknownRecord;
178
178
  type Data2 = UnknownRecord;
179
179
  ```
180
180
 
181
+ ### 6. `enableNoUncheckedIndexedAccessTransformer`
182
+
183
+ Appends `!` to the index accesses that turning on
184
+ [`noUncheckedIndexedAccess`](https://www.typescriptlang.org/tsconfig/#noUncheckedIndexedAccess)
185
+ would turn into type errors, as a stopgap while the option is being enabled on
186
+ an existing codebase.
187
+
188
+ The rewrite is driven by the type checker rather than by syntax: each file is
189
+ checked twice, once with `noUncheckedIndexedAccess` off and once with it on,
190
+ and `!` is appended only where the option is what added `undefined` to the
191
+ expression's type. Consequently an index whose presence the type already
192
+ guarantees is left untouched — `[T, T][1]`, `[T, T, ...T[]][0]` and
193
+ `({ a: T })['a']` all keep their accesses as they are, as does an element type
194
+ that contained `undefined` to begin with, and so does an access a preceding
195
+ guard has already narrowed. `strictNullChecks` is turned on for both checks,
196
+ because `noUncheckedIndexedAccess` does nothing without it.
197
+
198
+ Both spellings of an index read are covered, because the option widens both:
199
+ `rec['a']` and the dotted `rec.a` of a type carrying an index signature. A
200
+ declared property is left alone either way, even on a type that also has an
201
+ index signature.
202
+
203
+ Options:
204
+
205
+ - `applyLevel`: `'all'` or `'avoidWhereUndefinedIsAllowed'` (default: `'all'`)
206
+ - `'all'`: appends `!` to every read whose type the option widens with `undefined`, so that as few type errors as possible are left behind
207
+ - `'avoidWhereUndefinedIsAllowed'`: additionally leaves the access alone where its contextual type already accepts `undefined` (e.g. `const x: number | undefined = xs[0];`, or an argument of a `(x: number | undefined) => void` parameter), which produces fewer but only load-bearing assertions
208
+
209
+ These positions are never rewritten, whichever level is selected:
210
+
211
+ - **Assignment targets**, where `!` is not valid syntax: `xs[0] = 1`, `xs[0] ??= 1`, `xs[0]++`, `--xs[0]`, `delete rec['a']`, `[xs[0]] = ys`, `for (xs[0] of ys)`.
212
+ - **Accesses that already account for `undefined`**: `xs[0]!`, `xs[0] as T`, `xs[0]?.foo`, `fns[0]?.()`, `xs[0] ?? d`, `xs[0] && d`.
213
+ - **Positions that read the value precisely to find out whether it is there**: `typeof xs[0]`, `!xs[0]`, `xs[0] === undefined`, and the condition of `if` / `while` / `do` / `for` / `?:`, and the subject of `switch` — asserting there would defeat the check (`case undefined:` stops compiling against an asserted subject).
214
+
215
+ Two things it cannot fix, which are left for a human to deal with:
216
+
217
+ - **Destructuring**, since `!` has nowhere to go in `const [head] = xs;` or `const { a } = rec;`.
218
+ - **Compound assignment**, `xs[0] += 1`, whose left-hand side is read as well as written.
219
+
220
+ Two things to know before running it:
221
+
222
+ - **Each file is transformed on its own**, as it is by every transformer in this library. A type that comes from another module does not resolve, and neither does a narrowing that depends on an imported type guard — `if (Arr.isNonEmpty(xs))` does not narrow `xs[0]` here. An unresolved type is never widened, so nothing is asserted there; a lost narrowing is the other way round, and produces an assertion the whole-program check does not need.
223
+ - **Review the diff, and let ESLint clean up after it.** An assertion the compiler can prove unnecessary is reported by [`@typescript-eslint/no-unnecessary-type-assertion`](https://typescript-eslint.io/rules/no-unnecessary-type-assertion/), whose fixer removes it, so running `eslint --fix` over the transformed files takes most of the surplus back out. What no rule can see is an assertion that makes a later check dead — `const v = xs[0]!; if (v === undefined) …` — which is why this is a stopgap rather than a fix.
224
+
225
+ Example:
226
+
227
+ ```ts
228
+ const xs: readonly number[] = [1, 2, 3];
229
+
230
+ const pair: readonly [number, string] = [1, 'a'];
231
+
232
+ const rec: ReadonlyRecord<string, number> = { a: 1 };
233
+
234
+ // Before (with `noUncheckedIndexedAccess` on, only `pair[1]` is known to be there)
235
+ const first: number | undefined = xs[0];
236
+
237
+ const entry: number | undefined = rec['a'];
238
+
239
+ const second: string = pair[1];
240
+
241
+ // After (the two unchecked reads are asserted, the guaranteed index is left alone)
242
+ const first2: number = xs[0]!;
243
+
244
+ const entry2: number = rec['a']!;
245
+
246
+ const second2: string = pair[1];
247
+ ```
248
+
181
249
  ### Disabling Transformers
182
250
 
183
251
  - Nodes on the line immediately following a `// transformer-ignore-next-line` comment will be skipped.
@@ -1,23 +1,2 @@
1
- export { appendAsConstTransformer } from './functions/ast-transformers/append-as-const.mjs';
2
- export { convertInterfaceToTypeTransformer } from './functions/ast-transformers/convert-interface-to-type.mjs';
3
- export { convertToReadonlyTransformer } from './functions/ast-transformers/convert-to-readonly.mjs';
4
- export { compareUnionIntersectionTypes } from './functions/ast-transformers/readonly-transformer-helpers/compare-union-types.mjs';
5
- export { invalidDeepReadonlyTypeName } from './functions/ast-transformers/readonly-transformer-helpers/constants.mjs';
6
- export { groupUnionIntersectionTypes } from './functions/ast-transformers/readonly-transformer-helpers/group-union-types.mjs';
7
- export { nextReadonlyContext } from './functions/ast-transformers/readonly-transformer-helpers/readonly-context.mjs';
8
- export { replaceAnyWithUnknownTransformer } from './functions/ast-transformers/replace-any-with-unknown.mjs';
9
- export { replaceRecordWithUnknownRecordTransformer } from './functions/ast-transformers/replace-record-with-unknown-record.mjs';
10
- export { transformSourceCode } from './functions/ast-transformers/transform-source-code.mjs';
11
- export { IGNORE_FILE_COMMENT_PREFIXES, IGNORE_LINE_COMMENT_PREFIXES } from './functions/constants/ignore-comment-text.mjs';
12
- export { hasDisableNextLineComment } from './functions/functions/has-disable-next-line-comment.mjs';
13
- export { isAsConstNode } from './functions/functions/is-as-const-node.mjs';
14
- export { isAtomicTypeNode } from './functions/functions/is-atomic-type-node.mjs';
15
- export { isConditionalTypeDistributionGuard } from './functions/functions/is-conditional-type-distribution-guard.mjs';
16
- export { isReadonlyArrayTypeNode, isReadonlyTupleOrArrayTypeNode, isReadonlyTupleTypeNode, isReadonlyTypeReferenceNode, isShallowReadonlyTypeNode } from './functions/functions/is-readonly-node.mjs';
17
- export { isSpreadNamedTupleMemberNode, isSpreadParameterNode } from './functions/functions/is-spread-parameter-node.mjs';
18
- export { removeParentheses } from './functions/functions/remove-parentheses.mjs';
19
- export { shouldAvoidParenthesesForReadonly } from './functions/functions/should-avoid-parentheses-for-readonly.mjs';
20
- export { unwrapReadonlyTypeArgText } from './functions/functions/unwrap-readonly.mjs';
21
- export { wrapWithParentheses } from './functions/functions/wrap-with-parentheses.mjs';
22
- export { replaceNodeWithDebugPrint } from './functions/utils/replace-with-debug.mjs';
23
- //# sourceMappingURL=entry-point.mjs.map
1
+ export * from './index.mjs';
2
+ //# sourceMappingURL=entry-point.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"entry-point.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"entry-point.mjs","sourceRoot":"","sources":["../src/entry-point.mts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC"}