00869-extreme-distributeunions

Back

// 整体思路:利用联合类型可以分发的特性,分别处理数组和对象,数组可以用 [infer F, ...infer Rest] 来遍历每一项再递归分发再组合, 对象通过取到每一项key,用Omit<K, key> 去遍历每一项,最后再组合

type IsNever<T> = [T] extends [never] ? true : false

type DistributeUnions<T> =
  T extends unknown[] ? DistributeArray<T>
  : T extends object ? DistributeObject<T>
    : T

type JoinArray<F, R extends unknown[]> =
  F extends any ? [F, ...R] : never

type DistributeArray<T extends unknown[]> =
  T extends [infer F, ...infer Rest] ? JoinArray<DistributeUnions<F>, DistributeArray<Rest>> : []

type HandleObjectChild<V, K extends keyof any> =
  V extends any ? { [key in K]:  V } : never

//  处理对象某一属性及其子对象
type HandleObjectItem<T, K extends keyof T, V = T[K]> =
  V extends any ? HandleObjectChild<DistributeUnions<V>, K> : never

// 处理对象中剩余属性
type HandleObjectRest<T, K extends keyof T, U extends keyof T = K> =
  IsNever<Exclude<U, K>> extends true ? {} : DistributeObject<T, Exclude<U, K>>

// 合并对象
type JoinObject<T, O> =
  T extends any ?
    O extends any ? { [key in keyof T | keyof O]: key extends keyof T ? T[key] : key extends keyof O ? O[key] : never } : never : never

// 遍历对象
type DistributeObject<T, K extends keyof T = keyof T, U extends keyof T = K> =
  K extends any ? JoinObject<HandleObjectItem<T, K>, HandleObjectRest<T, K, U>>  : never

Solution by moonshadow-miao #34226

type Param<F> = [F] extends [(p: infer P) => void] ? P : never;
type Intersection<T> = Param<T extends unknown ? (p: T) => void : never>;

type Pick1<T, K extends keyof T, V> = [V extends V ? {[K1 in K]: V} : never];

type DistrObj<T, K extends keyof T = keyof T> = (Intersection<K extends K ? Pick1<T, K, Distr<T[K]>> : never> & [unknown])[0];

type DistrArr<A extends unknown[], R extends unknown[] = []> = A extends [infer F, ...infer T]
    ? Distr<F> extends infer Fi ? Fi extends Fi ? DistrArr<T, [...R, Fi]> : never : never
    : R;

type Distr<T> = T extends unknown[] ? DistrArr<T> : T extends object ? DistrObj<T> : T;

type Norm<T> = T extends object ? {[K in keyof T]: Norm<T[K]>} : T;

type DistributeUnions<T> = Norm<Distr<T>>;

Solution by alexandroppolus #30996

type Union2Intersection<T> = (T extends T ? (arg: T) => any : never) extends (arg: infer P) => any ? P : never;
type UnionLast<T> = Union2Intersection<T extends T ? () => T : never> extends () => infer R ? R : never;
type Union2Array<T, _TL = UnionLast<T>> = [T] extends [never] ? [] : [...Union2Array<Exclude<T, _TL>>, _TL];
// type IsUnion<T, U = T> = U extends T ? [Exclude<T, U>] extends [never] ? false : true : false;
type ExtractObj<T> =
  T extends [] ? [] :
  T extends [infer F, ...infer R extends any[]] ? [ExtractObj<F>, ...ExtractObj<R>] : T extends object ? T[keyof T] : T;

/**数组交叉 */
type UnionTuplesCross<T extends any[], _DistributeObj extends boolean = true> =
  T extends [infer TF, ...infer TR] ?
  [TF] extends [never] ? never :
  [UnionLast<TF> extends object ? _DistributeObj extends true ? DistributeUnions<UnionLast<TF>> : UnionLast<TF> : UnionLast<TF>, ...UnionTuplesCross<TR>] | UnionTuplesCross<[Exclude<TF, UnionLast<TF>>, ...TR]>  // ×[1 | 2, 'a' | 'b', false | true] ==> [1, ...×['a' | 'b', false | true]] | [2, ...×['a' | 'b', false | true]] ==> [1, ...( [`a`, ...×[false | true] | [`b`, ...×[false | true] )]] | [2, ...( [`a`, ...×[false | true] | [`b`, ...×[false | true] )]]
  : [];

