TypeScript数组转换为字符串字面量类型

188

我目前有一个字符串数组和一个包含相同字符串的字符串字面量联合类型:

const furniture = ['chair', 'table', 'lamp'];
type Furniture = 'chair' | 'table' | 'lamp';

我需要在应用程序中同时使用它们,但我想保持我的代码DRY。那么有没有办法从一个中推断出另一个?

基本上我想说类似于type Furniture = [furniture数组中的任何字符串],这样就不会有重复的字符串了。

4个回答

346

TypeScript 3.4+

有了这种新语法,我们可以得到一个简洁明了的解决方案:

const furniture = ['chair', 'table', 'lamp'] as const;
type Furniture = typeof furniture[number];

关于新const上下文的更多内容可以在此PR中找到,以及发行说明中。

TypeScript 3.0+

使用泛型rest参数,有一种方法可以正确地推断string[]为一个文字元组类型,然后获得文字联合类型。

步骤如下:

const tuple = <T extends string[]>(...args: T) => args;
const furniture = tuple('chair', 'table', 'lamp');
type Furniture = typeof furniture[number];

了解更多有关泛型 rest 参数的信息


34
我可以问一下,索引签名注释[number]的目的是什么?这不是可以推断出来的吗? - robC
35
[number]的原因是,如果没有它,typeof furniture将返回一个数组类型。有了索引签名typeof furniture[number]表示“furniture中任何有效数字索引的类型”,因此您会得到一个值联合类型的类型,而不是一个数组类型。 - Jason Kohles
6
不幸的是,这仅适用于字面量数组。这将无法工作:const a = ["a", "b", "c"]; const b = a as const; - 这将抛出以下错误:'const'断言只能应用于对枚举成员、字符串、数字、布尔值、数组或对象文本的引用。 - Slavik Meltser
@SlavikMeltser:我认为问题在于你的数组的第一个定义必须带有 as const。在你的例子中,在定义 a 和定义 b 之间可能会插入一个新指令来更改 a,所以我认为这就是 TS 不想信任它的原因。对于我来说,在运行 TS 4.5.2 时,以下代码可以正常工作:const a = ["x", "y", "z"] as const; // const b = a; 如果我查看 b 的 TS 类型,我会得到 const b: readonly ["x", "y", "z"] - Erdős-Bacon
@SlavikMeltser @Erdős-Bacon Typescript已经实现了规则,如果没有给出类型注释,则推断值的正确类型。例如,["a", "b", "c"]被推断为string[]。这只是TypeScript设计者做出的选择 - 它也可以是const ["a", "b", "c"]。选择这种方式的原因是大多数情况下,开发人员想要的是string[],这就是为什么如果您想以另一种方式使用它,就需要使用as const来覆盖它的原因。 - ggradnig
显示剩余7条评论

19

这个答案已经过时,请参见@ggradnig的答案

最佳可用解决方法:

const furnitureObj = { chair: 1, table: 1, lamp: 1 };
type Furniture = keyof typeof furnitureObj;
const furniture = Object.keys(furnitureObj) as Furniture[];

理想情况下,我们可以这样做:

const furniture = ['chair', 'table', 'lamp'];
type Furniture = typeof furniture[number];

很不幸,今天furniture被推断为string[],这意味着Furniture现在也是一个string

我们可以通过手动注释来强制类型为文字,但它会带来重复:

const furniture = ["chair", "table", "lamp"] as ["chair", "table", "lamp"];
type Furniture = typeof furniture[number];

TypeScript issue #10195 追踪指示给TypeScript的列表应被推断为静态元组而不是string[],因此也许将来这将成为可能。


9

在TypeScript 3.4中,最简单的方法是使用常量断言(const assertions):

(请注意,TypeScript 3.4添加了常量断言
const furniture = ["chair", "table", "lamp"] as const;
type Furniture = typeof furniture[number]; // "chair" | "table" | "lamp"

参见https://dev59.com/0lQK5IYBdhLWcg3wSeK9#55505556

或者,如果你把这些键作为对象的属性,你也可以将其转换为联合类型:

const furniture = {chair:{}, table:{}, lamp:{}} as const;
type Furniture = keyof typeof furniture; // "chair" | "table" | "lamp"

-2
我唯一建议的调整是使const与类型保证兼容,像这样:
type Furniture = 'chair' | 'table' | 'lamp';

const furniture: Furniture[] = ['chair', 'table', 'lamp'];

如果您在数组中拼写错误或添加未知项,这将会给您一个警告:

// Warning: Type 'unknown' is not assignable to furniture
const furniture: Furniture[] = ['chair', 'table', 'lamp', 'unknown'];

唯一无法帮助您的情况是数组中不包含其中一个值。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接