Repository navigation
Decide on the strict tsconfig flags wxt 0.21 introduced (currently opted out) #688
Description
Background
wxt 0.21's generated .wxt/tsconfig.json turns on four compiler options that 0.20.27 did not:
verbatimModuleSyntaxnoUncheckedIndexedAccessnoImplicitOverridenoFallthroughCasesInSwitch
Because packages/extension/tsconfig.json extends the generated config and maps pilo-core/core and pilo-core/ariaTree to core's sources via paths, the extension typecheck applies these to packages/core/src/** as well as the extension's own code. The wxt 0.21 bump (#687) therefore surfaced 64 errors in files it didn't touch.
In #687 I kept verbatimModuleSyntax on — it's the check that catches the value-import-of-a-type pattern that now hard-fails the rolldown/vite 8 release build ([MISSING_EXPORT] "Logger" is not exported by "../core/src/core.ts") — and fixed the 19 imports it flagged. The other three flags are explicitly disabled in packages/extension/tsconfig.json as a holding position, since adopting them meant 45 more changes inside a dependency bump.
What this issue is (and isn't)
I went through all 45. These are not latent bugs. Almost every one is a provably-safe access that the flag can't narrow:
packages/core/src/webAgent.ts:979—this.messages[i]insidefor (let i = this.messages.length - 1; i >= 0; i--)packages/core/src/webAgent.ts:1369—aiResponse.toolResults[0]guarded byif (aiResponse.toolResults.length > 1)packages/core/src/browser/ariaTree/roleUtils.ts:480-489—tokens[index]insidewhile (index < tokens.length)packages/extension/src/background/ExtensionBrowser.ts:990-993—return tabs[0]guarded byif (!tabs.length || !tabs[0].id) throw- 21 of the 45 are
container.querySelector(...)/mock.calls[n]destructuring in test files
The two with any teeth are still low-impact:
ExtensionBrowser.ts×5 —const [{ result }] = await browser.scripting.executeScript(...)throwsTypeError: Cannot destructure property 'result' of 'undefined'if the array comes back empty (target frame gone). All five sites are already insidetry/catch, so the observable difference is a confusingTypeErrorin the log instead of a clean error.ExtensionBrowser.ts:246—dataUrl.split(",")[1]passed toBuffer.from; only reachable ifcaptureVisibleTabreturns something that isn't a data URL.
packages/core/src/events.ts:547 (noImplicitOverride, TS4114) is the one genuinely correct finding: emit does override EventEmitter.emit and should say so. It's a one-word fix.
So this is a code-hygiene / config-coherence issue, not a correctness one. Filing it so the opt-out doesn't silently become permanent by accident.
Options
- Adopt the flags monorepo-wide — turn them on in core, cli, server and extension in one deliberate PR and fix the 45 sites. Coherent, and the flags then actually hold. Cost: a wide mechanical diff, mostly
!and?.in tests. - Adopt them for the extension only — smaller, but core would then be checked at two different strictness levels depending on which package compiles it, and core's own
tsconfig.jsonwouldn't enforce it, so it would rot straight back. - Keep the opt-out permanently and replace the comment in
packages/extension/tsconfig.jsonwith a link to this issue explaining the decision.
My read: (1) if the team wants the strictness, (3) if it doesn't. (2) is the worst of both. There's no urgency either way — nothing here is broken today.
packages/core/src/events.ts:547 is worth doing regardless of which option wins.
Full list
45 errors attributable to the three disabled flags
../core/src/browser/ariaTree/cssTokenizer.ts(101,5) TS2322: Type 'number | undefined' is not assignable to type 'number'.
../core/src/browser/ariaTree/roleUtils.ts(480,26) TS2532: Object is possibly 'undefined'.
../core/src/browser/ariaTree/roleUtils.ts(485,9) TS2532: Object is possibly 'undefined'.
../core/src/browser/ariaTree/roleUtils.ts(489,26) TS2532: Object is possibly 'undefined'.
../core/src/browser/ariaTree/roleUtils.ts(677,34) TS2345: Argument of type 'HTMLOptionElement | undefined' is not assignable to parameter of type 'Element'.
../core/src/events.ts(547,3) TS4114: This member must have an 'override' modifier because it overrides a member in the base class 'EventEmitter<string | symbol, any>'.
../core/src/snapshotCompressor.ts(167,7) TS2322: Type 'string | undefined' is not assignable to type 'string'.
../core/src/webAgent.ts(1369,24) TS2532: Object is possibly 'undefined'.
../core/src/webAgent.ts(1384,26) TS18048: 'toolResult' is possibly 'undefined'.
../core/src/webAgent.ts(1936,24) TS18048: 'firstToolResult' is possibly 'undefined'.
../core/src/webAgent.ts(979,11) TS18048: 'msg' is possibly 'undefined'.
../core/src/webAgent.ts(979,48) TS18048: 'msg' is possibly 'undefined'.
../core/src/webAgent.ts(980,9) TS2322: Type '{ content: any[]; } | { content: any[]; role: "system"; providerOptions?: SharedV4ProviderOptions | undefined; } | { content: any[]; role: "user"; providerOptions?: SharedV4ProviderOptions | undefined; } | { ...; } | { ...; }' is not assignable to type 'ModelMessage'.
../core/src/webAgent.ts(982,21) TS18048: 'msg' is possibly 'undefined'.
e2e/fixtures/extension.ts(72,15) TS2345: Argument of type 'string | undefined' is not assignable to parameter of type 'string'.
src/background/ExtensionBrowser.ts(127,16) TS2339: Property 'result' does not exist on type 'InjectionResult | undefined'.
src/background/ExtensionBrowser.ts(169,16) TS2339: Property 'result' does not exist on type 'InjectionResult | undefined'.
src/background/ExtensionBrowser.ts(246,26) TS2769: No overload matches this call.
src/background/ExtensionBrowser.ts(589,16) TS2339: Property 'result' does not exist on type 'InjectionResult | undefined'.
src/background/ExtensionBrowser.ts(690,16) TS2339: Property 'result' does not exist on type 'InjectionResult | undefined'.
src/background/ExtensionBrowser.ts(951,18) TS2339: Property 'result' does not exist on type 'InjectionResult | undefined'.
src/background/ExtensionBrowser.ts(990,26) TS2532: Object is possibly 'undefined'.
src/background/ExtensionBrowser.ts(993,5) TS2322: Type 'Tab | undefined' is not assignable to type 'Tab'.
src/ui/components/sidepanel/SidePanel.tsx(93,23) TS2345: Argument of type 'Tab | undefined' is not assignable to parameter of type 'SetStateAction<Tab | null>'.
test/background/configSeed.test.ts(136,20) TS2532: Object is possibly 'undefined'.
test/background/configSeed.test.ts(240,29) TS2532: Object is possibly 'undefined'.
test/background/configSeed.test.ts(260,20) TS2532: Object is possibly 'undefined'.
test/background/configSeed.test.ts(273,20) TS2532: Object is possibly 'undefined'.
test/background/indicatorControl.test.ts(147,45) TS18048: 'lastCall' is possibly 'undefined'.
test/background/indicatorControl.test.ts(79,23) TS18048: 'call' is possibly 'undefined'.
test/components/sidepanel/ChatView.test.tsx(1013,33) TS2532: Object is possibly 'undefined'.
test/components/sidepanel/ChatView.test.tsx(1052,22) TS2532: Object is possibly 'undefined'.
test/components/sidepanel/ChatView.test.tsx(1053,23) TS2532: Object is possibly 'undefined'.
test/components/sidepanel/ChatView.test.tsx(13,10) TS2532: Object is possibly 'undefined'.
test/components/sidepanel/ChatView.test.tsx(462,9) TS18048: 'orphanEl' is possibly 'undefined'.
test/components/sidepanel/ChatView.test.tsx(700,35) TS2532: Object is possibly 'undefined'.
test/components/sidepanel/ChatView.test.tsx(729,35) TS2532: Object is possibly 'undefined'.
test/components/sidepanel/ChatView.test.tsx(756,35) TS2532: Object is possibly 'undefined'.
test/components/sidepanel/ChatView.test.tsx(786,35) TS2532: Object is possibly 'undefined'.
test/components/sidepanel/ChatView.test.tsx(815,35) TS2532: Object is possibly 'undefined'.
test/components/sidepanel/ChatView.test.tsx(844,35) TS2532: Object is possibly 'undefined'.
test/components/sidepanel/ChatView.test.tsx(913,33) TS2532: Object is possibly 'undefined'.
test/components/sidepanel/ChatView.test.tsx(937,33) TS2532: Object is possibly 'undefined'.
test/components/sidepanel/ChatView.test.tsx(967,33) TS2532: Object is possibly 'undefined'.
test/components/sidepanel/ChatView.test.tsx(990,33) TS2532: Object is possibly 'undefined'.
Reproduce by removing the three false overrides from packages/extension/tsconfig.json and running pnpm --filter pilo-extension run typecheck.
Metadata
Metadata
Assignees
Labels
Type
Fields
Priority
Activity
lmorchard commentedon Sep 15, 2026
I lean toward option 3, since option 1 sounds like some ocean boiling for unclear benefits.
lmorchard commentedon Sep 15, 2026
Measured numbers (supersedes the "~45 findings" above)
The count in the issue body came from the extension typecheck only, which sees extension code plus whatever of
core/srcis reachable through thepilo-core/corebarrel. That undercounts some areas and double-counts others. Here are real per-flag numbers, deduplicated across packages — cli, server and extension all typecheck core viapaths, so raw per-package counts overlap heavily.noFallthroughCasesInSwitchnoImplicitOverridecore/src)noUncheckedIndexedAccesscore/testnoUncheckedIndexedAccessby area:extension/testcore/srccli/testextension/srcextension/e2ePR #691 takes the first two flags — 0 sites and 5 one-word annotations. That reduces this issue to a single question about
noUncheckedIndexedAccess.On that remaining question
31 of the 55 sites are in test files, where the flag buys close to nothing — a test indexing
mock.calls[0]fails either way. That leaves 24 production sites, which I've now read in full across two audit passes (the 45 from the original extension run, plus the two only reachable from a core-only typecheck:bidiBrowser.ts:120, guarded byif (treeResult?.contexts?.length > 0), andplaywrightBrowser.ts:543, where the index is% total).Zero are real bugs. Every one is a provably-safe access the flag can't narrow. The closest to a defect is still the five
ExtensionBrowserexecuteScriptdestructures, which would throw a confusingTypeErrorrather than a clean error — and all five are already insidetry/catch.So the honest case for this flag is forward-looking — it catches future unguarded indexing — not remedial. That's a legitimate reason to adopt it, but it's a different argument than "we have latent bugs," and worth deciding on its own terms.
Related finding
packages/core/tsconfig.jsonexcludes**/*.test.ts, so core's 993 tests are not typechecked by any config or CI job. They're clean at baseline (verified), but that's where 145 of the 200 total sites live. Filed separately rather than tangled into this decision.