/**结构体部分 */
namespace someComplicatedHelper {
  type ObjectTypes2Array<T extends object, _Single extends object = { [P in keyof T]: { [K in P]: T[K] extends object ? UnionObjectsCross<T[K]> : T[K] } }> = Union2Array<_Single[keyof _Single]>;
  type ToKeyTypesObject<T extends object, _TUL = UnionLast<ExtractObj<T>>> = { [P in keyof T]: _TUL } | ([Exclude<ExtractObj<T>, _TUL>] extends [never] ? never : ToKeyTypesObject<{ [P in keyof T]: Exclude<ExtractObj<T>, _TUL> }>);
  type KTOA<T extends any[]> = T extends [infer F extends object, ...infer R extends object[]] ? [ToKeyTypesObject<F>, ...KTOA<R>] : [];
  type CombObj<T extends object | unknown> = { [P in keyof T]: T[P] };
  type CombSingleObjsCross<T extends object[]> = T extends [infer F, ...infer R extends object[]] ? F & CombSingleObjsCross<R> : unknown;
  type ObjectUnionCross<T extends object> = CombObj<CombSingleObjsCross<UnionTuplesCross<KTOA<ObjectTypes2Array<T>>, false>>>

  /**结构体交叉 */
  export type UnionObjectsCross<T extends object, _UL = UnionLast<T>> =
    [T] extends [never] ? never : ObjectUnionCross<_UL extends object ? _UL : never> | UnionObjectsCross<Exclude<T, _UL>>;
}

type DistributeUnionsInner<T> = T extends any[] ? UnionTuplesCross<T> :
  T extends object ? someComplicatedHelper.UnionObjectsCross<T> :
  T;

type DistributeUnions<T> = T extends any[] ? DistributeUnionsInner<DistributeUnionsInner<T>> : DistributeUnionsInner<T>;

Solution by E-uler #25793

// your answers
type DistributeTuple<T extends unknown[]> = 
  T extends [infer Head, ...infer Rest]
  ? DistributeUnions<Head> extends infer Value
    ? Value extends unknown
      ? [Value, ...DistributeTuple<Rest>]
      : never
    : never
  : T

type DistributeRecord<T extends Record<PropertyKey, unknown>, _Key extends keyof T = keyof T> = 
  T extends Record<PropertyKey, never> ? {} : 
  _Key extends unknown
  ? DistributeUnions<T[_Key]> extends infer Value
    ? Value extends unknown
      ? DistributeRecord<Omit<T, _Key>> extends infer Rest
        ? Rest extends unknown
          ? {[P in keyof T]: P extends keyof Rest ? Rest[P] : Value}
          : never
        : never
      : never
    : never
  : never

type DistributeUnions<T> = 
  T extends unknown[]
  ? DistributeTuple<T>
  : T extends Record<PropertyKey, unknown>
    ? DistributeRecord<T>
    : T

Solution by jxhhdx #24544

Version 1: It works

type DistributeTuple<T extends unknown[]> = 
  T extends [infer Head, ...infer Rest]
  ? DistributeUnions<Head> extends infer Value
    ? Value extends unknown
      ? [Value, ...DistributeTuple<Rest>]
      : never
    : never
  : T

type DistributeRecord<T extends Record<PropertyKey, unknown>, _Key extends keyof T = keyof T> = 
  T extends Record<PropertyKey, never> ? {} : 
  _Key extends unknown
  ? DistributeUnions<T[_Key]> extends infer Value
    ? Value extends unknown
      ? DistributeRecord<Omit<T, _Key>> extends infer Rest
        ? Rest extends unknown
          ? {[P in keyof T]: P extends keyof Rest ? Rest[P] : Value}
          : never
        : never
      : never
    : never
  : never

type DistributeUnions<T> = 
  T extends unknown[]
  ? DistributeTuple<T>
  : T extends Record<PropertyKey, unknown>
    ? DistributeRecord<T>
    : T

playground for version 1

Version 2: More effecient algorithm

