Typescript从元组/数组值派生联合类型

WHI*_*LOR 46 typescript

说我有清单 const list = ['a', 'b', 'c']

是不是可以从这个值的联合类型派生出来的'a' | 'b' | 'c'

我想要这个,因为我想定义只允许来自静态数组的值的类型.而且我还需要在运行时枚举这些值,所以我使用数组.

示例如何使用索引对象实现:

const list = ['a', 'b', 'c']

'a' | 'b' | 'c'

const list = ['a', 'b', 'c']

但我想知道没有这个索引地图是否可行.

jca*_*alz 94

更新2019年2月

应该在2019年3月发布的TypeScript 3.4中,可以通过使用语法告诉编译器将文字元组的类型推断为文字元组,而不是说.这种类型的断言使编译器推断出一个值可能的最窄类型,包括制作所有内容.它应该如下所示:string[]as constreadonly

const list = ['a', 'b', 'c'] as const; // TS3.4 syntax
type NeededUnionType = typeof list[number]; // 'a'|'b'|'c';
Run Code Online (Sandbox Code Playgroud)

这将消除对任何类型的辅助功能的需要.祝大家好运!


2018年7月更新

看起来,从TypeScript 3.0开始,TypeScript可以自动推断元组类型.一旦发布,tuple()您需要的功能可以简洁地写为:

export type Lit = string | number | boolean | undefined | null | void | {};
export const tuple = <T extends Lit[]>(...args: T) => args;
Run Code Online (Sandbox Code Playgroud)

然后你可以像这样使用它:

const list = tuple('a','b','c');  // type is ['a','b','c']
type NeededUnionType = typeof list[number]; // 'a'|'b'|'c'
Run Code Online (Sandbox Code Playgroud)

希望对人们有用!


更新2017年12月

自从我发布这个答案后,如果你愿意为你的库添加一个函数,我找到了一种推断元组类型的方法.退房的功能tuple()tuple.ts.使用它,您可以编写以下内容而不是重复:

const list = tuple('a','b','c');  // type is ['a','b','c']
type NeededUnionType = typeof list[number]; // 'a'|'b'|'c'
Run Code Online (Sandbox Code Playgroud)

祝好运!


原始2017年7月

一个问题是文字['a','b','c']将被推断为类型string[],因此类型系统将忘记特定值.您可以强制类型系统将每个值记住为文字字符串:

const list = ['a' as 'a','b' as 'b','c' as 'c']; // infers as ('a'|'b'|'c')[]
Run Code Online (Sandbox Code Playgroud)

或者,也许更好,将列表解释为元组类型:

const list: ['a','b','c'] = ['a','b','c']; // tuple
Run Code Online (Sandbox Code Playgroud)

这是烦人的重复,但至少它不会在运行时引入无关的对象.

现在你可以像这样得到你的联盟:

type NeededUnionType = typeof list[number];  // 'a'|'b'|'c'.
Run Code Online (Sandbox Code Playgroud)

希望有所帮助.

  • 有人可以解释一下 `[number]` 在 `list[number]` 中的作用吗? (8认同)
  • 它被解析为`(typeof list)[number]`...而不是`typeof(list[number])`。类型`T[K]`是一个[查找类型](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html#keyof-and-lookup-types)获取键为“K”的“T”属性的类型。在`(typeof list)[number]` 中,你得到`(typeof list)` 的属性类型,其键是`number`。像 `typeof list` 这样的数组有 [数字索引签名](https://www.typescriptlang.org/docs/handbook/interfaces.html#indexable-types),所以它们的 `number` 键产生所有数字索引的联合特性。 (8认同)
  • 它不应该与此一起工作,因为当你执行 `const xs = ['a','b','c']` 时,编译器已经将 `xs` 扩展为 `string[]` 并且完全忘记了具体值。至少从 TS3.2 开始我无法控制这种行为(不过未来可能会有一个“as const”表示法可以使用)。无论如何,我认为目前的答案仍然是我能做出的正确答案(我在那里提到 `['a','b','c']` 被推断为 `string[]`),所以我不确定您还需要什么。 (2认同)

ggr*_*nig 10

TypeScript 3.4的更新:

TypeScript 3.4中将提供一种名为“ const contexts”的新语法,该语法将提供一种甚至更简单的解决方案,不需要演示函数调用。如本PR中所述,此功能目前正在审查

简而言之,此语法允许创建具有狭窄类型(即,用type ['a', 'b', 'c']代替('a' | 'b' | 'c')[]or string[])的不可变数组。这样,我们可以轻松地从文字创建联合类型,如下所示:

const MY_VALUES = <const> ['a', 'b', 'c']
type MyType = typeof MY_VALUES[number]
Run Code Online (Sandbox Code Playgroud)

在替代语法中:

const MY_VALUES = ['a', 'b', 'c'] as const
type MyType = typeof MY_VALUES[number]
Run Code Online (Sandbox Code Playgroud)


Aid*_*din 6

我假设您在 2019 年 3 月之后就开始依赖 TypeScript 生活了。(现在是 2021 年 11 月)

我只是用可导出的实用函数扩展了最佳答案:

// Your handmade utils' library file
export type UnionOfArrayElements<ARR_T extends Readonly<unknown[]>> = ARR_T[number];
Run Code Online (Sandbox Code Playgroud)
// Usage
const a = ["hi", "bye", 3, false] as const;
type ta = UnionOfArrayElements<typeof a>; // false | "hi" | "bye" | 3

const b = [4, 5, 6];
type tb = UnionOfArrayElements<typeof b>; // number

Run Code Online (Sandbox Code Playgroud)