mcpField
T
typescript-best-practices

typescript-best-practices

github.com/mdsmithaustinDevelopment8188 installsUpdated today
Repository

About typescript-best-practices

A TypeScript skill for developers that emphasizes types which make invalid states difficult to express. It combines modeling guidance with validation, testing, and telemetry practices.

Key features

  1. 01Discriminated unions and branded types
  2. 02Modeling valid values by construction
  3. 03Validate external data at boundaries
  4. 04Prefer schemas and derived types
  5. 05Exhaustive matching and safer narrowing
  6. 06Real tests and structured telemetry

Use cases

  1. 01Modeling variants without optional-field bags
  2. 02Preventing accidental mixing of semantic IDs
  3. 03Parsing untrusted input into domain types
  4. 04Reviewing TypeScript code for unsafe casts

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/mdsmithaustin/pstack --skill typescript-best-practices

After installing, type/typescript-best-practicesin Claude Code to confirm it loaded.

Bundled filesagents/openai.yamlreferences/patterns.md

Details

From the project README

TypeScript best practices

Call the Skill tool with principle-type-system-discipline first.

RuleSummary
Discriminated unionsModel variants with a kind literal discriminant so impossible states can't be represented. No optional-field bags.
Branded typesBrand primitives with & { readonly __brand: "X" } so they can't be mixed up. Validate once at the boundary. A brand comes from a verifying type guard (input is AgentId), never from as.
Constructive modelingBuild the shape so the illegal value can't be constructed. [T, ...T[]] for non-empty, [T, T][] for even length, start plus duration for a range. Not a runtime guard, not a wish for refinement types.
Simplest total typeKeep T[] while every operation on it stays total. Strengthen to NonEmpty<T> only where the loose type forces !, a cast, or a "should never happen" throw.
unknown over anyExternal data is unknown.
Schemas before guardsBefore hand-writing a property-by-property type guard, use the repository's runtime schema library and infer the type from the schema, such as z.infer.
No as castsEvery as is a runtime crash waiting. Cast only after validation.
Narrowing hierarchyDiscriminant switch > in operator > typeof/instanceof > user-defined type guard > as.
Type guardsMust verify the claim. A lying guard is worse than as because the bug hides behind a name that says it's safe. Name them isX or hasX.
ExhaustivenessInline const _exhaustive: never = x; in default arms so the compiler errors when a new variant is added.
satisfies over asValidates the value without widening literal types.
Boundary validationParse where data crosses in, into a named domain type. Record<string, unknown> (however spelled) stops at that parse. Trust types inside. Call the Skill tool with principle-boundary-discipline.
Schema-derived typesReach for Pick/Omit/Parameters/ReturnType/Awaited/typeof before declaring a new interface.
Object argsPass objects, not positional, so argument order is self-documenting. Skip on hot paths (per-frame render, tokenizers, parsers).
Real testsDon't mock what you can run. Prefer the framework's real test primitives with leak/disposable checks, and verify UI in a running build. Mock only what you can't run locally.
Structured telemetryPrefer structured logger diagnostics with enough context to debug from an id. No console.log in shipped code.

Examples: references/patterns.md.

Comments

Markdown supported