Skip to content

fix(portal): make type-check actually type-check - #130

Merged
jim-counter merged 1 commit into
mainfrom
fix/portal-type-check-script
Aug 11, 2026
Merged

fix(portal): make type-check actually type-check#130
jim-counter merged 1 commit into
mainfrom
fix/portal-type-check-script

Conversation

@jim-counter

Copy link
Copy Markdown
Member

The problem

apps/portal/tsconfig.json is a solution-style config: it has "files": [] and delegates to project references.

{
  "files": [],
  "references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }]
}

Invoked without -b, tsc honours files: [] literally: it checks zero files and exits 0. So both of these guarded nothing at all:

"type-check": "tsc --noEmit",
"build": "tsc && vite build",

tsc --noEmit printed no output and exited 0 because there was nothing in its input set. Build mode (-b) is what walks the references array; plain mode ignores it.

Nothing else covered the gap either. Vite compiles with esbuild, which strips types without checking them, and our ESLint config does not enable no-undef for TS files. So an undefined global could pass lint, type-check, and build. Demonstrated on this branch by putting an undefined call into a component:

check result
yarn lint exit 0 (no-undef off for TS)
vite build exit 0 (esbuild does no type checking)
type-check (before) exit 0 (checked zero files)
type-check (after) exit 1TS2304: Cannot find name ...

CI has been running the vacuous command all along, showing a green "Type check" step that verified nothing.

The fix

Both scripts move to build mode:

"type-check": "tsc -b",
"build": "tsc -b && vite build",

Plain tsc -b rather than tsc -b --noEmit: both referenced configs already set noEmit: true, so the only thing written is .tsbuildinfo into apps/portal/node_modules/.tmp/ (gitignored). Keeping it preserves incremental caching that --noEmit would discard.

TypeScript pin 5.7.3 -> 5.8.3

The portal was the only workspace pinning an exact typescript version; every other one floats to 5.8.3. Its own tsconfig.node.json already sets erasableSyntaxOnly, a TS 5.8 option, so under build mode the workspace-local 5.7.3 failed with TS5023: Unknown compiler option 'erasableSyntaxOnly'. Build mode reads both referenced configs, where plain mode never touched tsconfig.node.json.

That option has therefore never once been read by the portal's own compiler, which is the same root cause wearing a different hat.

Two pre-existing errors, newly exposed

These are not introduced by this PR. They already existed on main and were invisible because the check was vacuous. The PR cannot be green without them:

  • components/transaction/TransactionForm.tsx:1TS1484. ReactNode is type-only and verbatimModuleSyntax is on: import React, { type ReactNode } from 'react';
  • stores/theme-store.ts:87TS2578, unused @ts-expect-error. MediaQueryList.addListener is declared in lib.dom.d.ts (marked @deprecated, not missing), so the directive suppressed nothing and became an error itself. Only the directive line is deleted; the Safari runtime fallback and surrounding try/catch are untouched, and since the directive is compile-time only, the built output is unchanged.

Heads-up: open PRs with type errors will now fail CI

This is the intended effect, not a regression. Any open PR carrying type errors was passing CI on a check that inspected nothing; those errors now surface where they should.

Confirmed against #129's head (6fb99ae), which has three genuine errors. With the fixed script they all report:

components/operators/OperatorCard.tsx(99,23): error TS2322: Property 'currentStakedValue' does not exist on type 'IntrinsicAttributes & OperatorPoolBreakdownProps'.
components/operators/OperatorCard.tsx(100,51): error TS2339: Property 'totalStorageFeeDeposit' does not exist on type 'Operator'.
components/staking/OperatorSummary.tsx(118,30): error TS2304: Cannot find name 'calculateTotalPositionValue'.

Left untouched here; they belong to that PR. Expect it to go red after this merges and to need those three fixed (plus a rebase to pick up the two above).

Note on CI scope

Worth recording, since it came up: the portal is now gated at two CI steps, since the Build step runs every workspace's build and the portal's now begins with tsc -b.

I considered switching the Type check step from yarn workspace @auto-portal/portal type-check to root yarn type-check, and recommend against it. shared-lib, shared-state, and shared-ui all use "build": "tsc -b", so CI's Build step already type-checks them (verified: injecting a type error into shared-lib fails its build with TS2322, exit 1). Widening the step would add little real coverage while fanning out to services/staking-indexer, whose src/types is generated. No workflow change in this PR.

Separately, and out of scope: CI never runs yarn test. The steps are Lint, Prettier format check, Type check, Build. The portal's 35 tests pass but nothing enforces that; resolving it first needs the @polkadot/types conflict in the indexer/worker workspaces sorted out. Probably worth its own issue.

Verification

  • yarn workspace @auto-portal/portal type-check — exit 0, with both .tsbuildinfo files written from a cleaned .tmp/, so it is doing real work rather than exiting early
  • yarn lint — exit 0
  • yarn build — exit 0
  • yarn workspace @auto-portal/portal test:run — 35/35 pass
  • yarn install --immutable — exit 0, lockfile is CI-clean
  • Deliberate-error test: injected an undefined call, confirmed type-check fails with TS2304 while lint and vite build both pass, then reverted

Two root suite commands fail identically on unmodified main (confirmed by stashing this work), so they are pre-existing and unrelated:

  • yarn format:check — 15 gitignored SubQuery codegen files under services/staking-indexer/src/types/. None of the files in this diff are flagged.
  • yarn test@polkadot/types version conflict in the indexer/worker workspaces. CI does not run this.

🤖 Generated with Claude Code

apps/portal/tsconfig.json is a solution-style config with "files": [] and
project references. Invoked without -b, tsc honours files: [], checks zero
files, and exits 0. So both "type-check": "tsc --noEmit" and the tsc in
"build": "tsc && vite build" guarded nothing, and a green CI proved nothing.

Vite compiles with esbuild, which does no type checking, and ESLint does not
enable no-undef for TS files, so an undefined global could pass lint,
type-check and build. Verified: an undefined call in a component passes both
yarn lint and vite build with exit 0.

Switch both scripts to build mode (tsc -b), which walks the referenced
projects. Both referenced configs already set noEmit: true, so nothing is
emitted but .tsbuildinfo under apps/portal/node_modules/.tmp/ (gitignored),
which keeps incremental caching that --noEmit would discard.

Bump the portal's typescript pin 5.7.3 -> 5.8.3 to match every other
workspace. tsconfig.node.json already sets erasableSyntaxOnly, a TS 5.8
option, so build mode failed on 5.7.3 with TS5023. That option had never
been read by the portal's own compiler, for the same reason.

Fix the two pre-existing errors the working check now exposes:
- TransactionForm.tsx: ReactNode is type-only under verbatimModuleSyntax
  (TS1484)
- theme-store.ts: unused @ts-expect-error (TS2578). MediaQueryList.addListener
  is present in lib.dom.d.ts, deprecated rather than missing, so the directive
  suppressed nothing. The Safari runtime fallback and try/catch are unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
auto-portal-web Ready Ready Preview Aug 11, 2026 11:57am

Request Review

@jim-counter
jim-counter merged commit 7e5fec2 into main Aug 11, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant