None of the five tsconfigs set noUncheckedIndexedAccess. It is the flag that would have surfaced the requireQuota fail-open without anyone going looking for it.
Why
strict: true is on in web, workers, shared, and db -- good baseline. But indexed access still lies:
const limit = quotas[quotaKey]; // typed number, actually undefined
That is the mechanism behind the quota guard defect. The flag makes the type number | undefined and forces the check.
Approach
Per package, smallest surface first:
packages/db
packages/shared
packages/workers
packages/web (will be noisy)
exactOptionalPropertyTypes is the natural follow-up -- it is what makes "? means absent, | null means nullable" enforceable rather than a convention -- but the fallout is much larger. Treat it as a separate decision after this one lands, not part of this issue.
Done when
noUncheckedIndexedAccess: true in all four tsconfigs
pnpm typecheck clean
Effort: roughly half a day per package, concentrated in web.
Prerequisite: #779
Do #779 first. There is no shared tsconfig base today -- four packages restate their options independently and have already drifted -- so turning this flag on per package means four edits that will drift again. One base config all four extend is the same work with a durable result.
The trap: do not pay the tax in !
This flag's cost is non-null assertions. Every indexed access becomes T | undefined, and the shortest way to silence each one is arr[i]! -- which re-suppresses exactly what the flag just surfaced. Turning on the flag and then !-ing the fallout is net-negative: the same unchecked claim, now with more ceremony.
cf-sync-engine has had this flag on since its first commit and carries 27 ! assertions in 6,549 lines as the honest cost -- payload[0]!, values[i]!, migrations[i - 1]!.to. Most sit in loops where the bound is provable a line or two away. That is the acceptable rate and the acceptable shape.
Set the rule before starting: fix with a check or a narrow; reach for ! only when the bound is provable within a few lines, and never across a function boundary. If a package's fallout cannot be fixed that way, that is a finding worth writing down, not a reason to bulk-assert.
Part of #778 (TypeScript correctness target state and tracker).
None of the five tsconfigs set
noUncheckedIndexedAccess. It is the flag that would have surfaced therequireQuotafail-open without anyone going looking for it.Why
strict: trueis on inweb,workers,shared, anddb-- good baseline. But indexed access still lies:That is the mechanism behind the quota guard defect. The flag makes the type
number | undefinedand forces the check.Approach
Per package, smallest surface first:
packages/dbpackages/sharedpackages/workerspackages/web(will be noisy)exactOptionalPropertyTypesis the natural follow-up -- it is what makes "?means absent,| nullmeans nullable" enforceable rather than a convention -- but the fallout is much larger. Treat it as a separate decision after this one lands, not part of this issue.Done when
noUncheckedIndexedAccess: truein all four tsconfigspnpm typecheckcleanEffort: roughly half a day per package, concentrated in
web.Prerequisite: #779
Do #779 first. There is no shared tsconfig base today -- four packages restate their options independently and have already drifted -- so turning this flag on per package means four edits that will drift again. One base config all four extend is the same work with a durable result.
The trap: do not pay the tax in
!This flag's cost is non-null assertions. Every indexed access becomes
T | undefined, and the shortest way to silence each one isarr[i]!-- which re-suppresses exactly what the flag just surfaced. Turning on the flag and then!-ing the fallout is net-negative: the same unchecked claim, now with more ceremony.cf-sync-enginehas had this flag on since its first commit and carries 27!assertions in 6,549 lines as the honest cost --payload[0]!,values[i]!,migrations[i - 1]!.to. Most sit in loops where the bound is provable a line or two away. That is the acceptable rate and the acceptable shape.Set the rule before starting: fix with a check or a narrow; reach for
!only when the bound is provable within a few lines, and never across a function boundary. If a package's fallout cannot be fixed that way, that is a finding worth writing down, not a reason to bulk-assert.Part of #778 (TypeScript correctness target state and tracker).