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 TOmit<T,K> removes all properties with keys in K from the object type TDict<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 }
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