Le pedí a Claude Code que "parseara la respuesta de la API" y recibí esto:
const data = response.data as any
const user = data.user as any
const name = (data.user as any).name as string
Tres casteos as any. Toda la información de tipos perdida. La verificación de TypeScript anulada por completo.
Por defecto, Claude Code prioriza "código que funciona" sobre "código type-safe." Cambiar eso requiere configuración explícita — del tipo que se establece una vez en CLAUDE.md en lugar de pelear en cada prompt.
Esto es lo que aprenderás en este artículo:
- Por qué se genera
as anyy cómo detenerlo - Cómo aplicar la configuración strict de
tsconfig.jsona través de CLAUDE.md - El flujo de trabajo para que Claude Code diseñe esquemas Zod
- Un bucle de retroalimentación que hace que Claude auto-corrija errores de tipo
- Una plantilla de CLAUDE.md para generación de código consistentemente type-safe
¿Por Qué Claude Code Abusa de any?
Hay tres razones principales por las que Claude Code recurre a any:
1. Intenta completar código cuando falta información de tipos
Sin tipo de respuesta de API definido, tipados de librería incompletos — cuando Claude no conoce el tipo, lo rellena con any para que el código compile. Terminar la funcionalidad toma prioridad sobre la corrección de tipos.
2. Tu prompt no menciona requisitos de tipos
"Parsea esto" y "transforma esto" no dicen nada sobre tipos. Claude se enfoca en entregar la funcionalidad solicitada. La rigurosidad de tipos queda como una idea secundaria.
3. La configuración de tipos de tu proyecto no se está comunicando
Si Claude Code no sabe sobre tu tsconfig.json, no sabe que estás ejecutando strict mode. Genera código que pasaría una verificación de tipos permisiva.
La solución tiene tres partes: comunicar la configuración del proyecto por adelantado, hacer que Claude defina tipos antes de escribir lógica, y requerir verificación de tipos como paso obligatorio.
¿Cómo Aplicas la Configuración Strict de tsconfig.json en CLAUDE.md?
Añadir lo siguiente a CLAUDE.md hace que Claude Code haga referencia a tus requisitos de seguridad de tipos antes de generar cualquier código.
Primero, la configuración de tsconfig.json:
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true
}
}
Luego las reglas de CLAUDE.md:
## TypeScript Configuration
### Code generation rules
1. `as any` is banned. No exceptions.
2. `as Type` casting is only allowed after a type guard or Zod validation
3. All function parameters and return types must have explicit type annotations
4. Use `unknown` instead of `any`, then narrow with type guards
5. Rely on type inference where possible (excessive explicit annotations are also bad)
### Required verification
After generating any code, run `npx tsc --noEmit`.
Fix all type errors before submitting. Do not return code with type errors.
Copiar tu tsconfig.json en CLAUDE.md es el cambio individual de mayor impacto que puedes hacer. Le dice a Claude exactamente bajo qué restricciones opera — sin necesidad de repetir "no uses any" en cada prompt.
¿Cómo Haces que Claude Code Diseñe Tus Esquemas Zod?
Para respuestas de APIs externas, el orden correcto es: definir el esquema Zod primero, luego generar el código de parsing. Así es cómo funciona.
Paso 1: Alimenta a Claude con un ejemplo de respuesta de API y obtén un esquema Zod
Generate a Zod schema from the following API response example.
API response example:
{
"id": 1,
"name": "John Doe",
"email": "john@example.com",
"role": "admin" | "user" | "guest",
"createdAt": "2026-01-01T00:00:00Z",
"profile": {
"bio": "Developer",
"avatarUrl": "https://example.com/avatar.jpg" | null
}
}
Requirements:
- Type every field explicitly
- Define role as a union type
- Convert createdAt to Date using z.coerce.date()
- Mark nullable fields with .nullable()
- Export inferred types using z.infer<typeof Schema>
Esquema generado:
import { z } from 'zod'
const ProfileSchema = z.object({
bio: z.string(),
avatarUrl: z.string().url().nullable(),
})
const UserRoleSchema = z.enum(['admin', 'user', 'guest'])
export const UserSchema = z.object({
id: z.number().int().positive(),
name: z.string().min(1),
email: z.string().email(),
role: UserRoleSchema,
createdAt: z.coerce.date(),
profile: ProfileSchema,
})
export type User = z.infer<typeof UserSchema>
export type UserRole = z.infer<typeof UserRoleSchema>
Paso 2: Genera la función de parsing usando el esquema
Write a function that fetches user data and parses it with UserSchema above.
Requirements:
- Receive the response as unknown
- Throw ZodError on parse failure
- No `as any` casts
import { UserSchema, type User } from './schemas/user'
export async function fetchUser(userId: number): Promise<User> {
const response = await fetch(`/api/users/${userId}`)
if (!response.ok) {
throw new Error(`Failed to fetch user: ${response.status}`)
}
const json: unknown = await response.json()
return UserSchema.parse(json)
}
Recibir response.json() como unknown, validar con Zod y devolver un valor tipado — este es el patrón correcto para manejar datos externos sin as any.
¿Cómo Haces de tsc --noEmit un Requisito Obligatorio en CLAUDE.md?
Añade esto a CLAUDE.md para evitar que Claude devuelva código con errores de tipo persistentes:
## Mandatory Check Before Submitting Code
After generating or modifying any code, always run `npx tsc --noEmit`.
Fix all type errors until the output shows zero errors.
Never return code that still has type errors.
La restricción de "cero errores antes de enviar" es lo que hace que esto funcione. Sin ella, Claude a veces devuelve código con errores de tipo argumentando que "funciona correctamente." El flag --noEmit ejecuta la verificación de tipos sin emitir archivos JavaScript — perfecto para este tipo de control.
Añádelo también a package.json:
{
"scripts": {
"type-check": "tsc --noEmit",
"type-check:watch": "tsc --noEmit --watch"
}
}
¿Cómo Ejecutas un Bucle que Hace que Claude Auto-Corrija Errores de Tipo?
Ejecuta tsc --noEmit, captura la salida y aliméntala directamente a Claude para corrección automática.
npx tsc --noEmit 2>&1
Ejemplo de salida:
src/utils/parseResponse.ts:12:5 - error TS2322: Type 'any' is not assignable to type 'User'.
src/utils/parseResponse.ts:18:3 - error TS7006: Parameter 'data' implicitly has an 'any' type.
Found 2 errors.
Pásalo a Claude:
Fix the following TypeScript errors.
Errors:
src/utils/parseResponse.ts:12:5 - error TS2322: Type 'any' is not assignable to type 'User'.
src/utils/parseResponse.ts:18:3 - error TS7006: Parameter 'data' implicitly has an 'any' type.
Fix approach:
- Do not use `as any`
- Use unknown type + type guards, or Zod validation
- Confirm `tsc --noEmit` shows zero errors before responding
Ejecuta este bucle hasta que el verificador de tipos esté limpio. Cuando hay múltiples errores, pásalos todos a la vez — Claude los abordará juntos.
¿Cómo Haces que Claude Code Tipe Respuestas de APIs Externas?
Cuando tienes una especificación Swagger u OpenAPI, pasarla a Claude para conversión a esquemas Zod es el enfoque más rápido.
Generate TypeScript + Zod schemas from the following OpenAPI spec.
Spec (excerpt):
---
components:
schemas:
Product:
type: object
required:
- id
- name
- price
properties:
id:
type: integer
name:
type: string
maxLength: 100
price:
type: number
minimum: 0
category:
type: string
enum: [electronics, clothing, food]
stock:
type: integer
nullable: true
---
Requirements:
- Export types using z.infer<typeof Schema>
- Reflect validation constraints (maxLength, minimum) in Zod
- Express nullable with .nullable()
Herramientas como openapi-zod-client también existen para conversión automatizada, pero dejar que Claude lo haga te da más control sobre casos límite y convenciones de nomenclatura.
¿Cómo Maximizas la Inferencia de Tipos al Usar Prisma?
Prisma auto-genera tipos TypeScript desde tu esquema de base de datos. Dile a Claude Code que use esos tipos en lugar de inventar los suyos.
## Prisma Type Rules (add to CLAUDE.md)
- Actively use Prisma-generated types (PrismaClient, Prisma.UserGetPayload, etc.)
- Import the User type from '@prisma/client', not custom definitions
- Use Prisma.UserGetPayload<{include: {posts: true}}> for query return types
- Do not override Prisma types with custom type definitions
Cómo se ve en la práctica:
import type { Prisma } from '@prisma/client'
import { prisma } from '@/lib/prisma'
type UserWithPosts = Prisma.UserGetPayload<{
include: { posts: { include: { tags: true } } }
}>
export async function getUserWithPosts(userId: string): Promise<UserWithPosts | null> {
return prisma.user.findUnique({
where: { id: userId },
include: {
posts: {
include: { tags: true },
},
},
})
}
Cuando usas los tipos generados por Prisma, los cambios en el esquema de la base de datos aparecen instantáneamente como errores de TypeScript. Claude Code solo necesita "usa los tipos de Prisma" en el prompt o CLAUDE.md para aprovechar esto.
¿Cómo Se Ve una Plantilla de CLAUDE.md para Generación de Código Type-Safe?
Aquí tienes la plantilla completa de CLAUDE.md basada en todo lo que cubre este artículo. Cópiala en tu proyecto tal cual.
## TypeScript Type Safety Policy
### Absolute prohibitions
- Using `as any` (banned regardless of circumstances)
- Using `@ts-ignore` (`@ts-expect-error` is allowed with a comment explaining why)
- Function definitions without type annotations (parameters and return types are required)
### Typing external data
- Receive external API/JSON data as `unknown` and validate with Zod
- `response.json() as SomeType` casting is banned
- Export types from Zod schemas using `z.infer<typeof Schema>`
### tsconfig.json compliance
strict: true / noImplicitAny: true / strictNullChecks: true / noUncheckedIndexedAccess: true
### Required check (mandatory)
After generating or modifying code, run `npx tsc --noEmit` and confirm zero errors before responding.
### Zod conventions
- Centralize schema definitions in `src/schemas/`
- `.parse()` throws on validation failure (intentional behavior)
- Use `.safeParse()` when you need to handle errors gracefully
- Always export the inferred type alongside the schema
Conclusión
La clave para obtener TypeScript type-safe de Claude Code es hacer que la seguridad de tipos sea explícita — no una expectativa implícita.
- Copia la configuración strict de tsconfig.json en CLAUDE.md — política de tipos a nivel de proyecto que se mantiene
- Requiere
tsc --noEmitcomo verificación obligatoria — evita que los errores de tipo se cuelen - Empieza con esquemas Zod para datos externos — la única forma de evitar
as anyen respuestas de API - Alimenta los logs de errores de tipo directamente a Claude — el bucle de retroalimentación maneja la auto-corrección
- Dile a Claude que use los tipos generados por Prisma — el esquema de BD y los tipos TypeScript se mantienen sincronizados automáticamente
Deja de decir "no uses any" en cada prompt. Escribe la política de tipos una vez en CLAUDE.md y deja que haga el trabajo.