ProgramaciónArquitecto de TypeScript

¿Cómo funcionan los tipos condicionales en TypeScript? ¿Qué es el comportamiento distributivo de los tipos condicionales y cómo se trabaja con él? Explica con ejemplos.

Supere entrevistas con el asistente de IA Hintsage

Respuesta.

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'

Comportamiento distributivo

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.

Cómo evitar el comportamiento distributivo

Envolvemos el tipo en una tupla:

type NoDistrib<T> = [T] extends [{id: number}] ? string : boolean; type Result = NoDistrib<{id: number} | {name: string}>; // boolean

Pregunta capciosa.

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

Ejemplos de errores reales debido al desconocimiento de las sutilezas del tema.


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.