Fundamentos de TypeScript para React
Receta
Configura un proyecto de React impulsado por TypeScript con un tsconfig.json bien configurado, comprende cómo funcionan los archivos .tsx y aprende las anotaciones de tipo central que todo desarrollador de React necesita.
Ejemplo de Trabajo
// tsconfig.json (Next.js 15 / React 19 recomendado)
{
"compilerOptions": {
"target": "ES2022",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [{ "name": "next" }],
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}// src/components/Greeting.tsx
type GreetingProps = {
name: string;
age?: number;
};
export function Greeting({ name, age }: GreetingProps) {
return (
<div>
<h1>Hola, {name}</h1>
{age !== undefined && <p>Edad: {age}</p>}
</div>
);
}// Uso
<Greeting name="Alice" />
<Greeting name="Bob" age={30} />Análisis Profundo
Cómo Funciona
- TypeScript agrega chequeo de tipos estático sobre JavaScript. El compilador detecta errores de tipo antes de que tu código se ejecute en el navegador.
- Los archivos
.tsxson archivos TypeScript que soportan sintaxis JSX. La configuraciónjsx: "preserve"le indica a TypeScript que deje JSX sin modificar para que el bundler (Next.js, Vite) lo maneje. strict: truehabilita una familia de chequeos estrictos (strictNullChecks,strictFunctionTypes,noImplicitAny, etc.) que detectan la mayoría de bugs.moduleResolution: "bundler"coincide con cómo los bundlers modernos resuelven importaciones, soportando camposexportsen package.json e importaciones sin extensión.- Los alias de ruta como
@/*te permiten escribirimport { Button } from "@/components/Button"en lugar de rutas relativas frágiles.
Variaciones
Configuración de Vite + React:
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2023", "DOM", "DOM.Iterable"],
"module": "ESNext",
"moduleResolution": "bundler",
"jsx": "react-jsx",
"strict": true,
"noEmit": true,
"isolatedModules": true,
"skipLibCheck": true
},
"include": ["src"]
}Agregar opciones más estrictas incrementalmente:
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"noFallthroughCasesInSwitch": true
}
}Notas de TypeScript
- Siempre instala tipos de React:
npm install -D @types/react @types/react-dom. Con React 19, los tipos se distribuyen conreacty@types/reactpodría no ser necesario dependiendo de tu configuración. - Usa
typepara props y formas simples; usainterfacecuando necesites declaration merging oextends. - Prefiere
unknownsobreany. Si debes usarany, añade un comentario// eslint-disabley un TODO para solucionarlo después.
Trampas
- Olvidar
"strict": truesignifica que TypeScript no marcará acceso anulloundefined, derrotando gran parte de su valor. - Configurar
jsxa"react"en lugar de"preserve"o"react-jsx"te fuerza a hacerimport React from "react"en cada archivo. skipLibCheck: truese recomienda para velocidad de compilación pero puede ocultar errores de tipo en tus propios archivos.d.ts.- Usar
anydesactiva silenciosamente el chequeo de tipos para todo lo que ese valor toca posteriormente.
Alternativas
| Enfoque | Ventajas | Desventajas |
|---|---|---|
strict: true desde el inicio | Detecta la mayoría de bugs temprano | Curva de aprendizaje más pronunciada para principiantes |
Adopción gradual (strict: false) | Migración más fácil desde JS | Se pierde detección de bugs críticos de null/undefined |
Tipos JSDoc (sin archivos .ts) | Cero cambios en pasos de compilación | Verboso, expresividad de tipos limitada |
interface para todos los props | Declaration merging, estilo OOP familiar | No puede expresar uniones o tipos mapeados |
type para todos los props | Uniones, intersecciones, tipos mapeados | Sin declaration merging |
Preguntas Frecuentes
¿Por qué se recomienda strict: true desde el inicio en un proyecto de React?
- Habilita
strictNullChecks,noImplicitAny,strictFunctionTypesy otros chequeos como grupo. - Detecta bugs de acceso a null/undefined que de otra manera solo aparecerían en runtime.
- Agregar modo estricto posteriormente a una base de código grande requiere reparar cientos de errores de una sola vez.
¿Qué hace jsx: "preserve" y cuándo deberías usar una configuración diferente?
"preserve"deja JSX sin modificar para que el bundler (Next.js, Vite) maneje la transformación.- Usa
"react-jsx"para proyectos Vite + React que usan el automatic JSX runtime. - Usa
"react"solo si necesitas la transformación clásicaReact.createElement(requiere importar React en cada archivo).
¿Cuándo deberías usar type vs interface para props de componentes?
- Usa
typecuando necesites uniones, intersecciones o tipos mapeados. - Usa
interfacecuando necesites declaration merging oextends. - Para la mayoría de props de React,
typees suficiente y más flexible.
¿Cuál es el propósito de moduleResolution: "bundler" en tsconfig?
- Coincide con cómo los bundlers modernos como Next.js y Vite resuelven importaciones.
- Soporta campos
exportsen package.json e importaciones sin extensión. - Reemplaza opciones más antiguas como
"node"o"node16"para proyectos agrupados.
¿Aún necesitas instalar @types/react con React 19?
- React 19 distribuye sus propios tipos, así que
@types/reactpodría no ser necesario. - Verifica tu configuración -- algunas configuraciones aún requieren el paquete separado.
- Cuando dudes, instálalo como una dependencia de desarrollo y deja que TypeScript resuelva conflictos.
¿Cuál es la diferencia entre archivos .ts y .tsx?
- Los archivos
.tsxsoportan sintaxis JSX además de TypeScript. - Los archivos
.tsson para lógica pura de TypeScript sin JSX. - Usa
.tsxpara cualquier archivo que retorne o contenga elementos JSX.
Trampa: ¿Qué sucede si establecer skipLibCheck: true en tu tsconfig?
- TypeScript omite el chequeo de tipos de todos los archivos
.d.ts, incluyendo tus archivos de declaración personalizados. - Esto acelera la compilación pero puede ocultar errores de tipo en tus declaraciones.
- Los errores en archivos
.d.tsde terceros también son ignorados silenciosamente.
¿Por qué usar any derrota el propósito de TypeScript?
anydesactiva silenciosamente el chequeo de tipos para el valor y todo lo que toca posteriormente.- Prefiere
unknown-- te fuerza a estrechar o validar antes de acceder a propiedades. - Si debes usar
any, añade un comentario explicando por qué y un TODO para solucionarlo después.
¿Qué hace el alias de ruta @/* en tsconfig?
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
}
}- Mapea
@/components/Buttona./src/components/Button. - Elimina rutas relativas frágiles como
../../../components/Button.
¿Cómo añaden noUncheckedIndexedAccess y exactOptionalPropertyTypes seguridad extra más allá de strict: true?
noUncheckedIndexedAccessañade| undefinedal acceso de índice de array y registro, detectando bugs de fuera de límites.exactOptionalPropertyTypesdistingue entre una propiedad faltante y una establecida aundefined.- Ninguno está incluido en
strict: true-- deben habilitarse por separado.
¿Cuál es la forma correcta de tipar un prop opcional con un valor por defecto?
type GreetingProps = {
name: string;
age?: number; // opcional
};
function Greeting({ name, age = 25 }: GreetingProps) {
return <p>{name} is {age} years old</p>;
}- Marca el prop como opcional con
?en el tipo. - Proporciona el valor por defecto en el patrón de desestructuración.
- TypeScript estrecha el tipo dentro del cuerpo de la función para excluir
undefined.