/**
 * @example type Output = ArrElement<['a', 'b', 'c']>;
 * // "a" | "b" | "c"
 */
export type ArrElement<ArrType> = ArrType extends readonly (infer ElementType)[]
  ? ElementType
  : never;

/** 
 * @example type Output = Split<'a.b1' | 'a.b2', '.'>;
 * // ["a", "b1"] | ["a", "b2"]
*/
type Split<S extends string, D extends string> =
  S extends `${infer A}${D}${infer B}` ? [A, ...Split<B, D>] : [S];

/** 
 * @example type Output = NestedObject<["a", "b1"] | ["a", "b2"]>;
 * // { a: { b1: string; }; } | { a: { b2: string; }; }
*/
type NestedObject<T extends string[]> =
  T extends [infer Head, ...infer Tail] ?
    Head extends string ?
      { [key in Head]: NestedObject<Tail extends string[] ? Tail : []> } : never :
    string;

/** 
 * @example type Output = UnionToIntersection<{ a: { b1: string; }; } | { a: { b2: string; }; }>;
 * // { a: { b1: string; }; } & { a: { b2: string; }; }
*/
type UnionToIntersection<U> =
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;

/** 
 * @example type Output = CreateTree<'a.b1' | 'a.b2', '.'>;
 * // { a: { b1: string; }; } & { a: { b2: string; }; }
*/
export type CreateTree<T> =
  UnionToIntersection<T extends infer U ?
    U extends string ?
      NestedObject<Split<U, '.'>> : never : never>;