TypeScript Strict Enforcement
Enforced maximum TypeScript strict mode across all 12 packages in the casehub-pages monorepo. The work went deeper than tsconfig flags — it restructured the component type system to eliminate type erasure at package boundaries.
The Problem
The monorepo had two tiers of TypeScript strictness. Five core packages used strict: true
with extra flags. Five legacy packages inherited a weaker base with only three strict
flags. No monorepo-wide type check existed, and webpack’s transpileOnly: true made
type errors in iframe components invisible to CI. All test files were excluded from
tsc --noEmit, meaning the 119 as any casts lived in files no type checker ever saw.
More fundamentally, Component.props: Record<string, unknown> erased all type information
at the component boundary. Builders cast typed props DOWN to Record<string, unknown>,
tests cast back UP via as any. Both directions were symptoms of the same root cause.
The Architecture Fix
The component model layer was incomplete. pages-component owned the Component interface
and layout props but data component props (BarChartProps, TableProps) and form input
props lived in pages-ui (the parser). This forced pages-viz (the renderer) to depend
on pages-ui — the renderer depended on the parser just to access type definitions.
We moved all props types to pages-component, completing it as the component model layer.
Component became generic: Component<T extends string, P> with a ComponentTypeRegistry
mapping type strings to their props interfaces. Type guards narrow both the type field
(to a string literal) and props (to the mapped type) simultaneously.
The existing ComponentTypeRegistry and getProps() infrastructure was already half-built —
a base registry in pages-component, an extended one in pages-ui with a cast-widened re-export.
Unifying eliminated the cast and the split.
Result: pages-viz no longer depends on pages-ui. Both independently depend on
pages-component (the component model) and pages-data (the data model). Clean
Layer 0 → Layer 1 architecture.
What Changed
- Generic Component<T, P> with unified
ComponentTypeRegistry(37 component types) - All DSL builders return
TypedComponent<T>— 22 productionas unknown ascasts eliminated - All 121 test
as anycasts eliminated via branded constructors, type guards,getProps()narrowing,HTMLElementTagNameMapdeclarations - Single authoritative base tsconfig —
strict: true,exactOptionalPropertyTypes,verbatimModuleSyntax,noUncheckedIndexedAccess, plus 6 more flags - tsconfig split:
tsconfig.json(includes tests) +tsconfig.build.json(excludes tests) - Root
yarn typecheckruns across all packages, added to CI - Legacy TS 4.6.2 → 5.6.0 for 5 packages
- Gallery fixes: kitchen sink cleaned up, dead URLs replaced, mock data corrected