type UnionToIntersection<T> = (T extends any ? (x: T) => void : never) extends (x: infer U) => void ?  U : never
type PopUnion<T> = UnionToIntersection<T extends any ? (x: T) => void : never> extends (x: infer U) => void ? U : never
type OneAndRest<T> = [PopUnion<T>, Exclude<T, PopUnion<T>>]

type DistributeTuple<T extends unknown[]> = 
  T extends [infer Head, ...infer Rest]
  ? DistributeUnions<Head> extends infer Value
    ? Value extends unknown
      ? [Value, ...DistributeTuple<Rest>]
      : never
    : never
  : T

type DistributeRecord<T extends Record<PropertyKey, unknown>> = 
  T extends Record<PropertyKey, never> ? {} : 
  OneAndRest<keyof T> extends [infer Key extends keyof T, infer RestKeys extends keyof T]
  ? DistributeUnions<T[Key]> extends infer Value
    ? Value extends unknown
      ? DistributeRecord<Pick<T, RestKeys>> extends infer RestRecord
        ? RestRecord extends unknown
          ? {[P in keyof T]: P extends keyof RestRecord ? RestRecord[P] : Value}
          : never
        : never
      : never
    : never
  : never

type DistributeUnions<T> = 
  T extends unknown[]
  ? DistributeTuple<T>
  : T extends Record<PropertyKey, unknown>
    ? DistributeRecord<T>
    : T

playground for version 2

Solution by zhaoyao91 #23086

// Tuple ====================
type _DistributeTuple<T extends unknown[]> =
  T extends [ infer F, ...infer R ]
    ? F extends F
      ? _DistributeNestForTuple<DistributeUnions<F>, _DistributeTuple<R>>
      : never
    : []

type _DistributeNestForTuple<T, Rest extends unknown[]> = T extends T ? [ T, ..._DistributeTuple<Rest>] : never
// Tuple ====================


// Object ===================
type _Never2Unknown<T> = [T] extends [never] ? unknown : T

type _CombineObject<T, _T extends T = T> =
(T extends T ? T & _Never2Unknown<Exclude<_T, Pick<_T, keyof T>>> : never) extends infer I
  ? { [K in keyof I]: I[K] }
  : never

type _DistributeByKey<T, K extends string> = T extends T ? { [_K in K]: T } : never

type _DistributeSingleObject<T extends object, _K extends keyof T = keyof T> =
  _K extends _K
    ? T[_K] extends infer V
      ? V extends object
        ? _DistributeByKey< // For nesting
        _CombineObject<_DistributeSingleObject<V>>,
        _K & string
        >
        : { [__K in _K]: V }
      : never
    : never

type _DistributeObject<T extends object> = T extends T ? _CombineObject<_DistributeSingleObject<T>> : never
// Object ===================


// Main ====================
type DistributeUnions<T> = T extends unknown[]
  ? _DistributeTuple<T>
  : T extends object
    ? _DistributeObject<T>
    : T
// Main ====================

Solution by lvjiaxuan #23028

// your answers
// 把前面几位的代码翻看了几十遍后模仿着写了一边
// Merze { } & { } => {...} 
// UnionToIntersection { } | { } => { } & { } 
// DistObj { ... , ... } => { _temp:{} } | { _temp:{} } => { _temp:{} } & { _temp:{} } => {} & {}
// ----------------------------------------------------------------------------
type Merze<T> = T extends Function ? T : { [K in keyof T]: T[K] };
// ----------------------------------------------------------------------------
type UnionToIntersection<U>
    = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
// ----------------------------------------------------------------------------
type ValueExt<K, V> = V extends V ? { [_ in K & string]: V } : never
// ----------------------------------------------------------------------------
type DistObj<T, K extends keyof T = keyof T> = Merze<
    UnionToIntersection<
        K extends K ? { _temp: ValueExt<K, DistributeUnions<T[K]>> } : never
    > extends { _temp: infer O } ? O : {}>
// ----------------------------------------------------------------------------
type ArrExt<A extends any[], U> = A extends A ? U extends U ? [U, ...A] : never : never
// ----------------------------------------------------------------------------
type DistArr<T> = T extends [infer U, ... infer L] ? ArrExt<DistArr<L>, DistributeUnions<U>> : []
// ----------------------------------------------------------------------------
type DistributeUnions<T>
    = T extends readonly any[]
    ? DistArr<T>
    : T extends Function
    ? T // 这个似乎还得完善一下。。。
    : T extends { [_: string]: any }
    ? DistObj<T>
    : T
// ----------------------------------------------------------------------------

Solution by goddnsgit #22276

// your answers
type Equals<
    A,B> =(<X>()=>X extends A ? 1 : 2) extends (<Y>()=>Y extends B ? 1 : 2) 
    ? true 
    : false ;

type UnionToIntersecton<
    U > =
    [U] extends [never]
    ? never
    :(U extends U ? (a:U)=>any : never) extends (a:infer R)=>any ? R : never;

type UnionToReturnType<
    U > =
    U extends U 
    ? (...a:any)=>U
    : never;

type ResultType<
    U > =
    U extends (...a:any)=>infer R ? R : any   

type LastOfUnion<
    U > =
    ResultType<UnionToIntersecton<UnionToReturnType<U>>>

type DistributeValue<
    T extends object,
    Key extends keyof T,
    Value = T[Key]> =
    Value extends Value 
    ? {[J in keyof T] : J extends Key ? DistributeMemberOnce<Value> : T[J]} 
    : never ;

type DistributeMemberOnce<
    T,
    Keys extends keyof T = keyof T,
    LastKey extends keyof T = LastOfUnion<Keys>&keyof T > =  
     T extends object
    ? [LastKey] extends [never]
    ? T
    : DistributeMemberOnce<DistributeValue<T,LastKey>,Exclude<Keys,LastKey>>
    : T ;

type DistributeUnionOnce<
    T > =
    T extends T
    ? DistributeMemberOnce<T>
    : never  ;

type DistributeUnions<
    T > =
    Equals<T,DistributeUnionOnce<T>> extends true
    ? T
    : DistributeUnions<DistributeUnionOnce<T>>  ;

Solution by justBadProgrammer #16841

type DistributeUnions<T>
  = T extends unknown[] ? DistributeArray<T>
  : T extends object ? Merge<DistributeObject<T>>
  : T

type DistributeArray<A extends unknown[]>
  = A extends [infer H, ...infer T]
  ? ArrHelper<DistributeUnions<H>, T>
  : []
type ArrHelper<H, T extends unknown[]> = H extends H ? [H, ...DistributeArray<T>] : never

type DistributeObject<O extends object, K extends keyof O = keyof O>
  = [K] extends [never] ? {}
  : K extends K ? ObjHelper<K, DistributeUnions<O[K]>> & DistributeObject<Omit<O, K>>
  : never
type ObjHelper<K, V> = V extends V ? { [k in K & string]: V } : never

type Merge<O> = { [K in keyof O]: O[K] }

Solution by Alexsey #11761

type DistributeUnions<T> = [T] extends [never] ? never : T extends T ? Distribute<{[K in keyof T]: DistributeUnions<T[K]>}> : never

type Distribute<T, K extends keyof T = PickOne<T>> = [K] extends [never] ? T : DistributeByValue<T, K, T[K]>

type DistributeByValue<T, K extends keyof T, V> = [V] extends [never] ? never : V extends V ? DistributeUnions<{[P in keyof T]: P extends Keys<T> ? DistributeUnions<P extends K ? V : T[P]> : T[P]}> : never

type PickOne<T> = ExtractOne<{[K in Keys<T>]: Set<T[K & Keys<T>]> extends Set<infer I> ? I extends I ? [Exclude<T[K], I>] extends [never] ? never : K : never : never}[Keys<T>]>

type ExtractOne<U> = (U extends U ? (_: Promise<U>) => void : never) extends (_: infer I extends Promise<U>) => void ? Awaited<I> : never

type Keys<T> = T extends unknown[] ? keyof T & `${number}` : keyof T

Playground

Solution by teamchong #11515

Playground to test the solution

type Resolve<T> = T extends Function ? T : { [K in keyof T]: T[K] };

type LooseLookup<T, K extends PropertyKey> = T[K & keyof T];

type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
  k: infer I
) => void
  ? I
  : never;

