Search code examples
typescriptmapped-types

How to convert interface to mapped type in Typescript


Background

In the typescript documentation for mapped types the following example is given:

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);

It is unclear to me how I would write the proxify function.

What I need it for

I have the following types

interface User extends BaseRecord {
    readonly 'id': number;
    readonly 'name'?: string;
}

interface FormField<T> {
    readonly value: T;
    readonly edited: boolean;
}

type WrappedInFormField<T> = {
    [P in keyof T]: FormField<T[P]>;
};

And I need to write a function with the following signature

const wrap = <T>(o: T): WrappedInFormField<T> => {
    // ...What goes here?...
}

wrappedUser: WrappedInFormField<User> = wrap<User>(UserIJustGotFromApi);

How can I do that?


Solution

  • You need to just build up the object. Typescript will offer no help in creating mapped types, you just build them up as you normally would in Javascript.

    const wrap = <T>(o: T): WrappedInFormField<T> => {
        // Create an empty object that we will add the properties to, assert it will be a WrappedInFormField<T> once we are done with it
        let result = {} as WrappedInFormField<T>;
        // Get all the keys of the original object
        for(var key in Object.keys(o)) { 
            // Create something compatible with FormField
            // You could instantiate a class, but you will not have access to the type of each property,
            // you could use any instead (for example if FormField<T> is a class you could just call new FormField<any> since types are erased it will not really matter)
            result[key] = {
                value: o[key],
                edited: false
            };
        }
        // return the result
        return  result;
    }