I wanted to create a short documentation and explain the principle of Angular Signals using JavaScript proxies in an example. Then the question came up: could I quickly write it in TypeScript and set up the types? I said, of course, no problem, but then, surprise—somehow the types didn't fit anymore.
How can I use the correct types e.g. in:
target[property as keyof StateType] = value as StateType[keyof StateType];
outputs an error:
Type 'string | number' is not assignable to type 'never'.
Type 'string' is not assignable to type 'never'.(2322)
This is the whole example (Online TS Playground):
type StateType = {
count: number;
name: string;
};
const state: StateType = {
count: 0,
name: 'React'
};
function onStateChange<K extends keyof StateType>(property: K, value: StateType[K]): void {
console.log(`The property "${property}" was changed to "${value}"`);
}
const handler: ProxyHandler<StateType> = {
set(target, property, value) {
if (property in target) {
target[property as keyof StateType] = value as StateType[keyof StateType];
onStateChange(property as keyof StateType, value as StateType[keyof StateType]);
return true;
}
return false;
}
};
const proxyState = new Proxy(state, handler);
proxyState.count = 1;
proxyState.name = 'Angular';
Okay, I've got it. I would have needed to spend another 15 minutes on it. So that others don’t run into the same problem, here is the solution (1st Attempt):
type StateType = {
count: number;
name: string;
};
const state: StateType = {
count: 0,
name: 'React'
};
function onStateChange<K extends keyof StateType>(property: K, value: StateType[K]): void {
console.log(`The property "${property}" was changed to "${value}"`);
}
const handler: ProxyHandler<StateType> = {
set<K extends keyof StateType>(
target: StateType,
property: K,
value: StateType[K]
): boolean {
if (property in target) {
target[property] = value;
onStateChange(property, value);
return true;
}
return false;
}
};
const proxyState = new Proxy(state, handler);
proxyState.count = 1;
proxyState.name = 'Angular';
Short Explanation:
Generics in set
Method ensures that prop and value both are correct typed due to StateType.
Edit & Update (2nd attempt, simplified, Online TS Playground):
type StateType = {
count: number;
name: string;
};
const state: StateType = {
count: 0,
name: 'React'
};
function onStateChange<K extends keyof StateType>(property: K, value: StateType[K]): void {
console.log(`The property "${property}" was changed to "${value}"`);
}
const handler: ProxyHandler<StateType> = {
set(
target: StateType,
property: string | symbol,
value: any
): boolean {
if (property in target) {
const key = property as keyof StateType;
(target[key] as any) = value;
onStateChange(key, value as StateType[typeof key]);
return true;
}
return false;
}
};
const proxyState = new Proxy(state, handler);
proxyState.count = 1;
proxyState.name = 'Angular';