Los tipos condicionales en TypeScript permiten describir un tipo a través de otro con el principio T extends U ? X : Y. Estos tipos ofrecen poderosas capacidades al construir lógica de tipos compleja, especialmente en bibliotecas y declaraciones de API.
Ejemplo de un tipo condicional básico:
type IsString<T> = T extends string ? 'yes' : 'no'; type A = IsString<string>; // 'yes' type B = IsString<number>; // 'no'
Si un tipo condicional se aplica a un tipo unión, TypeScript "distribuye" la condición sobre cada elemento de la unión por separado. Esto se llama comportamiento distributivo de los tipos condicionales.
Ejemplo:
type Foo<T> = T extends { id: number } ? string : boolean; type Result = Foo<{id: number} | {name: string}>; // string | boolean
Esta es una característica muy poderosa, pero puede causar confusión con el resultado esperado, especialmente al trabajar con arreglos y mapeo de tipos.
Envolvemos el tipo en una tupla:
type NoDistrib<T> = [T] extends [{id: number}] ? string : boolean; type Result = NoDistrib<{id: number} | {name: string}>; // boolean
Pregunta: "¿Qué sucede si se utiliza un tipo condicional con tipos unión sin envolverlo en tuplas? ¿Siempre es el mismo resultado que con la lógica normal?"
Respuesta: ¡El resultado puede ser inesperado! Debido a la distributividad, la condición se aplica a cada miembro de los tipos unión por separado. Para comparar estrictamente todo el tipo unión, se necesita utilizar un envoltorio (tupla).
Ejemplo:
type Test<T> = T extends string ? number : boolean; type A = Test<string | boolean>; // number | boolean, no solo boolean
Historia
En una biblioteca para serialización, se utilizó un tipo condicional para verificar la estructura de datos, pero se olvidaron de envolver el parámetro genérico en una tupla. Como resultado, en tipos unión complejos, las declaraciones fallaron y el compilador produjo tipos impredecibles al utilizar la API.
Historia
Al intentar implementar una transformación de tipos para procesar campos de modelos, se perdió parte de la información: debido a que la distributividad genera uniones, se olvidaron de manejar un par de ramas de la lógica, y al final la tipificación se volvió demasiado permisiva.
Historia
Un desarrollador asumió que T extends SomeType dentro de los tipos condicionales se comportaría como esperábamos para todo el objeto, pero ocurrió una "dispersión" — el compilador señaló una inconsistencia, y comenzaron el caos de tipos y serios errores en la generación de documentación automática.