Volver al Blog

React + TypeScript: Lo que aprendí después de perder horas en bugs

React + TypeScript: Lo que aprendí después de perder horas en bugs

La primera vez que metí TypeScript en un proyecto React fue porque me lo exigieron. Y yo, necio, me puse a tipar todo sin saber lo que estaba haciendo. Spoiler: me arrepentí a los 20 minutos.

Literalmente pasé una hora tratando de entender por qué useState me tiraba error. "Expected 0 arguments, but got 1" decía el mensaje. Yo pensaba que era un bug. Después entendí que lo estaba usando mal por no declarar correctamente el tipo, o peor, por declararlo doble.

Props y componentes: el dolor de cabeza inicial

Hay algo medio incómodo al principio con TS en React. Te obliga a pensar. Y eso, si vienes de escribir JS rápido y sin red, te da pereza.

Ejemplo real. Tenía un componente llamado TarjetaProducto que recibía un producto con 10 propiedades. Yo feliz pasando props sin más. Pero con TypeScript... había que tiparlo.

type Producto = {
  id: string;
  nombre: string;
  precio: number;
  stock: number;
  descripcion?: string;
  imagen?: string;
  activo: boolean;
  // y varios campos más...
}

¿Qué hice? Copié el tipo desde Postman manualmente. Mal. Luego cambiaron la API y el frontend petó todo. Moraleja: define bien los tipos y si puedes, genera los modelos automáticamente desde Swagger o usa Zod para validarlos.

Default values vs Props opcionales

Algo que no entendía al principio: cuándo usar el ? en los tipos y cuándo usar valores por defecto.

type Props = {
  titulo: string;
  subtitulo?: string;
}

Y luego:

const Header = ({ titulo, subtitulo = 'Sin subtítulo' }: Props) => {
  return (
    <div>
      <h1>{titulo}</h1>
      <h2>{subtitulo}</h2>
    </div>
  )
}

La diferencia no es solo técnica. Es de intención. Si vas a forzar a alguien a enviar una prop, tipala como obligatoria. Si puedes ofrecerle un valor razonable, ponle valor por defecto. Me ahorró muchos errores con juniors del equipo que no leían bien los docs internos.

Estados y tipos que se infieren

Esto me lo enseñó una colega (gracias Vale). Yo hacía esto:

const [cantidad, setCantidad] = useState<number>(0)

Pero claro, si el valor inicial es 0, TypeScript ya lo sabe. Entonces basta con:

const [cantidad, setCantidad] = useState(0)

Igual hay casos donde SÍ necesitas indicar el tipo, por ejemplo cuando el valor inicial es null o una llamada a función. Pero si no, no te compliques.

Mapear listas con tipos literales

Esta es una de esas cosas que uno aprende con el tiempo y se siente como truco de magia.

const roles = ['admin', 'user', 'guest'] as const;

type Rol = typeof roles[number];

Ahora Rol es el tipo 'admin' | 'user' | 'guest'. Maravilla. Y si lo usas para validar props o condiciones, tu editor te lo sugiere automáticamente. Lo amé desde el día uno.

Cuando el tipado excesivo te estorba

Hubo una etapa donde traté de tipar hasta los handlers de botones:

const handleClick: MouseEventHandler<HTMLButtonElement> = (e) => {
  // ...
}

Hermano... relájate. Si el editor ya lo infiere, no lo pongas. Solo lo estás complicando. Eso me lo repetía un mentor de forma muy directa: "El mejor tipo es el que no necesitas escribir". Tiene razón.

¿Qué hago con las APIs?

Este fue un infierno. La API de backend cambiaba cada dos días y yo me la pasaba corrigiendo tipos. Intenté usar tipos manuales, luego Swagger, luego ts-interface-builder. Nada me convencía. Hasta que descubrí Zod.

Con Zod defines tu esquema de validación así:

const UserSchema = z.object({
  id: z.string(),
  name: z.string(),
  email: z.string().email(),
})

Y luego validas los datos directamente:

const data = await res.json();
const usuario = UserSchema.parse(data);

Si hay error, te lo dice. Y encima puedes usar z.infer<typeof UserSchema> para generar el tipo TypeScript. Espectacular. Me salvó en producción varias veces.

Custom hooks con tipos genéricos (pero que no den miedo)

Una vez hice un hook tan genérico que me dio miedo usarlo. Literalmente no recordaba cómo se usaba. Tenía tres niveles de genéricos y un Partial<T> con Record<string, T[keyof T]> o algo así. Muy lindo... pero nadie lo entendía.

Ahora los hago simples. Por ejemplo:

function useLocalStorage<T>(key: string, defaultValue: T) {
  const [value, setValue] = useState<T>(() => {
    const json = localStorage.getItem(key);
    return json ? JSON.parse(json) : defaultValue;
  });

  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(value));
  }, [key, value]);

  return [value, setValue] as const;
}

Eso lo entiende cualquiera. Y se puede usar así:

const [nombre, setNombre] = useLocalStorage<string>('nombre', '')

Momentos donde TypeScript me salvó el pellejo

  • Detectar que alguien renombró una propiedad en la respuesta de una API, y me olvidé de actualizar el frontend
  • Evitar pasar un booleano donde esperaba un string (me pasó en un form)
  • Validar que un componente de pago recibiera todos los props que requería
  • Evitar un bucle infinito en un useEffect por depender mal de una prop opcional

Y también me hizo perder tiempo, la verdad

Por ejemplo, me costó más de 20 minutos entender este error:

Type 'null' is not assignable to type 'User'

¿La solución? Tipar así:

const [user, setUser] = useState<User | null>(null)

¿Simple? Sí. ¿Irritante cuando no sabes lo que estás haciendo? También. Pero con el tiempo se vuelve natural.

Resumen sin resumen

Si llegaste hasta aquí, seguro te sentiste identificado con al menos uno de estos puntos. Y si recién estás empezando con TS y React, no te frustres. A todos nos pasó. Lo importante es no rendirse en la curva inicial. Porque después, lo juro, todo fluye mucho mejor.

No hay una fórmula mágica ni un manual único. Hay cosas que simplemente aprendes por las malas. Y si puedes evitártelas leyéndolas por aquí, pues dale con todo.

C

Sobre Carlos Donoso

Full Stack Developer y AI Engineer apasionado por crear soluciones innovadoras. Me especializo en desarrollo web moderno, inteligencia artificial y automatización. Comparto conocimiento para ayudar a otros developers a crecer en su carrera.

Comentarios

Comparte tu opinión

0/1000 caracteres

No hay comentarios aún

Sé el primero en compartir tu opinión sobre este artículo.

¡Enlace copiado al portapapeles!