typescript

TypeScript: Is it possible to derive a type that lists the attribute names and types of another object type?


If I have a TypeScript type like

type O = { strKey: string; numKey: number; boolKey: boolean };

is it possible to define a type definition NameValue such that NameValue<O> would create the new type

type OAttrs =
 | { name: "strKey"; value: string }
 | { name: "numKey"; value: number }
 | { name: "boolKey"; value: boolean };

This could be useful when using Object.fromEntries to create an object of type O from a list of attributes, for example.

Two attempts that don't work.

This first attempt is not legal TypeScript.

// export type NameValue1<T> = { name: P extends keyof T, value: T[P] };
// Cannot find name 'P'. ts(2304)

The second attempt does not succeed in making the value type specific to the corresponding name.

export type NameValue2P<T, P extends keyof T> = { name: P; value: T[P] };
export type NameValue2<T> = NameValue2P<T, keyof T>;

export type ONameValue2 = NameValue2<O>;
// type ONameValue2 = {
//   name: keyof O;
//   value: string | number | boolean;
// }

Solution

  • You can use a mapped type which maps over the properties of O. Afterwards, you can index the mapped type with keyof O producing the desired union.

    type NameValue<O> = {
      [K in keyof O]: {
        name: K,
        value: O[K]
      }
    }[keyof O]
    
    type O = { strKey: string; numKey: number; boolKey: boolean };
    
    type OAttrs = NameValue<O>
    
    // type OAttrs = {
    //     name: "strKey";
    //     value: string;
    // } | {
    //     name: "numKey";
    //     value: number;
    // } | {
    //     name: "boolKey";
    //     value: boolean;
    // }
    

    Playground