typescriptgenericstypescript-genericsderived-types

Is there a utility type or something similar derive the name of a key from another?


This is a long shot but I would like to be able to generate a new interface from an existing one but with key names that are derived for the originals.

Below is an example of the kind of thing I would like to be able to do:

Given this source object:

interface Car {
  brand: Brand;
  model: string;
  year: number;
}

I would like to be able to declare an interface:

interface CarSetters {
  setBrand: (brand: Brand) => any;
  setModel: (model: string) => any;
  setYear: (year: number) => any;
}

but I don't want to have to declare it manually like above. Rather, I would like to be able to use it like:

type CarSetters = Setters<Car>

or

type CarState = Car & Setters<Car>

Solution

  • It looks to be a pretty simple mapping:

    type Brand = boolean;
    interface Car {
      brand: Brand;
      model: string;
      year: number;
    };
    type CarSetters = {
        [T in keyof Car as `set${Capitalize<T>}`]: (newValue: Car[T]) => any;
    };
    

    To keep symbols from causing issues, you can do:

    type CarSetters = {
        [T in keyof Car as T extends symbol ? never : `set${Capitalize<T>}`]: (newValue: Car[T]) => any;
    };
    

    or

    type CarSetters = {
        [T in keyof Car as T extends symbol ? never : `set${Capitalize<T>}`]: (newValue: Car[T]) => any;
    } & {
        [T in keyof Car as T extends symbol ? T : never]: Car[T];
    };