I'm trying to write a TypeScript function that takes an input object and returns a result object with properties derived from the input.
This is what I have so far: (this function should just create a new object with renamed keys)
type t1 = {
a: number,
b: number,
c: number
}
type t2 = {
i: number,
j: number,
k: number
}
function translate(input: Partial<t1>): Partial<t2> {
const result: Partial<t2> = {};
if("a" in input) result["i"] = input["a"];
if("b" in input) result["j"] = input["b"];
if("c" in input) result["k"] = input["c"];
return result;
}
const result = translate({
a: 1, b: 2
});
console.log(result); // logs { i: 1, j: 2 }
this works, but I want my result to be strongly typed based on the input object. For example, if the input object is { a: 1 }, the function should return an object of type { i: number }. If the input object is { a: 1, b: 2 }, the function should return an object of type { i: number, j: number } etc.
Update:
This is what I have now based on @user137794 's answer:
type t1 = {
a: number,
b: number,
c: number
}
type t2 = {
i: number,
j: number,
k: number
}
/** this defines how field names are translated */
const renameMap = { a: 'i', b: 'j', c: 'k' } as const;
type RenameKeys<T, KeyMap extends Record<string, string>> = {
[K in keyof T as K extends keyof KeyMap ? KeyMap[K] : never ]: T[K]
}
function translate<T extends Partial<t1>> (input: T): RenameKeys<T, typeof renameMap> {
const result: Partial<t2> = {};
for(const key of Object.keys(renameMap) as (keyof typeof renameMap)[])
if (key in input) result[renameMap[key]] = input[key];
return result as RenameKeys<T, typeof renameMap>;
}
const result = translate({
a: 1, b: 2
});
console.log(result); // logs { i: 1, j: 2 }
This function returns the a strongly typed object based on the input. It only works for simple 1:1 key renaming however. What should I do for more complex functions, where multiple fields from input object are mapped to a single output field?
It would be ideal if I could define the maping logic something like this:
const translationMap = {
i: (sourceObject: t1) => sourceObject.a,
j: (sourceObject: t1) => sourceObject.a + sourceObject.b,
k: (sourceObject: t1) => sourceObject.b || sourceObject.c,
}
and have TypeScript infer the type of each property in the result automatically. If that's not possible, any other workarounds are welcome
To answer my own question:
This function translates an object into another object and returns a strongly typed result. It works well with complex translation logic
(based on this answer by @Matt McCutchen)