Skip to content

Commit 136d313

Browse files
authored
build: enable noUncheckedIndexedAccess (#600)
* build: enable noUncheckedIndexedAccess * refactor: remove redundant indexed access fallbacks * fix: address rebased unchecked indexed access * fix: address unchecked access review feedback * refactor: simplify replay fill positional typing * refactor: avoid wait positional assertion * refactor: prefer value guards for indexed access * chore: keep fallow baseline for strictness changes
1 parent 819d7dc commit 136d313

62 files changed

Lines changed: 345 additions & 257 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/__tests__/cli-batch.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ test('batch --steps parses JSON and forwards batchSteps only', async () => {
3737
]);
3838
assert.equal(result.code, null);
3939
assert.equal(result.calls.length, 1);
40-
const req = result.calls[0];
40+
const req = result.calls[0]!;
4141
assert.equal(req.command, 'batch');
4242
assert.equal(req.session, 'sim');
4343
assert.equal(req.flags?.platform, 'ios');
@@ -57,7 +57,7 @@ test('batch --steps-file parses file payload', async () => {
5757
const result = await runCliCapture(['batch', '--steps-file', stepsPath, '--json']);
5858
assert.equal(result.code, null);
5959
assert.equal(result.calls.length, 1);
60-
const req = result.calls[0];
60+
const req = result.calls[0]!;
6161
assert.equal(req.command, 'batch');
6262
assert.equal((req.flags?.batchSteps ?? [])[0]?.command, 'wait');
6363
});
@@ -88,7 +88,7 @@ test('batch accepts legacy positionals/flags steps with deprecation warning', as
8888
assert.equal(result.code, null);
8989
assert.match(result.stderr, /positionals\/flags are deprecated.*next major version/);
9090
assert.equal(result.calls.length, 1);
91-
const req = result.calls[0];
91+
const req = result.calls[0]!;
9292
assert.equal(req.command, 'batch');
9393
assert.deepEqual((req.flags?.batchSteps ?? [])[0], {
9494
command: 'open',

src/__tests__/client-companion-tunnel-worker.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ function decodeWebSocketPayload(payload: Buffer, mask: Buffer | null): Buffer {
121121

122122
const decoded = Buffer.from(payload);
123123
for (let index = 0; index < decoded.length; index += 1) {
124-
decoded[index] ^= mask[index % 4];
124+
decoded[index]! ^= mask[index % 4]!;
125125
}
126126
return decoded;
127127
}

src/__tests__/selectors-public.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ const nodes: SnapshotNode[] = [
3737

3838
test('public selector subpath exposes platform-aware matching helpers', () => {
3939
const chain: SelectorChain = parseSelectorChain('role=button label="Continue" visible=true');
40-
const firstSelector: Selector = chain.selectors[0];
40+
const firstSelector: Selector = chain.selectors[0]!;
4141
assert.equal(firstSelector.raw, 'role=button label="Continue" visible=true');
4242
assert.equal(tryParseSelectorChain(chain.raw)?.raw, chain.raw);
4343
assert.equal(isSelectorToken('visible=true'), true);
@@ -55,8 +55,8 @@ test('public selector subpath exposes platform-aware matching helpers', () => {
5555
});
5656
assert.equal(resolved?.node.ref, 'e1');
5757

58-
assert.equal(isNodeVisible(nodes[0]), true);
59-
assert.equal(isNodeEditable(nodes[1], 'android'), true);
58+
assert.equal(isNodeVisible(nodes[0]!), true);
59+
assert.equal(isNodeEditable(nodes[1]!, 'android'), true);
6060
});
6161

6262
test('public selector diagnostics format failures', () => {

src/__tests__/upload-client.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ test('uploadArtifact disables macOS AppleDouble entries when archiving app bundl
350350
(cmd, args, options) => {
351351
if (cmd !== 'tar') return undefined;
352352
tarEnv = options.env;
353-
const archivePath = args[1];
353+
const archivePath = args[1]!;
354354
assert.equal(args[0], 'czf');
355355
assert.equal(typeof archivePath, 'string');
356356
fs.writeFileSync(archivePath, 'fake-archive');

src/cli.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ function hasExplicitMetroRuntimeOverrides(explicitFlagKeys: Set<FlagKey>): boole
481481

482482
function guessSessionFromArgv(argv: string[]): string | null {
483483
for (let i = 0; i < argv.length; i += 1) {
484-
const token = argv[i];
484+
const token = argv[i]!;
485485
if (token.startsWith('--session=')) {
486486
const inline = token.slice('--session='.length).trim();
487487
return inline.length > 0 ? inline : null;

src/commands/cli-grammar/capture.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,15 +116,16 @@ function readWaitOptionsFromPositionals(
116116
}
117117

118118
export function parseWaitPositionals(args: string[]): WaitParsed | null {
119-
if (args.length === 0) return null;
120-
const sleepMs = parseTimeout(args[0]);
119+
const firstArg = args[0];
120+
if (firstArg === undefined) return null;
121+
const sleepMs = parseTimeout(firstArg);
121122
if (sleepMs !== null) return { kind: 'sleep', durationMs: sleepMs };
122123
const timeoutMs = parseTimeout(args[args.length - 1]);
123-
if (args[0] === 'text') {
124+
if (firstArg === 'text') {
124125
const text = timeoutMs !== null ? args.slice(1, -1).join(' ') : args.slice(1).join(' ');
125126
return { kind: 'text', text: text.trim(), timeoutMs };
126127
}
127-
if (args[0].startsWith('@')) return { kind: 'ref', rawRef: args[0], timeoutMs };
128+
if (firstArg.startsWith('@')) return { kind: 'ref', rawRef: firstArg, timeoutMs };
128129
const argsWithoutTimeout = timeoutMs !== null ? args.slice(0, -1) : args.slice();
129130
const split = splitSelectorFromArgs(argsWithoutTimeout);
130131
if (split && split.rest.length === 0 && tryParseSelectorChain(split.selectorExpression)) {

src/commands/cli-grammar/interactions.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,14 +140,15 @@ function readLongPressTargetFromPositionals(positionals: string[]): LongPressOpt
140140
}
141141

142142
export function readFillTargetFromPositionals(positionals: string[]): DecodedFillTarget {
143-
if (positionals[0]?.startsWith('@')) {
143+
const firstPositional = positionals[0];
144+
if (firstPositional?.startsWith('@')) {
144145
const text =
145146
positionals.length >= 3 ? positionals.slice(2).join(' ') : positionals.slice(1).join(' ');
146147
return {
147148
kind: 'ref',
148149
target: {
149-
ref: positionals[0],
150-
label: positionals.length >= 3 ? optionalTrimmedText([positionals[1]]) : undefined,
150+
ref: firstPositional,
151+
label: positionals.length >= 3 ? optionalTrimmedText(positionals.slice(1, 2)) : undefined,
151152
},
152153
text,
153154
};

src/commands/interaction-targeting.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,9 @@ function findPreferredActionableDescendant(
8080
if (sameRectChildren.length !== 1) {
8181
break;
8282
}
83-
current = sameRectChildren[0];
83+
const child = sameRectChildren[0];
84+
if (child === undefined) break;
85+
current = child;
8486
}
8587

8688
return current === node ? null : current;

src/commands/react-native/overlay.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -177,15 +177,17 @@ function actionFromDismissNode(node: SnapshotNode): ReactNativeOverlayDismissTar
177177
function chooseCollapsedWarningNode(nodes: SnapshotNode[]): SnapshotNode | null {
178178
const withRect = nodes.filter((node) => node.rect);
179179
if (withRect.length === 0) return null;
180-
return withRect.sort((a, b) => {
181-
const aHittable = a.hittable === true ? 1 : 0;
182-
const bHittable = b.hittable === true ? 1 : 0;
183-
if (aHittable !== bHittable) return bHittable - aHittable;
184-
const aWidth = a.rect?.width ?? 0;
185-
const bWidth = b.rect?.width ?? 0;
186-
if (aWidth !== bWidth) return bWidth - aWidth;
187-
return (b.rect?.y ?? 0) - (a.rect?.y ?? 0);
188-
})[0];
180+
return (
181+
withRect.sort((a, b) => {
182+
const aHittable = a.hittable === true ? 1 : 0;
183+
const bHittable = b.hittable === true ? 1 : 0;
184+
if (aHittable !== bHittable) return bHittable - aHittable;
185+
const aWidth = a.rect?.width ?? 0;
186+
const bWidth = b.rect?.width ?? 0;
187+
if (aWidth !== bWidth) return bWidth - aWidth;
188+
return (b.rect?.y ?? 0) - (a.rect?.y ?? 0);
189+
})[0] ?? null
190+
);
189191
}
190192

191193
function collapsedBannerClosePoint(node: SnapshotNode): Point {

src/compat/maestro/replay-flow.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ function optimizeInputTextActions(
8585
const mergedActions: SessionAction[] = [];
8686
const mergedLines: number[] = [];
8787
for (let index = 0; index < actions.length; index += 1) {
88-
const action = actions[index];
88+
const action = actions[index]!;
8989
const optimized = optimizeTypedAfterTap(actions, actionLines, index);
9090
if (optimized) {
9191
mergedActions.push(...optimized.actions);
@@ -104,16 +104,17 @@ function optimizeTypedAfterTap(
104104
actionLines: number[],
105105
index: number,
106106
): { actions: SessionAction[]; actionLines: number[]; consumed: number } | null {
107-
const action = actions[index];
107+
const action = actions[index]!;
108108
const nextAction = actions[index + 1];
109109
const typedAfterTap = readPlainTypeText(nextAction);
110110
const tapSelector = readPlainMaestroTapSelector(action);
111-
if (typedAfterTap === null || tapSelector === null) return null;
111+
if (!nextAction || typedAfterTap === null || tapSelector === null) return null;
112112
const line = actionLines[index] ?? 1;
113113
if (!isLikelyTextEntrySelector(tapSelector)) {
114114
return { actions: [clearMaestroNonHittableTap(action)], actionLines: [line], consumed: 1 };
115115
}
116-
if (actions[index + 2]?.command !== MAESTRO_RUNTIME_COMMAND.pressEnter) {
116+
const pressEnterAction = actions[index + 2];
117+
if (pressEnterAction?.command !== MAESTRO_RUNTIME_COMMAND.pressEnter) {
117118
return { actions: [clearMaestroNonHittableTap(action)], actionLines: [line], consumed: 1 };
118119
}
119120
return {
@@ -129,7 +130,7 @@ function optimizeTypedAfterTap(
129130
positionals: [tapSelector, typedAfterTap],
130131
flags: action.flags,
131132
},
132-
actions[index + 2] as SessionAction,
133+
pressEnterAction,
133134
],
134135
actionLines: [line, line, actionLines[index + 2] ?? line],
135136
consumed: 3,

0 commit comments

Comments
 (0)