【问题标题】:How to define array with alternating types in TypeScript?如何在 TypeScript 中定义具有交替类型的数组?
【发布时间】:2020-07-24 02:17:55
【问题描述】:

我想定义一个数组类型,它允许根据位置不同的类型,但以重复、交替的方式,就像在某些数据结构中发现的那样。

例子:

[A, B, A, B, ...]
[A, B, C, A, B, C, ...]

这可能吗?

我知道我可以为具有固定数量元素的数组定义它,如上面(没有省略号),并且

(A | B)[]

另一方面,允许任何元素为 A 或 B 类型。

我试过这些:

[(A, B)...]
[...[A, B]]
[(A, B)*]

【问题讨论】:

  • 你不能。元组按索引具有不同类型但长度固定,数组不允许您定义交替类型。看起来您的数据结构很奇怪,为什么不使用例如[A, B][],元组数组?
  • 谢谢。 :-( 用于 Elasticsearch 批量操作。
  • 这是个好问题。希望有一个解决方案。
  • 这个问题可能与stackoverflow.com/questions/67315596/…重复
  • @Arc 您能否将具有此类要求的弹性文档的链接发给我?

标签: arrays typescript types


【解决方案1】:

我想出了一些“有效”的东西,但这有点疯狂:

type Alternating<T extends readonly any[], A, B> =
  T extends readonly [] ? T
  : T extends readonly [A] ? T
  : T extends readonly [A, B, ...infer T2]
    ? T2 extends Alternating<T2, A, B> ? T : never
  : never

由于递归条件类型,这需要 TypeScript 4.1+。


幼稚的用法需要将值复制为 T 参数的文字类型,这并不理想:

const x: Alternating<[1, 'a', 2], number, string> = [1, 'a', 2]

这似乎比仅仅写出[number, string, number] 作为类型更糟糕。然而,在虚拟函数的帮助下,可以避免重复:

function mustAlternate<T extends readonly any[], A, B>(
  _: Alternating<T, A, B>
): void {}

const x = [1, 'a', 2] as const
mustAlternate<typeof x, number, string>(x)

Here's a live demo with some test cases.


我实际上不建议在典型的代码库中依赖它(使用起来很尴尬,而且错误消息很糟糕)。我大多只是通过它来查看类型系统可以延伸多远。

如果有人对如何使它不那么不稳定有任何建议,我会全力以赴!

【讨论】:

    【解决方案2】:

    替代方法:

    type MAXIMUM_ALLOWED_BOUNDARY = 50
    
    type Mapped<
        Tuple extends Array<unknown>,
        Result extends Array<unknown> = [],
        Count extends ReadonlyArray<number> = []
        > =
        (Count['length'] extends MAXIMUM_ALLOWED_BOUNDARY
            ? Result
            : (Tuple extends []
                ? []
                : (Result extends []
                    ? Mapped<Tuple, Tuple, [...Count, 1]>
                    : Mapped<Tuple, Result | [...Result, ...Tuple], [...Count, 1]>)
            )
        )
    
    
    
    type Result = Mapped<[string, number, number[]]>
    
    // 3 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 30 | 33 | 36 | 39 | 42 | 45 | 48
    type Test = Result['length']
    
    /**
     * Ok
     */
    const test1: Result = ['a', 42, [1]]
    const test2: Result = ['a', 42, [1], 'b', 43, [2]]
    
    /**
     * Fail
     */
    const test3:Result = ['a'] // error
    
    const fn = <T, U>(tuple: Mapped<[T, U]>) => tuple
    
    fn([42, 'hello']) // ok
    fn([42, 'hello','sdf']) // expected error
    

    Playground

    Mapped - 丑陋的名字,但工作:D。创建元组的所有允许状态的联合。每个允许的元组状态都有一个可以除以 3 的长度:length%3===0 // true。你可以定义任何你想要的元组,4个值,5个等等......

    每次迭代我都会将 Count 数组增加 1。这就是我知道何时停止递归迭代的方式。

    【讨论】:

      猜你喜欢
      • 2015-06-05
      • 1970-01-01
      • 2019-07-01
      • 2017-10-20
      • 2015-10-07
      • 2021-05-08
      • 2019-01-23
      • 2021-06-14
      相关资源
      最近更新 更多