Skip to content

Instantly share code, notes, and snippets.

@nickhudkins
Last active March 14, 2025 21:11
Show Gist options
  • Save nickhudkins/0ede21f00d92f88736de4457e0a279f6 to your computer and use it in GitHub Desktop.
Save nickhudkins/0ede21f00d92f88736de4457e0a279f6 to your computer and use it in GitHub Desktop.
TypeSafe Path Proxy
// Make all properties in T deeply non-nullable recursively
type DeepNonNullable<T> = T extends object
? { [K in keyof T]-?: DeepNonNullable<NonNullable<T[K]>> }
: NonNullable<T>;
/**
* Creates a proxy that builds a dotted path string based on property access
* while restricting property access to only those defined in the generic type T.
*
* @param path Current accumulated path
* @returns A proxy object that tracks property access as a dotted path
*/
/**
* Creates a proxy that builds a dotted path string based on property access
* while restricting property access to only those defined in the generic type T.
* All nullable properties are treated as non-nullable to ensure safe path generation.
*
* @param path Current accumulated path
* @returns A proxy object that tracks property access as a dotted path but appears as type T
*/
export function createPathProxy<T extends object>(path: string = ''): DeepNonNullable<T> {
// List of properties to exclude from path generation
const excludedProps = new Set([
'call',
'apply',
'bind',
'name',
'length',
'arguments',
'caller',
'constructor',
'prototype',
'__proto__',
'__defineGetter__',
'__defineSetter__',
'__lookupGetter__',
'__lookupSetter__'
]);
const handler: ProxyHandler<any> = {
get(_, prop) {
// Handle special cases
if (typeof prop === 'symbol') {
if (prop === Symbol.toPrimitive) {
return () => path;
}
return undefined;
}
// Handle toString specially
if (prop === 'toString') {
return () => path;
}
// Skip excluded properties
if (excludedProps.has(prop as string)) {
return undefined;
}
// Handle numeric indexing for arrays
if (typeof prop === 'string' && !isNaN(Number(prop))) {
const index = Number(prop);
const newPath = `${path}[${index}]`;
return createPathProxy<any>(newPath);
}
// Build the new path by appending the property
const propStr = String(prop);
const newPath = path ? `${path}.${propStr}` : propStr;
// Return a new proxy with the updated path
return createPathProxy<any>(newPath);
},
apply() {
// When called as a function, return the path
return path;
}
};
// Use a function as the target so it can be both called and have properties accessed
const proxyTarget = function () { return path; };
return new Proxy(proxyTarget, handler) as unknown as DeepNonNullable<T>;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment