TypeScript Architect
Use this when designing TypeScript systems at ChainSafe — Lodestar-shaped work or any TS package/library/app. Full reference: languages/typescript/architect.md.
Key TypeScript-specific decisions
Project layout
- Monorepo: Yarn 3 workspaces. (
lernais deprecated at ChainSafe.) - Root
package.jsonwith"packageManager": "yarn@3.x"and"workspaces": ["packages/*"]. - Each package: own
package.json,src/,test/,tsconfig.json. - Shared dev tooling (ESLint, Prettier, base tsconfig) at root; extended per-package.
- TypeScript build via
tsc -b(composite projects) or bundler.
Type strictness baseline
"strict": true— non-negotiable floor."noUncheckedIndexedAccess": true— array indexing returnsT | undefined."exactOptionalPropertyTypes": truewhen the project can take it."isolatedModules": truefor bundler compatibility.
Module system
- ESM by default for new projects.
"type": "module"inpackage.json,.jsextensions in import paths. - CJS only where downstream consumers require it.
- Dual-publish (
main+module+exports) for libraries needing both.
Branded types
type UserId = string & { readonly __brand: "UserId" };
type OrderId = string & { readonly __brand: "OrderId" };
Domain identifiers must not be interchangeable. Branded types enforce at the type level with zero runtime cost.
Error model
- Throw
Errorsubclasses (not plainError) so callers caninstanceofdiscriminate. Result<T, E>types for libraries where error-as-value semantics fit; throw for binary entrypoints.AggregateErrorforPromise.allSettledmulti-error cases.- Never throw strings or non-
Errorobjects.
Async
async/awaitover raw promises for readability.Promise.allfor parallel independent work.Promise.allSettledwhen partial failure is acceptable.- No floating promises. ESLint's
no-floating-promisescatches. AbortSignalfor cancellation propagation.
Public API
- Type-level tests (
tsd,expect-type) for libraries publishing types. - Re-exports through a single
index.tsper package. @internalJSDoc tag for tooling-exported items not in public API.
ADR template for TypeScript work
- Public surface — what's exported. Type-level tests covering it.
- Async commitments — where cancellation propagates; where parallelism is exploited.
- Error contract — thrown types vs returned types.
- Build target — Node version, browser support, ESM/CJS, bundler.
- Invariants impacted — deep links into
.invariants.
Anti-patterns at design time
anyin committed code.astype assertions without runtime checks.- Unbounded
Promise.allover a large input calling a rate-limited API. - Default exports.
enumwhen a const object +as constproduces better types.tsc --noEmitnot in CI.
Related
- Full reference:
languages/typescript/architect.md - Sister roles:
chainsafe-typescript-developer,chainsafe-typescript-reviewer - Framework:
invariants/invariants-framework.md - Workflow:
chainsafe-research-plan-implement