I'm new to TypeScript and I'm trying to write a generic function which returns the intersection of two types.
I don't I'm quite understanding record types and their intersection very well so I'm probably doing something wrong but here goes...
Let's say we have some types like these:
type A = { foo: string }
var a: A = { foo: 'foo' }
type B = { [key: string]: string }
var b: B = a
interface C { foo: string }
var c: C = a
type D = { bar: number }
var d: D = { bar: 123 }
type E = { [key: string]: number }
var e: E = d
interface F { bar: number }
var f: F = d
type G = A & D
var g: G = { ...a, ...d }
g = { ...c, ...f }
type H = A & D
var h: H = { ...a, ...d }
h = { ...c, ...f }
type I = B & E
var i: I = { ...b, ...e } // Error!... >_<
i = { ...a, ...d } // Error!
i = { ...c, ...f } // Error!
type I2 = { [key: string]: string & number }
var i2: I2 = {} // Can only be empty
i = i2
type J = C & F
var j: J = { ...c, ...f }
j = { ...a, ...d }
type K = { [key: string]: string | number }
var k: K = { ...a, ...d }
k = { ...b, ...e }
k = { ...c, ...f }
Now the problem I was actually trying to solve was this:
const func1 = <T, U>(t: T, u: U, f: (t: T, u: U) => T & U): T & U => {
return f(t, u)
}
h = func1(a, d, (t, u) => ({ ...t, ...u }))
i = func1(b, e, (t, u) => ({ ...t, ...u })) // Error
j = func1(c, f, (t, u) => ({ ...t, ...u }))
And the workaround that I came up with:
type RecordIntersection<T, U> = { [C in (keyof T | keyof U)]: T[keyof T] | U[keyof U] }
const func2 = <T, U>(t: T, u: U, f: (t: T, u: U) => RecordIntersection<T, U>): RecordIntersection<T, U> => {
return f(t, u)
}
h = func2(a, d, (t, u) => ({ ...t, ...u })) // Error
k = func2(b, e, (t, u) => ({ ...t, ...u }))
j = func2(c, f, (t, u) => ({ ...t, ...u })) // Error
This works for my particular use case but I only get type K
back for everything.
Playing around some more and I can get this to work:
type Intersection<T, U> = keyof T extends keyof U ? RecordIntersection<T, U> : T & U
const func3 = <T, U>(t: T, u: U, f: (t: T, u: U) => Intersection<T, U>): Intersection<T, U> => {
return f(t, u)
}
h = func3(a, d, (t, u) => ({ ...t, ...u }))
k = func3(b, e, (t, u) => ({ ...t, ...u }))
j = func3(c, f, (t, u) => ({ ...t, ...u }))
I can't seem to do much with a RecordIntersection<T, U>
as I can't work out how to get either a T
or U
back out. See How to use conditional types to filter unions in Typescript.
Is there a better way to do this?