fix-typescript · diff
v1.0.0 to v1.0.0
1 added, 1 removed. Audit A to A.
---
name: fix-typescript
description: "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."
- disable-model-invocation: true
+ disable-model-invocation: false
allowed-tools: Read, Edit, Bash, Grep
version: 1.0.0
---
# 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:
```typescript
// 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[]>();
```
```typescript
// 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:
```typescript
// 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:
```typescript
// 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:
```bash
# 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.