Ir al contenido principal

Condicionales y discriminantes para tipos de datos en Typescript

 Consigna: typescript tipar un boolean que discrimine que la siguiente propiedad sea número o fecha


Respuesta:

Para discriminar si una propiedad es de tipo number o Date basándote en un valor booleano (o una condición), la técnica más robusta en TypeScript es usar tipos condicionales con la palabra clave extends. Esto permite definir un tipo que cambia dinámicamente según la estructura de los datos. 

Aquí tienes la implementación directa:

// 1. Definir el tipo condicional
// Si T tiene una propiedad 'esNumero' true, devuelve number. Si no, devuelve Date.
type PropiedadDiscriminada = T extends { esNumero: true } 
  ? number 
  : Date;

// 2. Uso en una función o interfaz
function procesarDato<T extends { esNumero: boolean }>(
  datos: T,
  valor: PropiedadDiscriminada
): void {
  console.log(valor);
}

// Caso A: esNumero es true -> El segundo argumento debe ser number
procesarDato({ esNumero: true }, 42);       // ✅ Correcto

// Caso B: esNumero es false -> El segundo argumento debe ser Date
procesarDato({ esNumero: false }, new Date()); // ✅ Correcto

// Caso C: Error de tipo
// procesarDato({ esNumero: false }, 42);     // ❌ Error: Type 'number' is not assignable to type 'Date'

Explicación clave

  • extends: Evalúa si el tipo genérico T cumple con la estructura { esNumero: true }.

  • ? y :: Sintaxis de operador ternario para tipos. Si la condición es true, el tipo resultante es number; si es false (o cualquier otro caso), es Date.

  • Discriminación: TypeScript verifica en tiempo de compilación que el valor pasado coincida con el tipo inferido por la condición booleana. 

Alternativa: Union Types con Discriminante (Discriminated Unions)

Si prefieres una estructura más explícita para estados complejos, usa uniones discriminadas:

type DatoNumerico = {
  tipo: 'numero';
  valor: number;
};

type DatoFecha = {
  tipo: 'fecha';
  valor: Date;
};

type Dato = DatoNumerico | DatoFecha;

function manejarDato(dato: Dato) {
  switch (dato.tipo) {
    case 'numero':
      console.log(dato.valor.toFixed(2)); // TS sabe que es number
      break;
    case 'fecha':
      console.log(dato.valor.toISOString()); // TS sabe que es Date
      break;
  }
}

Esta segunda opción es más segura si necesitas manejar múltiples tipos o lógica compleja, ya que el compilador fuerza el manejo de todos los casos posibles.

Comentarios