简体   繁体   English

如何在 TypeScript 中定义具有交替类型的数组?

[英]How to define array with alternating types in TypeScript?

I would like to define an array type that allows different types depending on position, but in a repeating, alternating manner as found in some data structures.我想定义一个数组类型,它允许根据 position 使用不同的类型,但是以在某些数据结构中发现的重复、交替的方式。

Example:例子:

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

Is this possible?这可能吗?

I know that I can define it for arrays with a fixed number of elements like above (without the ellipsis), and我知道我可以为 arrays 定义它,上面有固定数量的元素(没有省略号),并且

(A | B)[]

would on the other hand allow any element to be either of type A or B.另一方面,将允许任何元素为 A 或 B 类型。

I tried these:我试过这些:

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

I came up with something that "works", but it's kinda crazy:我想出了一些“有效”的东西,但这有点疯狂:

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

This requires TypeScript 4.1+ because of the recursive conditional type.由于递归条件类型,这需要 TypeScript 4.1+。


Naive usage requires duplicating the value as a literal type for the T parameter, which is not ideal:幼稚的用法需要将值复制为T参数的文字类型,这并不理想:

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

That seems strictly worse than just writing out [number, string, number] as the type.这似乎比仅仅写出[number, string, number]作为类型更糟糕。 However with the help of a dummy function it's possible to avoid repetition:然而,在虚拟 function 的帮助下,可以避免重复:

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 . 这是一个带有一些测试用例的现场演示


I wouldn't actually recommend relying on this in typical codebases (it's awkward to use and the error messages are terrible).我实际上并不建议在典型的代码库中依赖它(使用起来很尴尬,而且错误消息很糟糕)。 I mostly just worked through it to see how far the type system could be stretched.我大多只是通过它来查看类型系统可以延伸多远。

If anyone has suggestions for how to make it less wonky, I'm all ears!如果有人对如何使它不那么不稳定有任何建议,我全神贯注!

Alternative approach:替代方法:

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 - ugly name but does the job:D. Mapped - 丑陋的名字,但工作:D。 Creates a union of all allowed states of the tuple.创建元组的所有允许状态的联合。 Each allowed tuple state has a length which can be divided by 3: length%3===0 // true .每个允许的元组 state 的长度可以除以 3: length%3===0 // true You can define any tuple you want, with 4 values, 5 etc...你可以定义任何你想要的元组,有 4 个值,5 个等等......

Every iteration I\m increasing Count array by 1. That's how I know when to stop recursive iteration.每次迭代我都会将Count数组增加 1。这就是我知道何时停止递归迭代的方式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM