Changing Property Name in Typescript Mapped Type

Viewed 10456

I have a collection of Typescript objects that look like this:

SomeData {
  prop1: string;
  prop2: number;
}

And I need to end up with some objects that look like this:

type SomeMoreData= {
  prop1Change: string;
  prop2Change: number;
}

I know that if I just wanted to change the type, I could do this:

type SomeMoreData<T> = {
  [P in keyof T]: boolean;
}

Which would give me:

SomeMoreData<SomeData> {
  prop1: boolean;
  prop2: boolean;
}

Is there a way to manipulate the name produced by the [P in keyof T] bit? I've tried things like [(P in keyof T) + "Change"] with no success.

2 Answers

Key remapping in Mapped Types was introduced in typescript 4.1:

type SomeData = {
    prop1: string;
    prop2: number;
}

type WithChange<T> = { [P in keyof T & string as `${P}Change`]: T[P] };

type SomeMoreData = WithChange<SomeData>; // {  prop1Change: string, prop2Change: number }

Playground


In above example we use two new features: as clause in mapped types + template literal type.

Template string types are the type space equivalent of template string expressions. Similar to template string expressions, template string types are enclosed in backtick delimiters and can contain placeholders of the form ${T}, where T is a type that is assignable to string, number, boolean, or bigint. Template string types provide the ability to concatenate literal strings, convert literals of non-string primitive types to their string representation, and change the capitalization or casing of string literals. Furthermore, through type inference, template string types provide a simple form of string pattern matching and decomposition.

Some examples:

type EventName<T extends string> = `${T}Changed`;
type Concat<S1 extends string, S2 extends string> = `${S1}${S2}`;
type T0 = EventName<'foo'>;  // 'fooChanged'
type T1 = EventName<'foo' | 'bar' | 'baz'>;  // 'fooChanged' | 'barChanged' | 'bazChanged'
type T2 = Concat<'Hello', 'World'>;  // 'HelloWorld'
type T3 = `${'top' | 'bottom'}-${'left' | 'right'}`;  // 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'

Additionally mapped types support an optional as clause through which a transformation of the generated property names can be specified:

{ [P in K as N]: X }

where N must be a type that is assignable to string.

Related