// 整体思路:利用联合类型可以分发的特性,分别处理数组和对象,数组可以用 [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
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
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
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
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
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