typescript - 如何使用递归条件类型将类型(任意长度)的元组映射到嵌套类型

标签 typescript

在我对 Can Typescript Interfaces express co-occurrence constraints for properties 的回答中我生成了以下代码:

type None<T> = {[K in keyof T]?: never}
type EitherOrBoth<T1, T2> = T1 & None<T2> | T2 & None<T1> | T1 & T2

type CombinationOf<T> = T extends [infer U1, infer U2] ? EitherOrBoth<U1, U2> :
                        T extends [infer U1, infer U2, infer U3] ? EitherOrBoth<U1, EitherOrBoth<U2, U3>> :
                        T extends [infer U1, infer U2, infer U3, infer U4] ? EitherOrBoth<U1, EitherOrBoth<U2, EitherOrBoth<U3, U4>>> :
                        never;

type Monolith = CombinationOf<[Data1, Data2, Data3]>

目前,CombinationOf 处理类型参数的元组,最多四个元素。 虽然这可以简单地扩展,但我想知道随着 recursive conditional types 的出现是否可以,类型CombinationOf可以用更优雅的方式表达。

因此,我的问题是:

  • 我可以使用递归条件类型将类型元组(任意长度)映射到嵌套类型吗?
  • 如果是这样,递归是否有深度限制? (希望这只是理论上的问题)
  • 我如何向自己证明使用递归条件类型生成的新类型与我现有的代码等效?

Playground link

最佳答案

在 TypeScript 4.1 中引入递归条件类型时,您应该能够定义 CombinationOf<T>像这样:

type CombinationOf<T> =
  T extends [infer U1, ...infer R] ? (
    R extends [] ? never : EitherOrBoth<U1,
      R extends [infer U2] ? U2 : CombinationOf<R>
    >
  ) : never;

在这里,我尝试尽可能接近您的原始定义,而不用担心 EitherOrBoth<> 的内容。 type 应该是真正在做的事情。请注意,因为您的原始定义返回 neverT是一个单元素元组,这里的定义稍微复杂一些。 (我有点期望 CombinationOf<[X]> 只是 X 。但是🤷‍♂️)

如果T有 0 或 1 个元素,您将得到 never 。如果有两个元素,您将得到 EitherOrBoth<U1, U2>哪里U1U2T 的前两个元素。否则,您会得到 EitherOrBoth<U1, CombinationOf<R>>哪里R是元组的其余部分/尾部 T之后U1 .


是的,有深度​​限制,而且不是很深;大约 10 个级别:

type TestDepth = CombinationOf<[1, 2, 3, 4, 5, 6, 7, 8, 9]> // works for me
type TestDepthBad = CombinationOf<[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]> // error, too deep

你或许可以重写CombinationOf看起来更像你的原始版本,它切割了元组 T分为 3 或 4 组而不是 1 组,并增加深度限制(也许增加到 30 或 40...编辑:玩这个我似乎无法让它超过 25 左右)费用更加复杂CombinationOf 。但不确定这是否值得。


证明类型相同可以通过检查此 CombinationOf 的定义来完成和你的(我称之为 CombinationOfOrig ),或者通过构建检查器类型函数:

type IfEquals<T, U, Y = unknown, N = never> =
  (<G>() => G extends T ? 1 : 2) extends
  (<G>() => G extends U ? 1 : 2) ? Y : N;
type TestEqualCombinationOf<T> = 
  IfEquals<CombinationOfOrig<T>, CombinationOf<T>, "Same", "Oops">;

在这里,IfEquals<T, U, Y, N>将评估为 Y如果编译器看到 TU作为相同类型,并且 N否则。请参阅this comment in GitHub有关此类型相等运算符的更多信息。

TestEqualCombinationOf<T>将评估为 "Same"如果CombinationOfOrig<T>CombinationOf<T> 相同,和"Oops"否则。以下是测试结果:

type TestEmpty = TestEqualCombinationOf<[]> // Same
type TestSingleton = TestEqualCombinationOf<[Data1]> // Same
type TestPair = TestEqualCombinationOf<[Data1, Data2]>; // Same
type TestTriple = TestEqualCombinationOf<[Data1, Data2, Data3]>; // Same
interface Data4 { four: 4 }
type TestQuadruple = TestEqualCombinationOf<[Data1, Data2, Data3, Data4]>; // Same

interface Data5 { five: 5 }
type TestQuintuple = TestEqualCombinationOf<[Data1, Data2, Data3, Data4, Data5]>; // Oops

因此,至少在实验上,对于长度不超过 4 的任何元组,这两个定义都是相同的。我实际上有点担心 TestQuintuple"Oops"直到我想起原来的定义只适用于长度不超过 4 的元组。所以 "Oops"预计。


Playground link to code

关于typescript - 如何使用递归条件类型将类型(任意长度)的元组映射到嵌套类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63907907/

相关文章:

javascript - TS2304 : Cannot find name 'stripe'

AngularJS + Angular 8 不能一起工作 [混合应用]

javascript - 反射装饰器不保存/检索对象的数据

javascript - TypeScript 库的存储库应该包含 JS 版本吗?

typescript :尝试在接口(interface)中使用 `extends` 的泛型中使用 `this`

javascript - AudioContext.decodeAudioData 似乎会产生噼啪声

javascript - 如何利用 Intellisense 将 js 库导入 Typescript 模块/VS Code 任务

typescript :检查类型是否为联合

typescript - 棱镜 : how can I get all children on a condition?

带点的 typescript 别名