dot-path-value
Version:
Safely get deep nested properties using dot notation
21 lines (20 loc) • 1.93 kB
TypeScript
export type Primitive = null | undefined | string | number | boolean | symbol | bigint;
type ArrayKey = number;
type IsTuple<T extends readonly any[]> = number extends T['length'] ? false : true;
type TupleKeys<T extends readonly any[]> = Exclude<keyof T, keyof any[]>;
export type PathConcat<TKey extends string | number, TValue> = TValue extends Primitive ? `${TKey}` : `${TKey}` | `${TKey}.${Path<TValue>}`;
export type Path<T> = T extends readonly (infer V)[] ? IsTuple<T> extends true ? {
[K in TupleKeys<T>]-?: PathConcat<K & string, T[K]>;
}[TupleKeys<T>] : PathConcat<ArrayKey, V> : {
[K in keyof T]-?: PathConcat<K & string, T[K]>;
}[keyof T];
type ArrayPathConcat<TKey extends string | number, TValue> = TValue extends Primitive ? never : TValue extends readonly (infer U)[] ? U extends Primitive ? never : `${TKey}` | `${TKey}.${ArrayPath<TValue>}` : `${TKey}.${ArrayPath<TValue>}`;
export type ArrayPath<T> = T extends readonly (infer V)[] ? IsTuple<T> extends true ? {
[K in TupleKeys<T>]-?: ArrayPathConcat<K & string, T[K]>;
}[TupleKeys<T>] : ArrayPathConcat<ArrayKey, V> : {
[K in keyof T]-?: ArrayPathConcat<K & string, T[K]>;
}[keyof T];
export type PathValue<T, TPath extends Path<T> | ArrayPath<T>> = T extends any ? TPath extends `${infer K}.${infer R}` ? K extends keyof T ? R extends Path<T[K]> ? undefined extends T[K] ? PathValue<T[K], R> | undefined : PathValue<T[K], R> : never : K extends `${ArrayKey}` ? T extends readonly (infer V)[] ? PathValue<V, R & Path<V>> : never : never : TPath extends keyof T ? T[TPath] : TPath extends `${ArrayKey}` ? T extends readonly (infer V)[] ? V : never : never : never;
export declare function getByPath<T extends Record<string, any>, TPath extends Path<T>>(obj: T, path: TPath): PathValue<T, TPath>;
export declare function setByPath<T extends Record<string, any>, TPath extends Path<T>>(obj: T, path: TPath, value: PathValue<T, TPath>): T;
export {};