type _DistributeUnions<T> = {
  [K in keyof T]: {
    PROP: T[K] extends infer U
      ? U extends any
        ? { [KK in K]: U }
        : never
      : never;
  };
};

type DistributeUnions<
  T,
  DT = { [K in keyof T]: DistributeUnions<T[K]> }
> = DT extends object
  ? Resolve<
      LooseLookup<UnionToIntersection<_DistributeUnions<DT>[keyof DT]>, "PROP">
    > extends infer RES
    ? RES extends any
      ? DT extends readonly any[]
        ? { [K in keyof DT]: LooseLookup<RES, K> }
        : RES
      : never
    : never
  : DT;

Solution by jfet97 #11311

type PopUnion<T> = UnionToIntersection<T extends any ? (x: T) => 1 : never> extends (x: infer U) => 1 ? U : never
type UnionToIntersection<T> = (T extends any ? (x: T) => 1 : never) extends (x: infer U) => infer R ?  U : never
type UnionToTuple<T, U = PopUnion<T>> = [T] extends [never] ? [] : [U, ...UnionToTuple<Exclude<T, U>>]

type Merge<T> = {[P in keyof T]: T[P]}

type DistributeObject<T, U = UnionToTuple<keyof T>>
    = U extends [infer L, ...infer R]
        ? DistributeUnions<T[keyof T & L]> extends infer V
            ? V extends any ? Merge<{[P in keyof T & L]: V} & DistributeObject<T, R>>: never
        : never
    : {}

type DistributeArray<T> 
    = T extends [infer L, ...infer R]
        ? DistributeUnions<L> extends infer U
            ? U extends any ? [U, ...DistributeArray<R>] : never
        : never
    : []

type DistributeUnions<T> 
    = T extends any
        ? T extends any[] ? DistributeArray<T>
        : T extends object ? DistributeObject<T>
        : T
    : never

Solution by okayu29 #6309

TS Playground.

type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void
    ? I
    : never;

type UnionToTuple<T> = UnionToIntersection<T extends any ? (t: T) => T : never> extends (
    _: any,
) => infer W
    ? [...UnionToTuple<Exclude<T, W>>, W]
    : [];

type Tuple = readonly unknown[];
type Digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
type ToString<T extends number> = `${T}`;
type StringDigit = ToString<Digit>;
type StringNumber = `${StringDigit}${string}`;

/**
 * IsTuple<0[]> = false.
 * IsTuple<[0]> = true.
 */
type IsTuple<T> = T extends Tuple ? (number extends T['length'] ? false : true) : false;

/**
 * SetValue<[1, 2, 3], '1', 4 | 5> = [1, 4, 3] | [1, 5, 3].
 * SetValue<{ foo: 2 }, 'bar', 4 | 5> = { foo: 2, bar: 4} | { foo: 2, bar : 5 }.
 */
type SetValue<T, Key extends string, Value> = Value extends unknown
    ? IsTuple<T> extends true
        ? {
              [K in keyof T]: K extends Key ? Value : T[K];
          }
        : {
              [K in Key | keyof T]: K extends Key ? Value : K extends keyof T ? T[K] : never;
          }
    : never;

/**
 * Tail<[1, 2, 3]> = [2, 3].
 */
type Tail<T extends Tuple> = T extends readonly [unknown, ...infer Rest] ? Rest : [];

/**
 * KeysUnion<{ foo: 1, bar: 2 }> = "foo" | "bar".
 * KeysUnion<[3, 4, 5]>; = "0" | "1" | "2".
 */
type KeysUnion<T> = IsTuple<T> extends true ? StringNumber & keyof T : string & keyof T;

/**
 * Keys<[1 | 2]> = ["0"].
 */
type Keys<T, K = UnionToTuple<KeysUnion<T>>> = K extends ReadonlyArray<KeysUnion<T>> ? K : never;

type Reduce<T, K extends ReadonlyArray<string & keyof T>> = T extends unknown
    ? K extends []
        ? T
        : Reduce<SetValue<T, K[0], DistributeUnions<T[K[0]]>>, Tail<K>>
    : never;

type DistributeUnions<T> = T extends object ? Reduce<T, Keys<T>> : T;

Solution by uid11 #901