TypeScript
Distributividad y control de unions
Comportamiento distributivo de conditional types sobre unions y técnicas para activarlo, evitarlo o controlar transformaciones miembro por miembro.
- Última actualización
- Actualizada
- Nivel
- Profundización
TypeScript
Comportamiento distributivo de conditional types sobre unions y técnicas para activarlo, evitarlo o controlar transformaciones miembro por miembro.
Un conditional type es distributivo cuando comprueba un type parameter desnudo y recibe una union.
type ToArray<T> = T extends unknown
? T[]
: never;type Result = ToArray<string | number>;
// string[] | number[]ToArray<string | number>
→ ToArray<string> | ToArray<number>
→ string[] | number[]Si querías un solo array:
type ToArrayNonDistributed<T> =
[T] extends [unknown]
? T[]
: never;type Result = ToArrayNonDistributed<
string | number
>;
// (string | number)[]Envolver ambos lados en tuples evita la distribución.
type MyExclude<T, TExcluded> =
T extends TExcluded ? never : T;type Status =
| "idle"
| "loading"
| "success";
type FinalStatus = MyExclude<Status, "loading">;Cada miembro se evalúa y never desaparece de la union.
type MyExtract<T, TSelected> =
T extends TSelected ? T : never;type SuccessResult<T> = Extract<
T,
{ status: "success" }
>;type KeysOfUnion<T> =
T extends unknown ? keyof T : never;keyof (A | B) obtiene claves compartidas. Distribuir primero obtiene las claves de cada miembro.
Patrón avanzado:
type UnionToIntersection<T> =
(T extends unknown
? (value: T) => void
: never) extends (
value: infer TIntersection,
) => void
? TIntersection
: never;Se apoya en distribución y contravariance de parámetros. Es útil en librerías, pero puede ser demasiado complejo para código de aplicación.
type IsString<T> = T extends string
? true
: false;
type Result = IsString<string | number>;
// booleanEl resultado es true | false, simplificado a boolean.
Para comprobar la union completa:
type AllStrings<T> = [T] extends [string]
? true
: false;type IsNever<T> = T extends never
? true
: false;Con never, la distribución no tiene miembros y el resultado es never, no true.
Correcto:
type IsNever<T> = [T] extends [never]
? true
: false;any puede tomar ambas ramas. Para utilities públicas, documenta si aceptan any o si el resultado deja de ser confiable.
type PartialUnion<T> =
T extends unknown ? Partial<T> : never;Transforma cada variante conservando su separación.
Aplicar Partial<T> directamente sobre una union puede comportarse de forma homomorphic, pero las operaciones posteriores y claves compartidas requieren entender qué se distribuye.
type Entries<T> = {
[K in keyof T]: [K, T[K]];
}[keyof T];Para una union de objetos:
type DistributedEntries<T> =
T extends object ? Entries<T> : never;Conditional types distributivos anidados sobre unions grandes pueden producir explosión combinatoria.
Síntomas:
Reduce distribución, nombra etapas y evita calcular información que la API no necesita exponer.
type DomainEvent =
| ProductCreated
| ProductDeleted
| OrderCompleted;
type ProductEvent = Extract<
DomainEvent,
{ aggregate: "product" }
>;(A | B)[] y obtener A[] | B[].keyof union con claves de todos los miembros.¿Por qué [T] extends [string] evita distribuir una union?
Porque T ya no aparece desnudo en la condición; la union completa queda dentro de una tuple y se compara como una sola unidad.
Template literal types y key remapping construye strings y nombres de propiedades desde otros tipos.