Reputation: 403
Consider the following
type MergeFn = <K1 extends string, V1, K2 extends string, V2>(
k1: K1, v1: V1,
k2: K2, v2: V2
) => ???
let mergeFn: MergeFn // implementation not relevant to question
What would I have to fill in for ???
so that
mergeFn(
"hello", 1,
"typescript", 2
)
has type { hello: number, typescript: number }
.
I tried
??? = { [k in K1]: V1 } & { [k in K2]: V2 }
but the result will be
{ hello: number } & { typescript: number }
.
(Applying type Id<T> = { [k in keyof T]: T[k] }
as proposed here did not help either.)
Example in the typescript playground
Upvotes: 1
Views: 191
Reputation: 327964
One way you could do it is to use a single conditional mapped type instead of an intersection:
type MergeFn = <K1 extends string, V1, K2 extends string, V2>(
k1: K1, v1: V1,
k2: K2, v2: V2
) => { [K in K1 | K2]: K extends K1 ? V1 : V2}
which produces
const test = mergeFn(
"hi", 4,
"there", 4
)
/* const test: {
hi: number;
there: number;
} */
When you say the Id<T>
"didn't help" I assume you mean that the compiler decided to show you a type alias name like Id<A & B>
instead of expanding it out for you. I've found in such cases that the way to deal with it is to pass through an intermediate conditional type, like this:
type Expand<T> = T extends infer U ? { [K in keyof U]: U[K] } : never
If I use that on your original code:
type MergeFn = <K1 extends string, V1, K2 extends string, V2>(
k1: K1, v1: V1,
k2: K2, v2: V2
) => Expand<Record<K1, V1> & Record<K2, V2>>
I get the same output:
const test = mergeFn(
"hi", 4,
"there", 4
)
/* const test: {
hi: number;
there: number;
} */
So either way should work for you.
Okay, hope that helps; good luck!
Upvotes: 2