type Primitive = string | number | symbol; type GenericObject = Record; type Join = L extends string | number ? R extends string | number ? `${L}.${R}` : L : R extends string | number ? R : undefined; type Union = L extends undefined ? R extends undefined ? undefined : R : R extends undefined ? L : L | R; /** * NestedPaths * Get all the possible paths of an object * @example * type Keys = NestedPaths<{ a: { b: { c: string } }> * // 'a' | 'a.b' | 'a.b.c' */ type NestedPaths = { [K in keyof T]: T[K] extends GenericObject ? NestedPaths, Join> : Union, Join>; }[keyof T]; /** * TypeFromPath * Get the type of the element specified by the path * @example * type TypeOfAB = TypeFromPath<{ a: { b: { c: string } }, 'a.b'> * // { c: string } */ type TypeFromPath = { [K in Path]: K extends keyof T ? T[K] : K extends `${infer P}.${infer S}` ? T[P] extends GenericObject ? TypeFromPath : never : never; }[Path]; type CaseInsensitive = T | Uppercase | Lowercase; type EmptyObject = Record; export type { CaseInsensitive, EmptyObject, NestedPaths, TypeFromPath };