如何使用Typescript创建常量数组索引的并集类型



我有一个常量字符串数组,例如

const emojis = ['😄', '😊', '😐', '😕', '😣'] as const

我想要一个包含该数组索引并集的类型,例如

type emojiIndexes = IndexesOfArray<typeof emojis> // => 0 | 1 | 2 | 3 | 4

所以我不允许使用number,只使用数组中索引的确切数量

如果阵列大小,例如

// changed from this
// const emojis = ['😄', '😊', '😐', '😕', '😣'] as const
// to this 
const emojis = ['😄', '😊', '😐'] as const // removed 2 emojis

那么,IndexesOfArray<typeof emojis>将是0 | 1 | 2

如何创建IndexesOfArray来创建带有常量数组索引的并集类型?

这里有一个解决方案:(Playground Link(

type TupleIndices<A extends any[]>
= A extends [any, ...infer T]
? TupleIndices<T> | T['length']
: never

示例:

type Foo = ['foo', 'bar', 'baz', 'qux', 'quz']
// 0 | 4 | 3 | 2 | 1
type FooIndices = TupleIndices<Foo>

因为该解决方案是递归的,所以对于中等长度的元组,它将失败。如果你需要这个来处理更长的元组,你可以尝试一个尾部递归版本:(Playground Link(

type TupleIndices<A extends any[], Acc = never>
= A extends [any, ...infer T]
? TupleIndices<T, Acc | T['length']>
: Acc

用法相同。

您可以通过从参数类型中排除所有空数组键来实现这一点,因此您最终只得到索引的并集:

type IndexesOfArray<A> = Exclude<keyof A, keyof []>
const emojis = ['😄', '😊', '😐', '😕', '😣'] as const
type emojiIndexes = IndexesOfArray<typeof emojis> // => '0' | '1' | '2' | '3' | '4'

索引是字符串而不是数字,但这不应该引起任何问题。如果你确实想要数字,你可以使用递归条件类型来生成它们,但这会导致TypeScript的递归深度出现问题。或者,你可以使用一个稍微有点粗糙的硬编码数组并对其进行索引来获得数字:

type ToNum = [0,1,2,3,4,5,6,7] // add as many as necessary
type emojiNumIndexes = ToNum[IndexesOfArray<typeof emojis>] // => 0 | 1 | 2 | 3 | 4

TypeScript游乐场

我们可以利用这样一个事实,即keyof数组会生成数组上所有可用方法的并集,如pushconcatmap等,如果数组是固定长度的(即元组(,它还会将索引添加到此并集中。因此,我们可以使用Exclude从并集中仅提取索引。

type Indices<TArr> = Exclude<keyof TArr, keyof []>;

但我们可能还想为TArr添加一个适当的约束,以确保我们只将固定长度的数组传递给Indices

type Indices<TArr extends readonly [] | [any, ...any]> = Exclude<keyof TArr, keyof []>;

最新更新