#9014·qwik

[] tsOptimizer: three codegen defects (param renamed to _rawProps, duplicate `context` binding, invalid destructuring)

Author: thelfroesCreated Sep 16, 2026Updated Sep 16, 2026

Summary

Enabling the TypeScript optimizer (qwikVite({ tsOptimizer: true })) on main at c25867def produces three distinct codegen defects in our app. Two of them occur in entirely ordinary source with no user-side workaround.

We reached for tsOptimizer as a way around #9012 (the Rust optimizer's duplicate _defaultValue consts). It does avoid that, but the build then fails on the issues below. Filing separately from #9012 since the cause is unrelated.

I see 3dfe6f9fc fix(qwik-vite): restore Rust optimizer default reverted it as the default, and 0fb2c6a80 test(ts-optimizer): require full rust parity suggests parity is a known work item — so this is offered as three concrete parity gaps rather than a regression report.

All three come from the same area: the destructuring-elimination pass emits binding names that are either non-unique or syntactically invalid.

Reproduction

  • Qwik main @ c25867def4d2a11d84f8497925771789e04ce298 (tarballs from pkg.pr.new self-report 2.0.0-beta.43)
  • Vite 8 (Rolldown), Qwik Router, pnpm
typescript
// vite.config.mts
qwikVite({ tsOptimizer: true })

then build the app. Note Rolldown reports a batch of errors and stops, so the three below are a floor, not necessarily the full set.


1. A nested $-segment's destructured callback param is treated as component props

Source — a useComputed$ with a destructured ctx param, inside a component$ that destructures its own props:

typescript
export const WalletModalWithdraw = component$<Props>(({ isOpen, initialFee, ... }) => {
  const networkFee = useComputed$(async ({ track }) => {
    const open = track(() => isOpen?.value ?? true);
    // ...
  });
});

Generated (segment s_cGRGAVNcn80):

javascript
export const s_cGRGAVNcn80 = async (_rawProps) => {
const _rawProps = _captures[0], initialFee = _captures[1], selectedSignal = _captures[2];
        const open = _rawProps.track(() => _rawProps.isOpen?.value ?? true);

The callback's { track } parameter has been renamed to _rawProps, which (a) collides with the _rawProps declared on the next line from the capture list, and (b) is semantically wrong — it now reads track off the component's props rather than off the computed context.

This one does have a user-side workaround: write async (ctx) => ctx.track(...) instead of destructuring the ctx param.


2. Two useContext results renamed to the same identifier

Source — ordinary, no Qwik-specific shape involved:

typescript
const { wallets, activeCode, setCode$ } = useContext(WalletsContextId);
const coinIcons = useContext(CoinIconMapContextId);
const { user, isAuthenticated } = useContext(SessionContextId);

Generated — two separate declarations collapse onto the same name in one scope:

javascript
const context = useContext(WalletsContextId);
// ...
const context = useContext(SessionContextId);
Identifier 'context' has already been declared

No user-side workaround.


3. Syntactically invalid destructuring emitted

Source — two rest-destructures of props in different blocks of the same component:

typescript
const { loadingBenefitWidths, className, ...rest } = props;

Generated:

javascript
const { loadingBenefitWidths, className: props.className, ...rest } = props;

className: props.className is not a valid destructuring binding — the value position of a shorthand rename must be an identifier or pattern, not a member expression.

No user-side workaround.


Environment

  • Qwik: main @ c25867def (2.0.0-beta.43)
  • @qwik.dev/router: same SHA
  • Vite: 8.x (Rolldown)
  • Node 22, pnpm 10.17, macOS arm64

Happy to provide a minimal reproduction repo for any of the three if that would help — 2 and 3 in particular look like they should reduce to a few lines.