打字稿递归类型交集

时间:2019-10-10 19:59:42

标签: typescript conditional-types

我试图为循环交叉口定义类型定义,以实现这种行为:

type merged = Merged<[{a: string}, {b: string}, ...]>

成为{a: string} & {b: string} & ...

我定义了一些类型实用程序,例如HeadTail,以便能够对可变数量的输入类型进行操作,并设法定义了递归并集,但是对于交集却无法实现相同的目的:(< / p>

type Head<T extends any[]> = T[0]
type Tail<T extends any[]> =
((...args: T) => any) extends ((_arg0: any, ...rest: infer R) => any) ? R : never

type United<T extends any[]> = {
  0: Head<T>
  1: United<Tail<T>>
}[ T extends ([] | [any])
  ? 0
  : 0 | 1
]

type Merged<T extends any[]> = {
  0: Head<T>
  1: Merged<Tail<T>>
}[T extends ([] | [any])
  ? 0
  : 0 & 1
]

type a = {a: string}
type b = {b: string}
type c = {c: string}

type head = Head<[a, b, c, d]>       // {a: string} OK
type tail = Tail<[a, b, c, d]>       // [b, c]      OK
type united = United<[a, b, c, d]>   // a | b | c   OK
type merged = Merged<[a, b, c, d]>   // unknown     Not good

我肯定在TypeScript的某些基本内部组件上失败了,但不知道为什么在联合的情况下它可以工作而在相交的情况下却不能工作

我该如何解决?

1 个答案:

答案 0 :(得分:2)

我不会在这里尝试任何递归操作,特别是因为这种递归索引是not supported

此外,键类型的交集几乎总是会折叠成类似never的东西,因此会得到neverunknown或其他不受欢迎的东西。也就是说,索引访问不一定会在交叉路口之间分布:

type OOPS = { a: a, b: b }["a" & "b"] // never! not a & b

相反,我将使用here提供的解决方案进行合并并将其转换为交集:

type Merged<A extends readonly any[]> =
    A[number] extends infer U ?
    (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ?
    I : never : never;


type a = { a: string }
type b = { b: string }
type c = { c: string }
type d = { d: string }
type merged = Merged<[a, b, c, d]>   // a & b & c & d

那应该对您有用。希望能帮助到你;祝你好运!

Link to code