0Pricing
React Academy · Lección

Uniones discriminadas para variantes de componentes

Modele las props de variantes con uniones discriminadas para que TypeScript imponga combinaciones de props válidas.

Uniones discriminadas para variantes de componentes es una lección gratuita de React Academy en CoddyKit. Esta es la lección 1 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de React Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de React Academy incluye 4 lecciones en total.

El problema de las props de variantes

Un componente con props opcionales para distintos modos (por ejemplo, un botón que puede ser un enlace o un botón) puede admitir combinaciones de props no válidas. TypeScript no puede detectarlas sin uniones discriminadas.

¿Qué es una unión discriminada?

Una unión discriminada es una unión de tipos que comparten un campo de tipo literal común (el discriminante). TypeScript estrecha el tipo según el valor de ese campo.

type Shape =
  | { kind: 'circle'; radius: number }
  | { kind: 'rectangle'; width: number; height: number };

function area(shape: Shape): number {
  if (shape.kind === 'circle') return Math.PI * shape.radius ** 2;
  return shape.width * shape.height; // TS knows width/height exist here
}

Variante de botón frente a enlace

Modele un componente polimórfico con una unión discriminada basada en un campo as o variant para exigir las props correctas en cada caso.

type ButtonProps =
  | { as: 'button'; onClick: () => void; disabled?: boolean; children: React.ReactNode }
  | { as: 'a'; href: string; target?: string; children: React.ReactNode };

function ActionButton(props: ButtonProps) {
  if (props.as === 'button') {
    return <button onClick={props.onClick} disabled={props.disabled}>{props.children}</button>;
  }
  return <a href={props.href} target={props.target}>{props.children}</a>;
}

Variantes del componente Alert

Modele un Alert con datos obligatorios diferentes para cada tipo de severidad.

type AlertProps =
  | { type: 'success'; message: string }
  | { type: 'error'; message: string; onRetry: () => void }
  | { type: 'warning'; message: string; details?: string };

function Alert(props: AlertProps) {
  if (props.type === 'error') {
    return (
      <div className="alert error">
        <p>{props.message}</p>
        <button onClick={props.onRetry}>Retry</button>
      </div>
    );
  }
  return <div className={`alert ${props.type}`}>{props.message}</div>;
}

Estrechamiento en controladores de eventos

Las uniones discriminadas también funcionan con datos controlados por eventos, por lo que resultan ideales para máquinas de estados o estados basados en acciones.

type LoadingState<T> =
  | { status: 'idle' }
  | { status: 'loading' }
  | { status: 'success'; data: T }
  | { status: 'error'; error: Error };

function DataView<T>({ state }: { state: LoadingState<T> }) {
  if (state.status === 'loading') return <Spinner />;
  if (state.status === 'error') return <p>{state.error.message}</p>;
  if (state.status === 'success') return <pre>{JSON.stringify(state.data)}</pre>;
  return null;
}

Comprobaciones exhaustivas con never

Añada una comprobación de never en la rama predeterminada para que TypeScript informe de un error si se añade un nuevo miembro a la unión pero no se gestiona.

function assertNever(x: never): never {
  throw new Error('Unhandled case: ' + x);
}

function renderIcon(type: AlertProps['type']) {
  switch (type) {
    case 'success': return <CheckIcon />;
    case 'error': return <XIcon />;
    case 'warning': return <WarnIcon />;
    default: return assertNever(type); // TS error if a case is missing
  }
}

Uniones discriminadas para respuestas de API

Modele las estructuras de respuesta de la API como uniones discriminadas para que quienes las consuman gestionen las rutas de éxito y error sin conversiones de tipo.

type ApiResult<T> =
  | { ok: true; data: T }
  | { ok: false; error: string; code: number };

async function fetchUser(id: string): Promise<ApiResult<User>> {
  const res = await fetch(`/api/users/${id}`);
  if (!res.ok) return { ok: false, error: 'Not found', code: res.status };
  return { ok: true, data: await res.json() };
}

Type guards para el estrechamiento

Use type guards personalizados para estrechar uniones en escenarios más complejos, cuando el discriminante no sea una simple comprobación de igualdad.

function isSuccess<T>(result: ApiResult<T>): result is { ok: true; data: T } {
  return result.ok === true;
}

const result = await fetchUser('1');
if (isSuccess(result)) {
  console.log(result.data.name); // TS knows data exists
}

Evitar la acumulación de props opcionales

Sin uniones discriminadas, los componentes acumulan props opcionales que solo son válidas en determinadas combinaciones, lo que resulta confuso y carece de tipado. Las uniones discriminadas eliminan los estados imposibles.

// Bad: optional prop soup — invalid combos allowed:
interface BadProps {
  href?: string;
  onClick?: () => void;
  disabled?: boolean;
}

// Good: only valid combos via discriminated union:
type GoodProps =
  | { as: 'a'; href: string }
  | { as: 'button'; onClick: () => void; disabled?: boolean };

Composición de uniones

Use & (intersección) para añadir props compartidas a todos los miembros de una unión.

type BaseProps = { className?: string; children: React.ReactNode };

type ButtonVariant =
  | (BaseProps & { variant: 'primary'; onClick: () => void })
  | (BaseProps & { variant: 'link'; href: string });

Discriminación en tiempo de ejecución

React usa el discriminante en tiempo de ejecución para renderizar la interfaz adecuada. TypeScript lo usa en tiempo de compilación para exigir un uso correcto de las props. Ambas capas quedan protegidas.

Comprobación rápida

¿Qué es el discriminante en una unión discriminada?

Resumen

Las uniones discriminadas modelan componentes con conjuntos de props mutuamente excluyentes mediante un campo discriminante de tipo literal compartido. Use never en la rama predeterminada para realizar comprobaciones exhaustivas. Eliminan las combinaciones de props no válidas que permiten las props opcionales, y hacen que los componentes sean autoexplicativos y seguros en cuanto a tipos.

Preguntas frecuentes

¿La lección «Uniones discriminadas para variantes de componentes» es gratis?

Sí — el texto completo de «Uniones discriminadas para variantes de componentes» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de React Academy, actualiza a CoddyKit PRO. El curso de React Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Uniones discriminadas para variantes de componentes»?

Modele las props de variantes con uniones discriminadas para que TypeScript imponga combinaciones de props válidas. Practicas React Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar React Academy?

No se requiere experiencia previa. React Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 1 de 4.

¿Cuánto tiempo toma la lección «Uniones discriminadas para variantes de componentes»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de React Academy?

Sí. Cada lección de React Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Uniones discriminadas para variantes de componentes
  2. Tipos condicionales y mapeados en React
  3. Componentes polimórficos con la prop 'as'
  4. Formularios con seguridad de tipos y contratos de respuestas de API
← Volver a React Academy