TypeScript Strict Refactor Skill
Process
- Run
pnpm tsc --noEmitto get the current list of errors before touching anything — fix in the order TypeScript reports them, since later errors often resolve once earlier ones are fixed. - For each
any:- If it's a known shape, define an
interface/type. - If it's an external API response, define the shape via
zodand usez.infer, then validate the response with.parse()/.safeParse()at the network boundary. - If it's genuinely unknown, use
unknownand add a type guard before use — never leave it asany.
- If it's a known shape, define an
- For function parameters/returns lacking types, infer from usage (call sites, what's returned) and add explicit annotations — don't just widen to
unknownto silence the compiler. - Replace
// @ts-ignorewith either a real fix or, if truly unavoidable (e.g. a broken third-party type),// @ts-expect-errorwith a comment explaining why and a link to the upstream issue if one exists. - After each file, re-run
pnpm tsc --noEmitto confirm error count decreased and nothing new broke.
Common patterns
// Before
function handle(data: any) { return data.user.name; }
// After
import { z } from "zod";
const dataSchema = z.object({ user: z.object({ name: z.string() }) });
type Data = z.infer<typeof dataSchema>;
function handle(data: Data): string { return data.user.name; }
Checklist
-
pnpm tsc --noEmitpasses with zero errors - No remaining
any(search confirms zero hits outside ofnode_modules/generated files) - No new
@ts-ignoreintroduced - External data validated with
zod, not just type-asserted