Skip to main content
AI/MLcoalesce-labs

fix-typescript

Fix TypeScript errors with strict anti-reward-hacking rules. **ALWAYS use when** the user says 'fix type errors', 'fix typescript', 'type-check is failing', or when TypeScript compilation errors need to be resolved. Ensures runtime type safety — fixes root causes instead of silencing errors with casts.

Stars
12
Source
coalesce-labs/catalyst
Updated
2026-05-31
Slug
coalesce-labs--catalyst--fix-typescript
View on GitHubRaw SKILL.md

// install — copy + paste into any project

mkdir -p .claude/skills && curl -fsSL https://raw.githubusercontent.com/coalesce-labs/catalyst/HEAD/plugins/dev/skills/fix-typescript/SKILL.md -o .claude/skills/fix-typescript.md

Drops the SKILL.md into .claude/skills/fix-typescript.md. Works with Claude Code, Cursor, and any agent that loads SKILL.md files from .claude/skills/.

Fix TypeScript Errors

You are fixing TypeScript type errors. This command embeds strict rules to prevent "reward hacking" - patterns that make linters pass without actually fixing type safety issues.

CRITICAL: Read Before Starting

Your goal is RUNTIME TYPE SAFETY, not just passing linters.

If your fix would make the linter happy but could still crash at runtime, it's wrong.

Forbidden Patterns

These patterns are EXPLICITLY FORBIDDEN. Using them will require rework:

Pattern Why Forbidden What To Do Instead
as unknown as Type (undocumented) Erases all type info, defeats TypeScript Fix the source to return correct type
as any Disables type checking entirely Use proper typing or Zod validation
void (0 as unknown as Type) Tricks linter into thinking type is used Delete the unused type
const _var = ... (local variable) Suppresses unused warning Delete the unused variable
export type Foo (when unused elsewhere) Suppresses unused warning Remove export or delete the type
// @ts-ignore or // @ts-expect-error Hides real type problems Fix the actual type error
Commenting out code Dead code clutters codebase Delete it (git has history)
Excluding files from tsconfig Hides errors in those files Include files, fix errors

Required Approach

For Internal Code (services, models, aggregations)

Fix the SOURCE to return the correct type:

// WRONG - Casting a query result
const users = (await db.from("users").select("*")) as unknown as User[];

// CORRECT - Use the query builder's generic typing
const { data: users } = await db.from("users").select("*").returns<User[]>();
// WRONG - Casting a service result
const account = (await accountService.findById(id)) as Account;

// CORRECT - Fix the service to return the right type
const account = await accountService.findById(id); // Returns Account | null
if (account === null) throw new NotFoundError();

For External Data (API requests, webhooks, file uploads)

Validate with Zod at the boundary:

// WRONG - Trust external data
const webhook = req.body as WebhookPayload;

// CORRECT - Validate at boundary
const webhook = WebhookPayloadSchema.parse(req.body);

When Type Assertions ARE Acceptable

Type assertions are ONLY acceptable for third-party library limitations with full documentation:

// ACCEPTABLE - Third-party library type gap with documentation
// LIBRARY TYPE LIMITATION: The thirdPartyWrapper() function returns a type
// that TypeScript can't verify implements the expected interface.
// Verified at runtime that the object has the required methods.
// TODO: Remove when library updates types (tracked in TICKET-XXX)
const wrapped = thirdPartyResult as unknown as ExpectedInterface;

Requirements for acceptable assertions:

  1. The type error is from a THIRD-PARTY LIBRARY we don't control
  2. There's a detailed comment explaining WHY
  3. The comment explains what was verified at runtime
  4. There's a TODO with a tracking ticket

Process

  1. Understand the error: Read the TypeScript error message carefully
  2. Find the root cause: Why doesn't the type already match?
  3. Fix at the source: Change the source function/type, not the consumer
  4. Verify the fix: Detect the package manager and run type-check (see below)
  5. Run validation: Execute /scan-reward-hacking before marking complete

Detecting Package Manager and Type-Check Script

First, check package.json for a type-check script. Then detect the package manager by looking for lock files:

  • bun.lockb -> bun run type-check
  • pnpm-lock.yaml -> pnpm type-check
  • yarn.lock -> yarn type-check
  • package-lock.json -> npm run type-check

If no lock file is found, fall back to npx tsc --noEmit.

Before Marking Complete

You MUST run these checks:

# Type check must pass (use the detected package manager)
# e.g., npm run type-check / yarn type-check / bun run type-check / pnpm type-check

# Scan for forbidden patterns (run /scan-reward-hacking command)
grep -r "as unknown as" [files-you-changed]
grep -r "void (0" [files-you-changed]
grep -r "as any" [files-you-changed]

If ANY forbidden pattern exists in your changes without proper documentation, your work is not complete.

The Golden Rule

If you need as, ask "Why doesn't the type already match?" and fix THAT.

Type assertions are a code smell. They mean either:

  1. The source function has wrong types (fix the source)
  2. External data wasn't validated (add Zod validation)
  3. A library has incomplete types (document and track)

Never use assertions to silence errors - fix the underlying type mismatch.