Tipado de props con TypeScript

Intermedio
React
React
Actualizado: 22/04/2026

Diagrama: tipado de props con TypeScript en React

El tipado estático con TypeScript es uno de los pilares de cualquier base de código React profesional. Cuando una consultora entrega un proyecto a un cliente, el equipo que hereda el código necesita contratos claros sobre qué propiedades espera cada componente, qué son opcionales y qué forma tienen los valores que se pasan entre padres e hijos. Tipar las props permite detectar errores en el editor antes de que lleguen al navegador y sirve como documentación viva.

Interfaces y types para props

La forma más habitual de describir las props de un componente es declarar una interface con el sufijo Props. El nombre sigue la convención NombreComponenteProps para que el enlace entre el tipo y el componente sea inmediato al revisar el código.

// Boton.tsx
interface BotonProps {
  texto: string;
  variante: "primario" | "secundario";
}

export function Boton({ texto, variante }: BotonProps) {
  return <button className={variante}>{texto}</button>;
}

La anotación { texto, variante }: BotonProps aplica desestructuración tipada: TypeScript comprueba que quien use <Boton /> pase ambas propiedades y que variante sea uno de los dos valores literales permitidos. Si el componente consumidor escribe <Boton variante="terciario" />, el compilador marca el error antes de ejecutar nada.

Las type aliases sirven para el mismo propósito y se usan cuando se necesitan uniones, intersecciones o tipos calculados:

type TamanoBoton = "sm" | "md" | "lg";

type BotonProps = {
  texto: string;
  tamano: TamanoBoton;
};

La regla práctica es usar interface cuando describes la forma de un objeto de props y type cuando necesitas combinar uniones, utilidades o formas más complejas. Ambas alternativas producen el mismo efecto en el chequeo de tipos de un componente.

Props opcionales y valores por defecto

Una propiedad se marca como opcional con el símbolo ? después del nombre del campo. El componente puede asignar un valor por defecto mediante desestructuración para que el resto de la lógica trabaje siempre con un valor definido.

interface TarjetaProps {
  titulo: string;
  descripcion?: string;
  destacado?: boolean;
}

export function Tarjeta({ titulo, descripcion = "Sin descripción", destacado = false }: TarjetaProps) {
  return (
    <article className={destacado ? "tarjeta destacada" : "tarjeta"}>
      <h3>{titulo}</h3>
      <p>{descripcion}</p>
    </article>
  );
}

Los valores por defecto se definen en la firma del componente, no con la antigua propiedad estática defaultProps, que React ha eliminado para componentes funcionales. Así el tipo y el valor por defecto viven en el mismo lugar y no hay duplicación.

Children y funciones callback

Las props children representan los elementos anidados que recibe un componente contenedor. Para tiparlas correctamente se usa ReactNode, que cubre cualquier cosa renderizable por React: cadenas, números, elementos JSX, fragmentos, arrays de estos elementos y null.

import type { ReactNode } from "react";

interface PanelProps {
  titulo: string;
  children: ReactNode;
}

export function Panel({ titulo, children }: PanelProps) {
  return (
    <section className="panel">
      <h2>{titulo}</h2>
      <div className="contenido">{children}</div>
    </section>
  );
}

Cuando el componente expone un handler hacia el padre, el tipo de la función describe sus parámetros y su valor de retorno. Para callbacks que no devuelven nada se usa void; para los que devuelven un valor se específica el tipo concreto.

interface BuscadorProps {
  placeholder: string;
  onBuscar: (termino: string) => void;
}

export function Buscador({ placeholder, onBuscar }: BuscadorProps) {
  return (
    <input
      type="search"
      placeholder={placeholder}
      onChange={(event) => onBuscar(event.target.value)}
    />
  );
}

Este patrón de callback tipado es fundamental en formularios, listas filtrables y tablas paginadas, donde el padre controla el estado y los hijos emiten eventos con un contrato estable.

Props compuestas con objetos y arrays

Los componentes reales rara vez reciben solo primitivos. Lo habitual es que se les pasen objetos de dominio o listas que a su vez necesitan un tipo explícito. La práctica recomendada es declarar el tipo del dato fuera del componente, en un fichero de modelos, y reutilizarlo desde la interfaz de props.

// models/Producto.ts
export interface Producto {
  id: number;
  nombre: string;
  precio: number;
  disponible: boolean;
}
// ListaProductos.tsx
import type { Producto } from "./models/Producto";

interface ListaProductosProps {
  productos: Producto[];
  onSeleccionar: (producto: Producto) => void;
}

export function ListaProductos({ productos, onSeleccionar }: ListaProductosProps) {
  return (
    <ul className="lista-productos">
      {productos.map((producto) => (
        <li key={producto.id} onClick={() => onSeleccionar(producto)}>
          {producto.nombre} - {producto.precio.toFixed(2)} €
        </li>
      ))}
    </ul>
  );
}

El componente ListaProductos garantiza en tiempo de compilación que el padre pasa un array cuyos elementos cumplen el contrato Producto. Si intentara renderizar <ListaProductos productos={[{ id: 1, nombre: "Libro" }]} />, el compilador avisaría de que faltan precio y disponible. Este contrato reduce drásticamente la cantidad de incidencias en los equipos que consumen tus componentes.

Extender props nativas del DOM

Cuando un componente envuelve un elemento HTML, conviene heredar las props nativas de ese elemento y añadir las propias. TypeScript expone los tipos ComponentPropsWithoutRef y ComponentPropsWithRef para este caso.

import type { ComponentPropsWithoutRef } from "react";

interface BotonProps extends ComponentPropsWithoutRef<"button"> {
  variante: "primario" | "secundario";
}

export function Boton({ variante, className, ...resto }: BotonProps) {
  const clases = `${variante} ${className ?? ""}`.trim();
  return <button className={clases} {...resto} />;
}

Al extender ComponentPropsWithoutRef<"button"> el componente acepta automáticamente onClick, disabled, type, aria-* y cualquier atributo estándar de un <button>. La propagación {...resto} los transmite al elemento nativo y variante queda reservada como prop personalizada sin filtrarse al DOM.

Este patrón compone interfaces de diseño internas que respetan las APIs estándar de accesibilidad y HTML, algo imprescindible en proyectos corporativos donde la accesibilidad forma parte del contrato de entrega.

Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, React es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de React

Explora más contenido relacionado con React y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

Definir interfaces para props de componentes. Utilizar types frente a interfaces. Tipar children, funciones callback y props opcionales. Aplicar valores por defecto en componentes funcionales.