1
export type Foo = {
  a: boolean
  b: number
}

I'd like to create another type based on Foo, but add a prefix to its fields names:

export type Foo2 = {
  bar_a: boolean
  bar_b: number
}

is it possible?

wong2
  • 34,358
  • 48
  • 134
  • 179
  • 3
    If you can explicitly list all members, yes. If you want it to automatically prefix all members without listing them, currently no. – Ingo Bürk Jun 11 '19 at 07:41

1 Answers1

4

As of TypeScript 3.5 you cannot do this programmatically. There is an open issue in GitHub that suggests being able to augment keys in mapped types. If you care about this a lot, you might want to head over there and give it a or describe your use case if you think it's more compelling than what's already in here.


In the meantime the only thing you can do is maintain such a mapping yourself manually. There are lots of possible ways to implement a mapped type that transforms keys, and lots of caveats about where this goes wrong. Mapped types are designed to transform property values, not keys, and so the tools TypeScript gives us are not perfectly suited to the task. Still, here's one way to do it:

// MapKeys<T, M> takes an object type T and a key map M  and produces 
// a new type whose keys are mapped from M (or left alone if they are not in  M)
//
// Thus MapKeys<{a: 0, b: 1}, {a: "A", z: "Z"}> becomes {A: 0, b: 1}
//
// The following is one of many possible implementations. 
// It does not properly deal well with optional properties, e.g., {a?: number}
// and types with index-signature properties, e.g., {[k: string]: number}
// (these can be worked around with even more complexity)
type MapKeys<T extends object, M extends Record<keyof M, keyof any>> = {
  [K in keyof T]-?: (a: { [P in K extends keyof M ? M[K] : K]: T[K] }) => void
}[keyof T] extends (a: infer U) => void
  ? { [K in keyof U]: U[K] }
  : never;

And let's see it applied to your type:

export type Foo = {
  a: boolean;
  b: number;
};

type KeyMap = {
  a: "bar_a";
  b: "bar_b";
  c: "bar_c";
  d: "bad_d";
  // etc
};

export type Foo2 = MapKeys<Foo, KeyMap>;
// type Foo2 = {
//    bar_a: boolean;
//    bar_b: number;
// }

Looks good. Okay, hope that helps. Good luck!

Link to code

jcalz
  • 264,269
  • 27
  • 359
  • 360