Search code examples
typescripttype-aliasmapped-types

Proxy with mapped types


I walk through TS Handbook and I came to the mapped types. There is a code snippet to wrap object property into proxy.

type Proxy<T> = {
    get(): T;
    set(value: T): void;
}
type Proxify<T> = {
    [P in keyof T]: Proxy<T[P]>;
}
function proxify<T>(o: T): Proxify<T> {
   // ... wrap proxies ...
}
let proxyProps = proxify(props);

I'm trying to fill the gap in the proxify function with my implementation of it, and I get something like this:

function proxify<T>(t: T): Proxify<T> {
    let result = <Proxify<T>>{};
    for (const k in t) {
      result[k] = {    //(*) from that moment I lose strong typing  
        get: () => t[k],
        set: (value) => t[k] = value
      }
    }
    return result;
  }

I can't control types inside the loop and everything must by of any type there. How to cope with that, assuming that my implementation is correct at all?


Solution

  • The implementation seems ok. While creating the object there is some safety lost but you don't loose it all. The amount of typing you still get might actually surprise you

    function proxify<T>(t: T): Proxify<T> {
        let result = <Proxify<T>>{};
        for (const k in t) { // k is of type Extract<keyof T, string> so it must be a key of T
            // result[k] and t[k] both work because k is a key of both T and Proxify<T> but result['random'] would be invalid
            result[k] = { // get/set fields are checked, so _get would be an error
                // the return of get must be T[Extract<keyof T, string>] so ()=> 0 would be an error
                get: () => t[k],
                // value and t[k] must be T[Extract<keyof T, string>] so t[k] = '' would also be an error
                set: (value) => t[k] = value
            }
        }
        return result;
    }