¿Te has encontrado con que tu tipado en TypeScript no es tan seguro como pensabas?

El truco

Antes (mal):

type MessageType = 'success' | 'error';

function handleMessage(type: MessageType, message: string) {
  if (type === 'success') {
    console.log('Success:', message);
  } else if (type === 'error') {
    console.log('Error:', message);
  }
}

handleMessage('info', 'Esta es una información'); // Error en tiempo de ejecución

Después (bien):

type MessageType = 'success' | 'error';
type Response<T extends MessageType> = T extends 'success' ? { data: string } : { error: string };

function handleMessage<T extends MessageType>(type: T, payload: Response<T>) {
  if (type === 'success') {
    console.log('Success:', payload.data);
  } else if (type === 'error') {
    console.log('Error:', payload.error);
  }
}

handleMessage('success', { data: 'Operación completada' }); // ✔️ Bien tipado
handleMessage('error', { error: 'Fallo en la operación' }); // ✔️ Bien tipado
// handleMessage('success', { error: 'Esto da error de tipo' }); // ❌ Error de compilación

Los tipos condicionales de TypeScript te permiten definir tipos que dependen de un valor de tipo, agregando una capa extra de seguridad. En el ejemplo, Response<T> retorna tipos específicos dependiendo del valor de T, asegurando que las propiedades pasadas a handleMessage sean correctas. Esto evita errores en tiempo de ejecución al definir una correspondencia clara entre el tipo y el contenido del mensaje.

Aprovechar los tipos condicionales asegura que el tipado sea robusto y evita errores comunes al manejar múltiples tipos de datos.