Promptheus/commands36 commands · free · CC0Promptheus hub ↗
← All commands
/types [file | path]

Refactor

Tighten types

Kill `any`, add generics, model with unions.

typescripttypes
CategoryRefactor
Arguments[file | path]
Allowed toolsReadGrepGlobEditBash
What it does

Strengthen typing: remove `any`, add generics and discriminated unions, narrow safely — keep it building.

.claude/commands/types.md.claude/commands/ (project) · ~/.claude/commands/ (global)
Install into your repo
npx promptheus-commands add types

The prompt

Tighten the static typing in $ARGUMENTS without changing runtime behavior and without breaking the build.

If $ARGUMENTS is empty, type-check the whole project, then scope the work to files with any, non-null assertions (!), or as casts — prioritize the ones on hot paths and public exports. If it names a file, directory, or symbol, scope to that.

Method

  1. Detect conventions first: read tsconfig.json (is strict/noUncheckedIndexedAccess/exactOptionalPropertyTypes on?), the lint config, and neighboring files. Match the codebase's style (type vs interface, readonly usage, zod/valibot schemas). Do not introduce a new pattern the project doesn't already use.
  2. Establish a baseline: run the typechecker over the correct scope and record its error count and the exact command. Prefer the project's own script (npm run typecheck); for a single tsconfig use tsc --noEmit -p <tsconfig>; for a monorepo with project references (references array, or composite: true) use tsc -b, and pick the tsconfig that actually covers $ARGUMENTS — never a root config that skips the target's scope. If the baseline is already red, that is not license to add errors: leave those pre-existing errors reported but no worse, introduce zero new errors, and reduce the count wherever your edits touch failing code.
  3. Fix in this order, re-checking after each cluster:
    • Replace any with the real type. If genuinely unknown at a boundary, use unknown and narrow at the point of use — never leave any.
    • Remove unsafe casts. as SomeType and as unknown as T hide bugs; replace with a type guard, a discriminated check, or a correct annotation. Keep as const and casts to a strict subtype that the compiler can't infer but you can prove.
    • Remove non-null !. Prove non-null with a guard, early return, or invariant/assert helper that throws.
    • Model the data precisely: make illegal states unrepresentable. Use discriminated unions (shared literal kind/type tag) over optional-bag objects; T | null over sentinels; template-literal and branded types where the project already does.
    • Add generics to eliminate duplication and preserve the input→output type relationship. Prefer inference; add constraints (extends) instead of casting inside the body. Do not add type parameters that appear only once.
    • Narrow safely with user-defined type guards (x is T), in, typeof, instanceof, and exhaustive switch closed by a never default. At external boundaries (network, JSON, env) parse untrusted data through a runtime validator that returns a typed value (zod/valibot .parse); never as-assert it into a type, since a cast validates nothing at runtime.
  4. Prefer satisfies T to check a value against a type while keeping its narrow inferred type instead of annotating-and-widening; prefer Pick/Omit/Parameters/ReturnType/mapped types over restating shapes. Turn magic strings into unions.
  5. Re-run the exact baseline command until it reports no new errors and a count <= baseline (green if the baseline was green). Run the linter and any affected tests. Never suppress with @ts-ignore/@ts-expect-error/eslint-disable to reach that bar — if a suppression is truly unavoidable, use @ts-expect-error with a one-line reason and flag it.

Output

  • A short summary: files touched, and counts of any/as/! removed.
  • For each non-trivial change, a before/after diff with one line on why the new type is safer.
  • The final typechecker command and its clean result.
  • A "Left alone" list: any cast/any you kept, with the reason it's load-bearing.

Rules

  • Never change runtime logic, control flow, or emitted JS to satisfy the types. If the code is genuinely wrong, report it separately — do not paper over it with types.
  • Never loosen a type to silence an error (widening to any, adding | undefined to dodge a real null bug, making fields optional).
  • Never weaken tsconfig or disable rules.
  • Keep the project compiling after every step: introduce no new type error, and if the baseline was green, never hand back a red build.

Add it to your toolkit

Save it as .claude/commands/types.md or .cursor/commands/types.md, then invoke it with /types and your arguments.

Back to top ↑