如何在Typescript中声明匹配值的约束元组?

ca1c2owp  于 2023-06-30  发布在  TypeScript
关注(0)|答案(1)|浏览(104)

我试图创建一个pair类型,它只允许从给定的字符串联合中分配匹配的值。
首先,我已经可以从给予const数组创建一个字符串联合:

const allFoos = ['foo', 'bar', 'baz'] as const;

// Creates a string union of ('foo' | 'bar' | 'baz')
type Foo = typeof allFoos[number];

另外,我可以创建一个元组,它只允许分配这个联合的成员。

// Creates a tuple allow any two of Foo to be assigned
type Pair = readonly [Foo, Foo];

const good: Pair = ['foo', 'bar']; // Assigns fine
const bad: Pair = ['foo', 'zzz']; // Compile error

理想情况下,我想要一个只允许相同值元组类型。
显然,我可以通过手写指定所有结果:

type HandWrittenMatchedPair = 
  | readonly ['foo', 'foo']
  | readonly ['bar', 'bar']
  | readonly ['baz', 'baz'];

const good: HandWrittenMatchedPair = ['foo', 'foo']; // Assigns fine
const bad: HandWrittenMatchedPair = ['foo', 'baz']; // Compile error

添加的元素越多,手写所有变体的可行性就越小。同样对于N个元素元组,它变得更加乏味,所以理想情况下应该生成这些类型

// How to do this without handwriting every possibility?
type MatchedPair = [???]; 

// Ideal outcome
const good: MatchedPair = ['foo', 'foo']; // Should assign fine
const bad: MatchedPair = ['foo', 'bar']; // Should result in compile error

我想做的事情可能吗?

sczxawaw

sczxawaw1#

您希望定义Pair,以便它自动地将“turnT into readonly [T, T]”操作 * 分发 * 到Foo联合体上。有几种方法可以做到这一点:
由于Foo的成员是类键的(字符串文字类型"foo""bar""baz"可以是对象的键),您可以编写 * 分布式对象类型 *(如ms/TS#47109中所创造的),这是您在成员上创建mapped type,就好像它们是对象键一样,然后立即index into该Map类型以获得属性值的并集。像这样:

type Pair = { [T in Foo]:
    readonly [T, T]
}[Foo]

或者,对于任意联合(其成员可能不是keylike),您可以编写一个分布式条件类型,其中检查类型是泛型类型参数,如下所示:

type Pair = Foo extends infer T ? T extends unknown ? (
    readonly [T, T]
) : never : never

(Note在上面的例子中,T是泛型类型参数,而Foo * 不是 * 泛型类型参数。不能将其缩短为Foo extends unknown ? readonly [Foo, Foo] : never。我使用条件类型infer ence的全部原因是将Foo复制到泛型类型参数T,以便内部条件类型是分布式的。
无论哪种方式

// type Pair = 
//   readonly ["foo", "foo"] | 
//   readonly ["bar", "bar"] | 
//   readonly ["baz", "baz"]

如果Foo的定义更新,则其将自动更新。
Playground链接到代码

相关问题