I have the following types
type A = {
foo: number
bar: number
}
type B = {
foo: string
bar: string
}
And I want to write a function f()
that takes a parameter that is completely or partially type A, and outputs an object with the same keys, but with types from type B.
For example,
f(a) = {foo: 1, bar: 2}
// should have return type {foo: string, bar: string}
f(a) = {foo: 1}
// should have return type {foo: string}
f(a) = {bar: 1}
// should have return type {bar: string}
I have tried to use generics, but since typescript doesn't check for extra fields, I get an error when I use keyof
:
function f<T extends A>(input: T): {[K in keyof T]: B[K]} {
// Type 'K' cannot be used to index type 'B' ^^^^^
// ... function implementation here
}
If this solution is to work, I'll have to somehow restrict T
to only being a subset of type A
, though I haven't found a way to do it that makes the compiler understand keyof T
can index A
.
The closest I have come is to using Partial
, but again that is not exactly the behavior I want:
function f(input: Partial<A>): Partial<B> {
// mock implementation, output does not matter, asking about types here
const res: Partial<B> = {};
let k: keyof typeof input;
for (k in input) {
const v = input[k];
res[k] = parseInt(v ?? "0");
}
return res;
}
This is incorrect because the following inputs all give outputs of the same type:
const a = f({})
const b = f({foo: 1})
const c = f({bar: 2})
// a, b, c all have type {foo?: number, bar?: number)
// should be:
// a: {}
// b: {foo: string}
// c: {bar: string}
Is what I'm asking for possible in Typescript right now? Any solutions/workarounds would be much appreciated.
CodePudding user response:
My suggestion here is to make the function f()
generic in the type of the keys K
of the input
parameter, and then you can represent the input
as Pick<A, K>
and the output type as Pick<B, K>
, using the Pick<T, K>
utility type:
declare function f<K extends keyof A>(input: Pick<A, K>): Pick<B, K>;
Or you can inline the definition of Pick
to get the following version:
declare function f<K extends keyof A>(input: { [P in K]: A[K] }): { [P in K]: B[K] };
Let's test it out:
const b0 = f({ foo: 1, bar: 2 });
/* const b0: {
foo: string;
bar: string;
} */
const b1 = f({ foo: 1 });
/* const b1: {
foo: string;
} */
const b2 = f({ bar: 1 });
/* const b2: {
bar: string;
} */
Looks good.