export type PropertyPath = string | number;
export type PropertyParts = Array<PropertyPath>;
export interface ObjectValue {
    [key: PropertyPath]: unknown;
}
export interface ObjectValueDeep {
    [key: PropertyPath]: unknown | ObjectValueDeep;
}
/**
 * Extracts all possible paths in an object, including array indices
 * For example, ExtractPaths<{items: string[], x: {y: number}}, '.'> => "items" | "items.0" | "items.1" | ... | "x" | "x.y"
 * For example, ExtractPaths<{items: string[], x: {y: number}}, '/'> => "items" | "items/0" | "items/1" | ... | "x" | "x/y"
 */
export type ExtractPaths<Obj, Sep extends string> = {
    [Name in keyof Obj & string]: Name | (NonNullable<Obj[Name]> extends ObjectValueDeep ? `${Name}${Sep}${ExtractPaths<NonNullable<Obj[Name]>, Sep>}` : NonNullable<Obj[Name]> extends Array<infer E> ? `${Name}${Sep}${number}` | (E extends ObjectValueDeep ? `${Name}${Sep}${number}${Sep}${ExtractPaths<E, Sep>}` : never) : never);
}[keyof Obj & string];
export type ExtractPathsStarted<Obj, Sep extends string> = {
    [Name in keyof Obj & string]: `${Sep}${Name}` | (NonNullable<Obj[Name]> extends ObjectValueDeep ? `${Sep}${Name}${Sep}${ExtractPaths<NonNullable<Obj[Name]>, Sep>}` : NonNullable<Obj[Name]> extends Array<infer E> ? `${Sep}${Name}${Sep}${number}` | (E extends ObjectValueDeep ? `${Sep}${Name}${Sep}${number}${Sep}${ExtractPaths<E, Sep>}` : never) : never);
}[keyof Obj & string];
export type ExtractPathsAny<Obj, Sep extends string> = ExtractPaths<Obj, Sep> | ExtractPathsStarted<Obj, Sep>;
/**
 * Extracts paths only to leaf properties of an object (array elements are not considered).
 * For example, ExtractPathsLeaf<{a: number, b: {c: string, d: {e: boolean}}, f: string[]}, '.'> => "a" | "b.c" | "b.d.e" | "f"
 */
export type ExtractPathsLeaf<Obj, Sep extends string> = {
    [Name in keyof Obj & string]: NonNullable<Obj[Name]> extends ObjectValueDeep ? NonNullable<Obj[Name]> extends Record<PropertyPath, never> ? Name : `${Name}${Sep}${ExtractPathsLeaf<NonNullable<Obj[Name]>, Sep>}` : Name;
}[keyof Obj & string];
/**
 * Extracts paths with asterisks for operations that clear all properties or elements.
 * For example, ExtractPathsAsterisk<{a: number, b: {c: string}, d: number[]}, '.'> => "a" | "a.*" | "b" | "b.*" | "b.c" | "b.c.*" | "d" | "d.*" | "d.0" | "*"
 * The asterisk can be used at the root level to clear all properties, or after a property name to clear all elements of an array or set a non-object, non-array property to undefined.
 */
export type ExtractPathsAsterisk<Obj, Sep extends string> = {
    [Name in keyof Obj & string]: Name | `${Name}${Sep}*` | (NonNullable<Obj[Name]> extends ObjectValueDeep ? `${Name}${Sep}${ExtractPathsAsterisk<NonNullable<Obj[Name]>, Sep>}` : NonNullable<Obj[Name]> extends Array<infer E> ? `${Name}${Sep}${number}` | (E extends ObjectValueDeep ? `${Name}${Sep}${number}${Sep}${ExtractPathsAsterisk<E, Sep>}` : never) : never);
}[keyof Obj & string] | '*';
/**
 * Extracts the value type for a specific path, including objects, arrays, and their elements
 * For example, `PathToType<{items: string[]}, 'items.0'>` => `string`
 * For example, `PathToType<{items: {prop: number}}, 'items.prop'>` => `number'
 * For example, `PathToType<{items: string[]}, ''>` => `{items: string[]}`
 */
export type PathToType<T, P extends string, Sep extends string> = P extends '' ? T : P extends keyof T ? T[P] : P extends `${Sep}${infer Rest}` ? PathToType<T, Rest, Sep> : P extends `${infer Key}${Sep}${infer Rest}` ? Key extends keyof T ? T[Key] extends Array<infer E> | undefined ? Rest extends `${infer Index extends number}` ? E : Rest extends `${infer Index extends number}${Sep}${infer SubPath}` ? E extends ObjectValueDeep ? PathToType<E, SubPath, Sep> : undefined : undefined : T[Key] extends ObjectValueDeep | undefined ? PathToType<NonNullable<T[Key]>, Rest, Sep> : undefined : undefined : undefined;
