Changing a Property Name in Typescript Display Type

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

SomeData { prop1: string; prop2: number; } 

And I need to finish 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 created by the [P in keyof T] bit? I tried things like [(P in keyof T) + "Change"] without success.

+5
source share
1 answer

You can do what you want, but not automatically. The big blocker is that there is currently no type operator that allows you to add string literals at the type level, so you cannot even describe the conversion you are doing:

 // without Append<A extends string, B extends string>, you can't type this: function appendChange<T extends string>(originalKey: T): Append<T,'Change'> { return originalKey+'Change'; } 

This means that you need to actually hard code the specific mapping you are looking for, from a string literal to a string literal. In fact, the only way to get this to work is to specify a reverse mapping:

 type SomeMoreDataMapping = { prop1Change: 'prop1'; prop2Change: 'prop2'; } 

Armed with reverse mapping, you can define them:

 type Diff<T extends string, U extends string> = ({ [P in T]: P } & { [P in U]: never } & { [x: string]: never })[T]; type Omit<T, K extends keyof T> = {[P in Diff<keyof T, K>]: T[P]}; type Dict<T> = { [k: string]: T }; type MapKeys<T extends Dict<any>, M extends Dict<string>> = { [K in keyof M]: T[M[K]]; } & Omit<T, M[keyof M]>; 

(Some of them require TypeScript v2.4 and higher. If you are using an earlier version of TypeScript, see this issue for ways to implement Diff and Omit )

Short walkthrough:

  • Diff<T,U> acts on the union of types of string literals, removing them from U from those that are in T
  • Omit<T,K> removes all properties with keys in K from the object type T
  • Dict<T> is just an object with string keys whose properties are all of type T
  • and finally MapKeys<T,M> is what you need: it takes an object T and converts the keys according to the reverse mapping to M If the key in T not in M , the key is not converted. If the key in M absent in T , it will be present at the output with type any .

Now you can (finally) do this:

 type SomeMoreData= MapKeys<SomeData, SomeMoreDataMapping>; 

And if you check SomeMoreData , you will see that it has the correct type:

 var someMoreData: SomeMoreData = { prop1Change: 'Mystery Science Theater', prop2Change: 3000 } // type checks 

This should allow you to do some fun things, for example:

 function makeTheChange<T>(input: T): MapKeys<T, SomeMoreDataMapping> { var ret = {} as MapKeys<T, SomeMoreDataMapping>; for (var k in input) { // lots of any needed here; hard to convince the type system you're doing the right thing var nk: keyof typeof ret = <any>((k === 'prop1') ? 'prop1Change' : (k === 'prop2') ? 'prop2Change' : k); ret[nk] = <any>input[k]; } return ret; } var changed = makeTheChange({ prop1: 'Gypsy', prop2: 'Tom', prop3: 'Crow' }); console.log(changed.prop1Change.charAt(0)); //ok console.log(changed.prop2Change.charAt(0)); //ok console.log(changed.prop3.charAt(0)); //ok 

Hope this helps. Good luck

+3
source

Source: https://habr.com/ru/post/1268461/


All Articles