typescriptadvanced
JavaScript Proxy Handler Pattern
Use Proxy and Reflect to create observable objects, validation layers, and dynamic API clients.
typescriptPress ⌘/Ctrl + Shift + C to copy
// Observable object with change tracking
function createObservable<T extends object>(
target: T,
onChange: (prop: string, oldVal: unknown, newVal: unknown) => void,
): T {
return new Proxy(target, {
set(obj, prop, value) {
const old = Reflect.get(obj, prop);
const result = Reflect.set(obj, prop, value);
if (old !== value) onChange(String(prop), old, value);
return result;
},
});
}
// Validated object
function createValidated<T extends object>(
target: T,
validators: Partial<Record<keyof T, (v: unknown) => boolean>>,
): T {
return new Proxy(target, {
set(obj, prop, value) {
const validator = validators[prop as keyof T];
if (validator && !validator(value)) {
throw new TypeError(`Invalid value for ${String(prop)}: ${value}`);
}
return Reflect.set(obj, prop, value);
},
});
}
// Dynamic API client (generates methods from URL paths)
function createApiClient(baseUrl: string): Record<string, Function> {
return new Proxy({} as Record<string, Function>, {
get(_, prop) {
return async (params?: Record<string, string>) => {
const url = new URL(`/${String(prop)}`, baseUrl);
if (params) {
Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v));
}
console.log(`GET ${url.toString()}`);
// const res = await fetch(url); return res.json();
return { endpoint: String(prop), params };
};
},
});
}
// Usage
const user = createObservable({ name: 'Alice', age: 30 }, (prop, old, val) => {
console.log(`${prop}: ${old} -> ${val}`);
});
user.name = 'Bob'; // name: Alice -> Bob
user.age = 31; // age: 30 -> 31
const config = createValidated(
{ port: 3000, host: 'localhost' },
{ port: (v) => typeof v === 'number' && (v as number) > 0 && (v as number) < 65536 },
);
config.port = 8080; // OK
try { config.port = -1; } catch (e: any) { console.log(e.message); }
const api = createApiClient('https://api.example.com');
api.users({ page: '1' }).then(console.log);
api.products({ category: 'tools' }).then(console.log);Use Cases
- Observable state management
- Runtime input validation
- Dynamic REST client generation
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
typescriptintermediate
TypeScript Typed Event Emitter
Create type-safe event emitters in Node.js with full TypeScript support and autocomplete.
Best for: Type-safe pub/sub communication between modules
#nodejs#events
typescriptintermediate
Event-Driven Architecture Pattern
Build loosely coupled systems with typed event bus, async event handlers, and domain event patterns.
Best for: Microservice communication
#nodejs#events
typescriptintermediate
Middleware Chain Pattern
Implement the middleware/pipeline pattern for composable request processing without Express.
Best for: Custom HTTP framework
#nodejs#middleware
typescriptintermediate
Pub/Sub Messaging Pattern
Implement publish/subscribe messaging with topics, filtered subscriptions, and message replay.
Best for: In-process event messaging
#nodejs#pubsub