export type Primitive = null | undefined | string | number | boolean | symbol | bigint; type ArrayKey = number; type IsTuple = number extends T['length'] ? false : true; type TupleKeys = Exclude; export type PathConcat = TValue extends Primitive ? `${TKey}` : `${TKey}` | `${TKey}.${Path}`; export type Path = T extends readonly (infer V)[] ? IsTuple extends true ? { [K in TupleKeys]-?: PathConcat; }[TupleKeys] : PathConcat : { [K in keyof T]-?: PathConcat; }[keyof T]; type ArrayPathConcat = TValue extends Primitive ? never : TValue extends readonly (infer U)[] ? U extends Primitive ? never : `${TKey}` | `${TKey}.${ArrayPath}` : `${TKey}.${ArrayPath}`; export type ArrayPath = T extends readonly (infer V)[] ? IsTuple extends true ? { [K in TupleKeys]-?: ArrayPathConcat; }[TupleKeys] : ArrayPathConcat : { [K in keyof T]-?: ArrayPathConcat; }[keyof T]; export type PathValue | ArrayPath> = T extends any ? TPath extends `${infer K}.${infer R}` ? K extends keyof T ? R extends Path ? undefined extends T[K] ? PathValue | undefined : PathValue : never : K extends `${ArrayKey}` ? T extends readonly (infer V)[] ? PathValue> : never : never : TPath extends keyof T ? T[TPath] : TPath extends `${ArrayKey}` ? T extends readonly (infer V)[] ? V : never : never : never; const UNSAFE_PATH_SEGMENTS = new Set(['__proto__', 'constructor', 'prototype']); function assertNoUnsafePathSegments(path: string): void { for (const segment of path.split('.')) { if (UNSAFE_PATH_SEGMENTS.has(segment)) { throw new TypeError(`Path segment "${segment}" is not allowed (prototype pollution risk)`); } } } export function getByPath, TPath extends Path>( obj: T, path: TPath, ): PathValue { assertNoUnsafePathSegments(path); return path.split('.').reduce((acc, key) => acc?.[key], obj) as PathValue; } export function setByPath, TPath extends Path>( obj: T, path: TPath, value: PathValue, ) { assertNoUnsafePathSegments(path); const segments = path.split('.') as TPath[]; const lastKey = segments.pop(); let target: T = obj; for (let i = 0; i < segments.length; i++) { const key = segments[i] as TPath; if (!(key in target)) { target[key] = {} as PathValue; } target = target[key]; } if (lastKey) { target[lastKey] = value; } return obj; }