ts-codemod-lib 3.2.0 → 3.3.0

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
@@ -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,6 +1,7 @@
1
1
  export { appendAsConstTransformer } from './functions/ast-transformers/append-as-const.mjs';
2
2
  export { convertInterfaceToTypeTransformer } from './functions/ast-transformers/convert-interface-to-type.mjs';
3
3
  export { convertToReadonlyTransformer } from './functions/ast-transformers/convert-to-readonly.mjs';
4
+ export { enableNoUncheckedIndexedAccessTransformer } from './functions/ast-transformers/enable-no-unchecked-indexed-access.mjs';
4
5
  export { compareUnionIntersectionTypes } from './functions/ast-transformers/readonly-transformer-helpers/compare-union-types.mjs';
5
6
  export { invalidDeepReadonlyTypeName } from './functions/ast-transformers/readonly-transformer-helpers/constants.mjs';
6
7
  export { groupUnionIntersectionTypes } from './functions/ast-transformers/readonly-transformer-helpers/group-union-types.mjs';
@@ -1 +1 @@
1
- {"version":3,"file":"entry-point.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"entry-point.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,65 @@
1
+ import { type DeepReadonly } from 'ts-type-forge';
2
+ import { type TsMorphTransformer } from './types.mjs';
3
+ /**
4
+ * Appends `!` to the index accesses that turning on the
5
+ * [`noUncheckedIndexedAccess`](https://www.typescriptlang.org/tsconfig/#noUncheckedIndexedAccess)
6
+ * compiler option would turn into type errors, as a stopgap while the option
7
+ * is being enabled.
8
+ *
9
+ * The rewrite is driven by the type checker rather than by syntax: the file is
10
+ * checked twice, once with `noUncheckedIndexedAccess` off and once with it on,
11
+ * and `!` is appended only where the option is what added `undefined` to the
12
+ * expression's type. Consequently an index whose presence the type already
13
+ * guarantees keeps its access untouched — `[T, T][1]`, `[T, T, ...T[]][0]`,
14
+ * and `({ a: T })['a']` are all left as they are, as is an element type that
15
+ * contained `undefined` to begin with.
16
+ *
17
+ * Both spellings of an index read are covered, because the option widens both:
18
+ * `rec['a']` and the dotted `rec.a` of a type carrying an index signature.
19
+ *
20
+ * `strictNullChecks` is turned on for both checks, because
21
+ * `noUncheckedIndexedAccess` does nothing without it and the project the
22
+ * transformer is handed may not have it on.
23
+ *
24
+ * The result is a stopgap, not a fix, and wants reviewing. A file transformed
25
+ * on its own — which is how {@link transformSourceCode} drives every
26
+ * transformer here — resolves neither a type from another module nor a
27
+ * narrowing that depends on an imported type guard, and the latter produces
28
+ * assertions a whole-program check does not need. Most of those are reported
29
+ * by `@typescript-eslint/no-unnecessary-type-assertion`, whose fixer removes
30
+ * them; what no rule can see is an assertion that makes a later check dead,
31
+ * as the `if` of `const v = xs[0]!; if (v === undefined) …` now is.
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * declare const xs: readonly number[];
36
+ * declare const pair: readonly [number, number];
37
+ * declare const rec: Record<string, number>;
38
+ * declare const known: { a: number };
39
+ *
40
+ * xs[0].toFixed(); // -> xs[0]!.toFixed();
41
+ * rec['a'].toFixed(); // -> rec['a']!.toFixed();
42
+ * rec.a.toFixed(); // -> rec.a!.toFixed();
43
+ * pair[1].toFixed(); // -> pair[1].toFixed(); (guaranteed to exist)
44
+ * known.a.toFixed(); // -> known.a.toFixed(); (guaranteed to exist)
45
+ * ```
46
+ */
47
+ export declare const enableNoUncheckedIndexedAccessTransformer: (options?: EnableNoUncheckedIndexedAccessTransformerOptions) => TsMorphTransformer;
48
+ export type EnableNoUncheckedIndexedAccessTransformerOptions = DeepReadonly<{
49
+ /**
50
+ * How eagerly `!` is appended.
51
+ *
52
+ * - `"all"`: append it to every read whose type `noUncheckedIndexedAccess`
53
+ * widens with `undefined`, so that as few type errors as possible remain.
54
+ * - `"avoidWhereUndefinedIsAllowed"`: additionally leave the access alone
55
+ * where its contextual type already accepts `undefined` (e.g.
56
+ * `const x: number | undefined = xs[0];` or an argument of a
57
+ * `(x: number | undefined) => void` parameter), which produces fewer but
58
+ * only load-bearing assertions.
59
+ *
60
+ * @default "all"
61
+ */
62
+ applyLevel?: 'all' | 'avoidWhereUndefinedIsAllowed';
63
+ debug?: boolean;
64
+ }>;
65
+ //# sourceMappingURL=enable-no-unchecked-indexed-access.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enable-no-unchecked-indexed-access.d.mts","sourceRoot":"","sources":["../../../src/functions/ast-transformers/enable-no-unchecked-indexed-access.mts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAItD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,eAAO,MAAM,yCAAyC,aAC1C,gDAAgD,KACzD,kBAyBF,CAAC;AAEF,MAAM,MAAM,gDAAgD,GAAG,YAAY,CAAC;IAC1E;;;;;;;;;;;;OAYG;IACH,UAAU,CAAC,EAAE,KAAK,GAAG,8BAA8B,CAAC;IAEpD,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC,CAAC"}
@@ -0,0 +1,155 @@
1
+ import { Arr } from 'ts-data-forge';
2
+ import * as tsm from 'ts-morph';
3
+ import { hasDisableNextLineComment } from '../functions/has-disable-next-line-comment.mjs';
4
+ import '../functions/is-atomic-type-node.mjs';
5
+
6
+ const TRANSFORMER_NAME = "enable-no-unchecked-indexed-access";
7
+ const enableNoUncheckedIndexedAccessTransformer = (options) => {
8
+ const optionsInternal = {
9
+ applyLevel: options?.applyLevel ?? "all",
10
+ debugPrint: options?.debug === true ? console.debug : () => {
11
+ }
12
+ };
13
+ return {
14
+ name: TRANSFORMER_NAME,
15
+ transform: (sourceAst) => {
16
+ const positions = collectAssertionPositions(sourceAst, optionsInternal);
17
+ if (Arr.isEmpty(positions)) {
18
+ return;
19
+ }
20
+ sourceAst.replaceText(
21
+ [0, sourceAst.getEnd()],
22
+ insertNonNullAssertions(sourceAst.getFullText(), positions)
23
+ );
24
+ }
25
+ };
26
+ };
27
+ const collectAssertionPositions = (sourceAst, options) => {
28
+ const candidates = [
29
+ ...sourceAst.getDescendantsOfKind(tsm.SyntaxKind.ElementAccessExpression),
30
+ ...sourceAst.getDescendantsOfKind(tsm.SyntaxKind.PropertyAccessExpression)
31
+ ].filter(
32
+ (node) => isAssertableReadPosition(node) && !isIgnoredByComment(node)
33
+ );
34
+ if (Arr.isEmpty(candidates)) {
35
+ return [];
36
+ }
37
+ const project = sourceAst.getProject();
38
+ const originalOptions = project.getCompilerOptions();
39
+ try {
40
+ project.compilerOptions.set({
41
+ strictNullChecks: true,
42
+ noUncheckedIndexedAccess: true
43
+ });
44
+ const widened = candidates.filter(
45
+ (node) => typeIncludesUndefined(node.getType()) && !(options.applyLevel === "avoidWhereUndefinedIsAllowed" && contextAcceptsUndefined(node))
46
+ );
47
+ if (Arr.isEmpty(widened)) {
48
+ return [];
49
+ }
50
+ project.compilerOptions.set({ noUncheckedIndexedAccess: false });
51
+ return widened.filter((node) => !typeIncludesUndefined(node.getType())).map((node) => {
52
+ options.debugPrint(`${node.getText()} -> ${node.getText()}!`);
53
+ return node.getEnd();
54
+ });
55
+ } finally {
56
+ project.compilerOptions.set({
57
+ ...originalOptions,
58
+ strictNullChecks: originalOptions.strictNullChecks,
59
+ noUncheckedIndexedAccess: originalOptions.noUncheckedIndexedAccess
60
+ });
61
+ }
62
+ };
63
+ const insertNonNullAssertions = (fullText, positions) => {
64
+ const mut_parts = [];
65
+ let mut_restStart = 0;
66
+ for (const position of positions.toSorted((a, b) => a - b)) {
67
+ mut_parts.push(fullText.slice(mut_restStart, position), "!");
68
+ mut_restStart = position;
69
+ }
70
+ mut_parts.push(fullText.slice(mut_restStart));
71
+ return mut_parts.join("");
72
+ };
73
+ const typeIncludesUndefined = (type) => type.isUndefined() || type.isUnion() && type.getUnionTypes().some((t) => t.isUndefined());
74
+ const contextAcceptsUndefined = (node) => {
75
+ const contextualType = node.getContextualType();
76
+ return contextualType !== void 0 && (contextualType.isAny() || contextualType.isUnknown() || typeIncludesUndefined(contextualType));
77
+ };
78
+ const isIgnoredByComment = (node) => {
79
+ if (hasDisableNextLineComment(node, TRANSFORMER_NAME)) {
80
+ return true;
81
+ }
82
+ const statement = node.getFirstAncestor(
83
+ (ancestor) => tsm.Node.isStatement(ancestor)
84
+ );
85
+ return statement !== void 0 && hasDisableNextLineComment(statement, TRANSFORMER_NAME);
86
+ };
87
+ const isAssertableReadPosition = (node) => {
88
+ const parent = node.getParent();
89
+ if (parent === void 0) {
90
+ return false;
91
+ }
92
+ return !isAlreadyAssertedOrGuarded(node, parent) && !isWriteTarget(node, parent) && !isTestedForAbsence(node, parent);
93
+ };
94
+ const isAlreadyAssertedOrGuarded = (node, parent) => {
95
+ if (parent.isKind(tsm.SyntaxKind.NonNullExpression) || parent.isKind(tsm.SyntaxKind.AsExpression) || parent.isKind(tsm.SyntaxKind.TypeAssertionExpression)) {
96
+ return true;
97
+ }
98
+ if (isInOptionalChain(node)) {
99
+ return true;
100
+ }
101
+ return (parent.isKind(tsm.SyntaxKind.PropertyAccessExpression) || parent.isKind(tsm.SyntaxKind.ElementAccessExpression) || parent.isKind(tsm.SyntaxKind.CallExpression)) && parent.hasQuestionDotToken() && parent.getExpression() === node;
102
+ };
103
+ const isInOptionalChain = (node) => {
104
+ if (node.isKind(tsm.SyntaxKind.PropertyAccessExpression) || node.isKind(tsm.SyntaxKind.ElementAccessExpression) || node.isKind(tsm.SyntaxKind.CallExpression)) {
105
+ return node.hasQuestionDotToken() || isInOptionalChain(node.getExpression());
106
+ }
107
+ return node.isKind(tsm.SyntaxKind.NonNullExpression) && isInOptionalChain(node.getExpression());
108
+ };
109
+ const isWriteTarget = (node, parent) => {
110
+ if (parent.isKind(tsm.SyntaxKind.DeleteExpression) || parent.isKind(tsm.SyntaxKind.PostfixUnaryExpression)) {
111
+ return true;
112
+ }
113
+ if (parent.isKind(tsm.SyntaxKind.PrefixUnaryExpression)) {
114
+ const operator = parent.getOperatorToken();
115
+ return operator === tsm.SyntaxKind.PlusPlusToken || operator === tsm.SyntaxKind.MinusMinusToken;
116
+ }
117
+ if (parent.isKind(tsm.SyntaxKind.ForOfStatement) || parent.isKind(tsm.SyntaxKind.ForInStatement)) {
118
+ return parent.getInitializer() === node;
119
+ }
120
+ return parent.isKind(tsm.SyntaxKind.BinaryExpression) && parent.getLeft() === node && isAssignmentOperatorKind(parent.getOperatorToken().getKind());
121
+ };
122
+ const isTestedForAbsence = (node, parent) => {
123
+ if (parent.isKind(tsm.SyntaxKind.TypeOfExpression)) {
124
+ return true;
125
+ }
126
+ if (parent.isKind(tsm.SyntaxKind.PrefixUnaryExpression)) {
127
+ return parent.getOperatorToken() === tsm.SyntaxKind.ExclamationToken;
128
+ }
129
+ if (parent.isKind(tsm.SyntaxKind.BinaryExpression)) {
130
+ const operator = parent.getOperatorToken().getKind();
131
+ return equalityOperatorKinds.includes(operator) || parent.getLeft() === node && shortCircuitOperatorKinds.includes(operator);
132
+ }
133
+ if (parent.isKind(tsm.SyntaxKind.IfStatement) || parent.isKind(tsm.SyntaxKind.WhileStatement) || parent.isKind(tsm.SyntaxKind.DoStatement) || parent.isKind(tsm.SyntaxKind.SwitchStatement)) {
134
+ return parent.getExpression() === node;
135
+ }
136
+ if (parent.isKind(tsm.SyntaxKind.ForStatement)) {
137
+ return parent.getCondition() === node;
138
+ }
139
+ return parent.isKind(tsm.SyntaxKind.ConditionalExpression) && parent.getCondition() === node;
140
+ };
141
+ const isAssignmentOperatorKind = (kind) => tsm.SyntaxKind.FirstAssignment <= kind && kind <= tsm.SyntaxKind.LastAssignment;
142
+ const equalityOperatorKinds = [
143
+ tsm.SyntaxKind.EqualsEqualsToken,
144
+ tsm.SyntaxKind.EqualsEqualsEqualsToken,
145
+ tsm.SyntaxKind.ExclamationEqualsToken,
146
+ tsm.SyntaxKind.ExclamationEqualsEqualsToken
147
+ ];
148
+ const shortCircuitOperatorKinds = [
149
+ tsm.SyntaxKind.QuestionQuestionToken,
150
+ tsm.SyntaxKind.AmpersandAmpersandToken,
151
+ tsm.SyntaxKind.BarBarToken
152
+ ];
153
+
154
+ export { enableNoUncheckedIndexedAccessTransformer };
155
+ //# sourceMappingURL=enable-no-unchecked-indexed-access.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enable-no-unchecked-indexed-access.mjs","sources":["../../../src/functions/ast-transformers/enable-no-unchecked-indexed-access.mts"],"sourcesContent":["import { Arr } from 'ts-data-forge';\nimport * as tsm from 'ts-morph';\nimport { type DeepReadonly } from 'ts-type-forge';\nimport { hasDisableNextLineComment } from '../functions/index.mjs';\nimport { type TsMorphTransformer } from './types.mjs';\n\nconst TRANSFORMER_NAME = 'enable-no-unchecked-indexed-access';\n\n/**\n * Appends `!` to the index accesses that turning on the\n * [`noUncheckedIndexedAccess`](https://www.typescriptlang.org/tsconfig/#noUncheckedIndexedAccess)\n * compiler option would turn into type errors, as a stopgap while the option\n * is being enabled.\n *\n * The rewrite is driven by the type checker rather than by syntax: the file is\n * checked twice, once with `noUncheckedIndexedAccess` off and once with it on,\n * and `!` is appended only where the option is what added `undefined` to the\n * expression's type. Consequently an index whose presence the type already\n * guarantees keeps its access untouched — `[T, T][1]`, `[T, T, ...T[]][0]`,\n * and `({ a: T })['a']` are all left as they are, as is an element type that\n * contained `undefined` to begin with.\n *\n * Both spellings of an index read are covered, because the option widens both:\n * `rec['a']` and the dotted `rec.a` of a type carrying an index signature.\n *\n * `strictNullChecks` is turned on for both checks, because\n * `noUncheckedIndexedAccess` does nothing without it and the project the\n * transformer is handed may not have it on.\n *\n * The result is a stopgap, not a fix, and wants reviewing. A file transformed\n * on its own — which is how {@link transformSourceCode} drives every\n * transformer here — resolves neither a type from another module nor a\n * narrowing that depends on an imported type guard, and the latter produces\n * assertions a whole-program check does not need. Most of those are reported\n * by `@typescript-eslint/no-unnecessary-type-assertion`, whose fixer removes\n * them; what no rule can see is an assertion that makes a later check dead,\n * as the `if` of `const v = xs[0]!; if (v === undefined) …` now is.\n *\n * @example\n * ```ts\n * declare const xs: readonly number[];\n * declare const pair: readonly [number, number];\n * declare const rec: Record<string, number>;\n * declare const known: { a: number };\n *\n * xs[0].toFixed(); // -> xs[0]!.toFixed();\n * rec['a'].toFixed(); // -> rec['a']!.toFixed();\n * rec.a.toFixed(); // -> rec.a!.toFixed();\n * pair[1].toFixed(); // -> pair[1].toFixed(); (guaranteed to exist)\n * known.a.toFixed(); // -> known.a.toFixed(); (guaranteed to exist)\n * ```\n */\nexport const enableNoUncheckedIndexedAccessTransformer = (\n options?: EnableNoUncheckedIndexedAccessTransformerOptions,\n): TsMorphTransformer => {\n const optionsInternal: EnableNoUncheckedIndexedAccessTransformerOptionsInternal =\n {\n applyLevel: options?.applyLevel ?? 'all',\n debugPrint: options?.debug === true ? console.debug : () => {},\n } as const;\n\n return {\n name: TRANSFORMER_NAME,\n transform: (sourceAst) => {\n const positions = collectAssertionPositions(sourceAst, optionsInternal);\n\n if (Arr.isEmpty(positions)) {\n return;\n }\n\n // One text edit for the whole file: `insertText` forgets every\n // descendant node, so inserting one `!` at a time would reparse the\n // file once per assertion.\n sourceAst.replaceText(\n [0, sourceAst.getEnd()],\n insertNonNullAssertions(sourceAst.getFullText(), positions),\n );\n },\n };\n};\n\nexport type EnableNoUncheckedIndexedAccessTransformerOptions = DeepReadonly<{\n /**\n * How eagerly `!` is appended.\n *\n * - `\"all\"`: append it to every read whose type `noUncheckedIndexedAccess`\n * widens with `undefined`, so that as few type errors as possible remain.\n * - `\"avoidWhereUndefinedIsAllowed\"`: additionally leave the access alone\n * where its contextual type already accepts `undefined` (e.g.\n * `const x: number | undefined = xs[0];` or an argument of a\n * `(x: number | undefined) => void` parameter), which produces fewer but\n * only load-bearing assertions.\n *\n * @default \"all\"\n */\n applyLevel?: 'all' | 'avoidWhereUndefinedIsAllowed';\n\n debug?: boolean;\n}>;\n\ntype EnableNoUncheckedIndexedAccessTransformerOptionsInternal = DeepReadonly<{\n applyLevel: 'all' | 'avoidWhereUndefinedIsAllowed';\n debugPrint: (...args: readonly unknown[]) => void;\n}>;\n\n/**\n * An index read that `noUncheckedIndexedAccess` can widen: an element access,\n * and the dotted spelling of the same thing on a type with an index signature\n * (`rec.a`, which the option widens exactly as it widens `rec['a']`).\n */\ntype IndexReadExpression =\n tsm.ElementAccessExpression | tsm.PropertyAccessExpression;\n\n/**\n * Returns the positions at which a `!` has to be inserted, i.e. the end\n * offsets of the reads whose type `noUncheckedIndexedAccess` widens with\n * `undefined`.\n */\nconst collectAssertionPositions = (\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n sourceAst: tsm.SourceFile,\n options: EnableNoUncheckedIndexedAccessTransformerOptionsInternal,\n): readonly number[] => {\n const candidates: readonly IndexReadExpression[] = [\n ...sourceAst.getDescendantsOfKind(tsm.SyntaxKind.ElementAccessExpression),\n ...sourceAst.getDescendantsOfKind(tsm.SyntaxKind.PropertyAccessExpression),\n ].filter(\n (node) => isAssertableReadPosition(node) && !isIgnoredByComment(node),\n );\n\n if (Arr.isEmpty(candidates)) {\n return [];\n }\n\n const project = sourceAst.getProject();\n\n const originalOptions = project.getCompilerOptions();\n\n try {\n // With the option on first, because that narrows the candidates down to\n // the few reads worth checking a second time. `strictNullChecks` comes\n // with it: `noUncheckedIndexedAccess` does nothing without it, and the\n // project handed to the transformer may not have it on.\n project.compilerOptions.set({\n strictNullChecks: true,\n noUncheckedIndexedAccess: true,\n });\n\n const widened = candidates.filter(\n (node) =>\n typeIncludesUndefined(node.getType()) &&\n !(\n options.applyLevel === 'avoidWhereUndefinedIsAllowed' &&\n contextAcceptsUndefined(node)\n ),\n );\n\n if (Arr.isEmpty(widened)) {\n return [];\n }\n\n project.compilerOptions.set({ noUncheckedIndexedAccess: false });\n\n // What is possibly `undefined` without the option too is not the option's\n // doing — the element type says so, or the expression is erroneous (an\n // unresolved import resolves to `any`, which is never widened).\n return widened\n .filter((node) => !typeIncludesUndefined(node.getType()))\n .map((node) => {\n options.debugPrint(`${node.getText()} -> ${node.getText()}!`);\n\n return node.getEnd();\n });\n } finally {\n // `set` merges, so the two options overridden above have to be named\n // explicitly: they are absent from `originalOptions` when the project did\n // not carry them, and would otherwise stay overridden for whichever\n // transformer runs next.\n project.compilerOptions.set({\n ...originalOptions,\n strictNullChecks: originalOptions.strictNullChecks,\n noUncheckedIndexedAccess: originalOptions.noUncheckedIndexedAccess,\n });\n }\n};\n\nconst insertNonNullAssertions = (\n fullText: string,\n positions: readonly number[],\n): string => {\n const mut_parts: string[] = [];\n\n let mut_restStart: number = 0;\n\n for (const position of positions.toSorted((a, b) => a - b)) {\n mut_parts.push(fullText.slice(mut_restStart, position), '!');\n\n mut_restStart = position;\n }\n\n mut_parts.push(fullText.slice(mut_restStart));\n\n return mut_parts.join('');\n};\n\nconst typeIncludesUndefined = (\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n type: tsm.Type,\n): boolean =>\n type.isUndefined() ||\n (type.isUnion() && type.getUnionTypes().some((t) => t.isUndefined()));\n\n/**\n * Whether the position the access sits in already accepts `undefined`, in\n * which case `noUncheckedIndexedAccess` reports nothing there and the\n * assertion would only be noise.\n */\nconst contextAcceptsUndefined = (\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n node: IndexReadExpression,\n): boolean => {\n const contextualType = node.getContextualType();\n\n return (\n contextualType !== undefined &&\n (contextualType.isAny() ||\n contextualType.isUnknown() ||\n typeIncludesUndefined(contextualType))\n );\n};\n\nconst isIgnoredByComment = (\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n node: IndexReadExpression,\n): boolean => {\n if (hasDisableNextLineComment(node, TRANSFORMER_NAME)) {\n return true;\n }\n\n // An index access is rarely the first thing on its own line, so the comment\n // that covers it is the one above the statement it appears in.\n const statement = node.getFirstAncestor((ancestor) =>\n tsm.Node.isStatement(ancestor),\n );\n\n return (\n statement !== undefined &&\n hasDisableNextLineComment(statement, TRANSFORMER_NAME)\n );\n};\n\n/**\n * Whether a `!` appended to this access would be both syntactically valid and\n * meaningful.\n *\n * Note that most write positions need no rule of their own: TypeScript gives\n * the target of an assignment the element type without `undefined`, so the\n * type comparison in {@link collectAssertionPositions} already passes them\n * over. The exceptions handled here are the ones it does widen.\n */\nconst isAssertableReadPosition = (\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n node: IndexReadExpression,\n): boolean => {\n const parent = node.getParent();\n\n if (parent === undefined) {\n return false;\n }\n\n return (\n !isAlreadyAssertedOrGuarded(node, parent) &&\n !isWriteTarget(node, parent) &&\n !isTestedForAbsence(node, parent)\n );\n};\n\n/** `xs[0]!`, `xs[0] as T`, `<T>xs[0]`, `xs[0]?.foo`, `xs[0]?.()`, `xs[0]?.[1]`. */\nconst isAlreadyAssertedOrGuarded = (\n node: tsm.Node,\n parent: tsm.Node,\n): boolean => {\n if (\n parent.isKind(tsm.SyntaxKind.NonNullExpression) ||\n parent.isKind(tsm.SyntaxKind.AsExpression) ||\n parent.isKind(tsm.SyntaxKind.TypeAssertionExpression)\n ) {\n return true;\n }\n\n if (isInOptionalChain(node)) {\n return true;\n }\n\n // Only the object of the optional chain is guarded; an index argument such\n // as the `xs[0]` of `m?.[xs[0]]` is not.\n return (\n (parent.isKind(tsm.SyntaxKind.PropertyAccessExpression) ||\n parent.isKind(tsm.SyntaxKind.ElementAccessExpression) ||\n parent.isKind(tsm.SyntaxKind.CallExpression)) &&\n parent.hasQuestionDotToken() &&\n parent.getExpression() === node\n );\n};\n\n/**\n * Whether the read sits in an optional chain that can short-circuit before it,\n * as the `xs[0]?.toFixed` of `xs[0]?.toFixed()` does. `undefined` in such a\n * type comes from the chain rather than from the index, and the chain already\n * accounts for it; asserting there would assert away the author's own `?.`.\n *\n * Parentheses end a chain (`(a?.b).c` is not one), which is why the walk stops\n * at anything that is not a chain link.\n */\nconst isInOptionalChain = (node: tsm.Node): boolean => {\n if (\n node.isKind(tsm.SyntaxKind.PropertyAccessExpression) ||\n node.isKind(tsm.SyntaxKind.ElementAccessExpression) ||\n node.isKind(tsm.SyntaxKind.CallExpression)\n ) {\n return (\n node.hasQuestionDotToken() || isInOptionalChain(node.getExpression())\n );\n }\n\n return (\n node.isKind(tsm.SyntaxKind.NonNullExpression) &&\n isInOptionalChain(node.getExpression())\n );\n};\n\n/**\n * Positions that write the value rather than read it, where `!` cannot be\n * appended at all: an assignment target is not an expression (`xs[0]! = 1`\n * does not parse as an assignment), and `delete xs[0]!` and `xs[0]!++` are\n * equally invalid.\n *\n * `xs[0] += 1` is the one case this leaves a type error behind: its left-hand\n * side is read as well as written, so `noUncheckedIndexedAccess` reports it,\n * but no assertion can be spelled there.\n */\nconst isWriteTarget = (node: tsm.Node, parent: tsm.Node): boolean => {\n if (\n parent.isKind(tsm.SyntaxKind.DeleteExpression) ||\n parent.isKind(tsm.SyntaxKind.PostfixUnaryExpression)\n ) {\n return true;\n }\n\n if (parent.isKind(tsm.SyntaxKind.PrefixUnaryExpression)) {\n const operator = parent.getOperatorToken();\n\n return (\n operator === tsm.SyntaxKind.PlusPlusToken ||\n operator === tsm.SyntaxKind.MinusMinusToken\n );\n }\n\n if (\n parent.isKind(tsm.SyntaxKind.ForOfStatement) ||\n parent.isKind(tsm.SyntaxKind.ForInStatement)\n ) {\n return parent.getInitializer() === node;\n }\n\n return (\n parent.isKind(tsm.SyntaxKind.BinaryExpression) &&\n parent.getLeft() === node &&\n isAssignmentOperatorKind(parent.getOperatorToken().getKind())\n );\n};\n\n/**\n * Positions that read the access precisely to find out whether the value is\n * there — a truthiness check, an equality comparison, `??`, `typeof`. None of\n * them is an error under `noUncheckedIndexedAccess`, and asserting inside one\n * would defeat the check it performs (`case undefined:` would stop compiling\n * against an asserted `switch` subject).\n */\nconst isTestedForAbsence = (node: tsm.Node, parent: tsm.Node): boolean => {\n if (parent.isKind(tsm.SyntaxKind.TypeOfExpression)) {\n return true;\n }\n\n if (parent.isKind(tsm.SyntaxKind.PrefixUnaryExpression)) {\n return parent.getOperatorToken() === tsm.SyntaxKind.ExclamationToken;\n }\n\n if (parent.isKind(tsm.SyntaxKind.BinaryExpression)) {\n const operator = parent.getOperatorToken().getKind();\n\n return (\n equalityOperatorKinds.includes(operator) ||\n (parent.getLeft() === node &&\n shortCircuitOperatorKinds.includes(operator))\n );\n }\n\n if (\n parent.isKind(tsm.SyntaxKind.IfStatement) ||\n parent.isKind(tsm.SyntaxKind.WhileStatement) ||\n parent.isKind(tsm.SyntaxKind.DoStatement) ||\n parent.isKind(tsm.SyntaxKind.SwitchStatement)\n ) {\n return parent.getExpression() === node;\n }\n\n if (parent.isKind(tsm.SyntaxKind.ForStatement)) {\n return parent.getCondition() === node;\n }\n\n return (\n parent.isKind(tsm.SyntaxKind.ConditionalExpression) &&\n parent.getCondition() === node\n );\n};\n\n/**\n * `=` through `^=`, i.e. every assignment operator. TypeScript keeps them in\n * one contiguous block of {@link tsm.SyntaxKind} and marks its ends, which is\n * how the compiler itself recognizes them.\n */\nconst isAssignmentOperatorKind = (kind: tsm.SyntaxKind): boolean =>\n tsm.SyntaxKind.FirstAssignment <= kind &&\n kind <= tsm.SyntaxKind.LastAssignment;\n\nconst equalityOperatorKinds: readonly tsm.SyntaxKind[] = [\n tsm.SyntaxKind.EqualsEqualsToken,\n tsm.SyntaxKind.EqualsEqualsEqualsToken,\n tsm.SyntaxKind.ExclamationEqualsToken,\n tsm.SyntaxKind.ExclamationEqualsEqualsToken,\n] as const;\n\nconst shortCircuitOperatorKinds: readonly tsm.SyntaxKind[] = [\n tsm.SyntaxKind.QuestionQuestionToken,\n tsm.SyntaxKind.AmpersandAmpersandToken,\n tsm.SyntaxKind.BarBarToken,\n] as const;\n"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  export * from './append-as-const.mjs';
2
2
  export * from './convert-interface-to-type.mjs';
3
3
  export * from './convert-to-readonly.mjs';
4
+ export * from './enable-no-unchecked-indexed-access.mjs';
4
5
  export * from './readonly-transformer-helpers/index.mjs';
5
6
  export * from './replace-any-with-unknown.mjs';
6
7
  export * from './replace-record-with-unknown-record.mjs';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../../../src/functions/ast-transformers/index.mts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,iCAAiC,CAAC;AAChD,cAAc,2BAA2B,CAAC;AAC1C,cAAc,0CAA0C,CAAC;AACzD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0CAA0C,CAAC;AACzD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../../../src/functions/ast-transformers/index.mts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,iCAAiC,CAAC;AAChD,cAAc,2BAA2B,CAAC;AAC1C,cAAc,0CAA0C,CAAC;AACzD,cAAc,0CAA0C,CAAC;AACzD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0CAA0C,CAAC;AACzD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,aAAa,CAAC"}
@@ -1,6 +1,7 @@
1
1
  export { appendAsConstTransformer } from './append-as-const.mjs';
2
2
  export { convertInterfaceToTypeTransformer } from './convert-interface-to-type.mjs';
3
3
  export { convertToReadonlyTransformer } from './convert-to-readonly.mjs';
4
+ export { enableNoUncheckedIndexedAccessTransformer } from './enable-no-unchecked-indexed-access.mjs';
4
5
  export { compareUnionIntersectionTypes } from './readonly-transformer-helpers/compare-union-types.mjs';
5
6
  export { invalidDeepReadonlyTypeName } from './readonly-transformer-helpers/constants.mjs';
6
7
  export { groupUnionIntersectionTypes } from './readonly-transformer-helpers/group-union-types.mjs';
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;"}
1
+ {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;"}
@@ -1,6 +1,7 @@
1
1
  export { appendAsConstTransformer } from './ast-transformers/append-as-const.mjs';
2
2
  export { convertInterfaceToTypeTransformer } from './ast-transformers/convert-interface-to-type.mjs';
3
3
  export { convertToReadonlyTransformer } from './ast-transformers/convert-to-readonly.mjs';
4
+ export { enableNoUncheckedIndexedAccessTransformer } from './ast-transformers/enable-no-unchecked-indexed-access.mjs';
4
5
  export { compareUnionIntersectionTypes } from './ast-transformers/readonly-transformer-helpers/compare-union-types.mjs';
5
6
  export { invalidDeepReadonlyTypeName } from './ast-transformers/readonly-transformer-helpers/constants.mjs';
6
7
  export { groupUnionIntersectionTypes } from './ast-transformers/readonly-transformer-helpers/group-union-types.mjs';
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;"}
package/dist/index.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  export { appendAsConstTransformer } from './functions/ast-transformers/append-as-const.mjs';
2
2
  export { convertInterfaceToTypeTransformer } from './functions/ast-transformers/convert-interface-to-type.mjs';
3
3
  export { convertToReadonlyTransformer } from './functions/ast-transformers/convert-to-readonly.mjs';
4
+ export { enableNoUncheckedIndexedAccessTransformer } from './functions/ast-transformers/enable-no-unchecked-indexed-access.mjs';
4
5
  export { compareUnionIntersectionTypes } from './functions/ast-transformers/readonly-transformer-helpers/compare-union-types.mjs';
5
6
  export { invalidDeepReadonlyTypeName } from './functions/ast-transformers/readonly-transformer-helpers/constants.mjs';
6
7
  export { groupUnionIntersectionTypes } from './functions/ast-transformers/readonly-transformer-helpers/group-union-types.mjs';
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-codemod-lib",
3
- "version": "3.2.0",
3
+ "version": "3.3.0",
4
4
  "private": false,
5
5
  "keywords": [
6
6
  "typescript",
@@ -43,7 +43,7 @@
43
43
  "ts-type-forge": "^9.2.2"
44
44
  },
45
45
  "devDependencies": {
46
- "@types/node": "26.3.0",
46
+ "@types/node": "26.4.0",
47
47
  "@vitest/browser-playwright": "4.1.11",
48
48
  "@vitest/coverage-v8": "4.1.11",
49
49
  "@vitest/ui": "4.1.11",
@@ -51,7 +51,7 @@
51
51
  "dedent": "1.7.2",
52
52
  "esbuild": "0.28.2",
53
53
  "eslint": "10.8.1",
54
- "eslint-config-typed": "5.9.5",
54
+ "eslint-config-typed": "5.10.0",
55
55
  "eslint-plugin-ts-data-forge": "0.5.0",
56
56
  "eslint-plugin-ts-fortress": "1.0.4",
57
57
  "eslint-plugin-ts-type-forge": "0.7.0",