Last active
October 23, 2025 18:48
-
-
Save SandeepK1729/6c08404c5b23ef4c2e4958b59dc17e14 to your computer and use it in GitHub Desktop.
property methods for nested objects with type safety
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Utility methods for nested object | |
| * read and write operations on objects. | |
| */ | |
| /** | |
| * Path - A recursive type to determine the path of nested properties in an object. | |
| * @template T - The object type to extract paths from. | |
| * @returns A union type of string paths representing the nested properties. | |
| * @example | |
| * type Example = { | |
| * a: { | |
| * b: { | |
| * c: number; | |
| * }; | |
| * d: string; | |
| * }; | |
| * e: boolean; | |
| * }; | |
| * | |
| * type ExamplePaths = Path<Example>; | |
| * // Result: "a" | "a.b" | "a.b.c" | "a.d" | "e" | |
| */ | |
| type Path<T extends Record<string, any>> = T extends Record< | |
| infer K extends string, | |
| infer V | |
| > | |
| ? V extends Record<string, any> | |
| ? K | `${K}.${Path<V>}` | |
| : K | |
| : never; | |
| /** | |
| * PathValue - A recursive type to determine the value type at a given path in an object. | |
| * @template T - The object type to extract values from. | |
| * @template P - The path type representing the nested properties. | |
| * @returns The type of the value located at the specified path. | |
| * @example | |
| * type Example = { | |
| * a: { | |
| * b: { | |
| * c: number; | |
| * }; | |
| * d: string; | |
| * }; | |
| * e: boolean; | |
| * }; | |
| * | |
| * type ValueAtPathC = PathValue<Example, "a.b.c">; // Result: number | |
| * type ValueAtPathD = PathValue<Example, "a.d">; // Result: string | |
| * type ValueAtPathE = PathValue<Example, "e">; // Result: boolean | |
| */ | |
| type PathValue< | |
| T extends Record<string, any>, | |
| P extends Path<T> | |
| > = P extends `${infer K}.${infer Rest}` | |
| ? K extends keyof T | |
| ? Rest extends Path<T[K]> | |
| ? PathValue<T[K], Rest> | |
| : never | |
| : never | |
| : P extends keyof T | |
| ? T[P] | |
| : never; | |
| /** | |
| * get - Retrieves the value at the specified path from the given object. | |
| * | |
| * @param obj an object to retrieve the value | |
| * @param path {@link Path} of the key | |
| * @returns value at the given path | |
| * @template T - The object type. | |
| * @template P - The path type representing the nested properties. | |
| * @example | |
| * const exampleObj = { | |
| * a: { | |
| * b: { | |
| * c: 42, | |
| * }, | |
| * d: "hello", | |
| * }, | |
| * e: true, | |
| * }; | |
| * | |
| * const valueC = get(exampleObj, "a.b.c"); // Result: 42 | |
| * const valueD = get(exampleObj, "a.d"); // Result: "hello" | |
| * const valueE = get(exampleObj, "e"); // Result: true | |
| */ | |
| const get = <T extends Record<string, any>, P extends Path<T>>( | |
| obj: T, | |
| path: P | |
| ): PathValue<T, P> => { | |
| return path.split(".").reduce((o: any, key: string) => o?.[key], obj); | |
| }; | |
| /** | |
| * set - Sets the value at the specified path in the given object. | |
| * @param obj an object to set the value | |
| * @param path {@link Path} of the key | |
| * @param value the value to set | |
| * @returns the updated object | |
| * @template T - The object type. | |
| * @template P - The path type representing the nested properties. | |
| * @example | |
| * const exampleObj = { | |
| * a: { | |
| * b: { | |
| * c: 42, | |
| * }, | |
| * d: "hello", | |
| * }, | |
| * e: true, | |
| * }; | |
| * | |
| * set(exampleObj, "a.b.c", 100); | |
| * // exampleObj is now: | |
| * // { | |
| * // a: { | |
| * // b: { | |
| * // c: 100, | |
| * // }, | |
| * // d: "hello", | |
| * // }, | |
| * // e: true, | |
| * // } | |
| */ | |
| const set = <T extends Record<string, any>, P extends Path<T>>( | |
| obj: T, | |
| path: P, | |
| value: PathValue<T, P> | |
| ): T => { | |
| const keys = path.split("."); | |
| const lastKey = keys.pop(); | |
| if (!lastKey) { | |
| return obj; | |
| } | |
| const target = keys.reduce( | |
| (o: any, key: string) => (o[key] = o[key] ?? {}), | |
| obj | |
| ); | |
| target[lastKey] = value; | |
| return obj; | |
| }; | |
| export type { Path, PathValue }; | |
| export { get, set }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment