打字稿从元组/数组值派生联合类型
Posted
技术标签:
【中文标题】打字稿从元组/数组值派生联合类型【英文标题】:Typescript derive union type from tuple/array values 【发布时间】:2017-12-28 07:58:14 【问题描述】:说我有清单
const list = ['a', 'b', 'c']
是否可以从 'a' | 'b' | 'c'
这个值联合类型派生?
我想要这个是因为我想定义只允许来自静态数组的值的类型,并且还需要在运行时枚举这些值,所以我使用数组。
如何使用索引对象实现它的示例:
const indexed = a: null, b: null, c: null
const list = Object.keys(index)
type NeededUnionType = keyof typeof indexed
我想知道是否可以不使用索引地图来做到这一点。
【问题讨论】:
所以本质上,您想动态生成类型? 类型只在编译时存在,不能在运行时动态创建。 这是一个有趣的问题。你的用例是什么? 【参考方案1】:2019 年 2 月更新
在TypeScript 3.4, which should be released in March 2019 中,可以通过使用as const
syntax。这种类型的断言导致编译器推断出一个值的最窄类型,包括将所有内容设为readonly
。它应该是这样的:
const list = ['a', 'b', 'c'] as const; // TS3.4 syntax
type NeededUnionType = typeof list[number]; // 'a'|'b'|'c';
这将消除对任何类型的辅助函数的需要。再次祝大家好运!
2018 年 7 月更新
看起来,从 TypeScript 3.0 开始,TypeScript 将可以使用automatically infer tuple types。一旦发布,你需要的tuple()
函数可以简洁的写成:
export type Lit = string | number | boolean | undefined | null | void | ;
export const tuple = <T extends Lit[]>(...args: T) => args;
然后你可以像这样使用它:
const list = tuple('a','b','c'); // type is ['a','b','c']
type NeededUnionType = typeof list[number]; // 'a'|'b'|'c'
希望对人们有用!
2017 年 12 月更新
自从我发布此答案后,如果您愿意将函数添加到库中,我找到了一种推断元组类型的方法。查看tuple.ts 中的函数tuple()
。使用它,您可以编写以下内容而不重复自己:
const list = tuple('a','b','c'); // type is ['a','b','c']
type NeededUnionType = typeof list[number]; // 'a'|'b'|'c'
祝你好运!
2017 年 7 月原稿
一个问题是文字['a','b','c']
将被推断为类型string[]
,因此类型系统会忘记具体的值。您可以强制类型系统将每个值记住为文字字符串:
const list = ['a' as 'a','b' as 'b','c' as 'c']; // infers as ('a'|'b'|'c')[]
或者,也许更好,将列表解释为元组类型:
const list: ['a','b','c'] = ['a','b','c']; // tuple
这是令人讨厌的重复,但至少它不会在运行时引入无关的对象。
现在你可以像这样得到你的工会:
type NeededUnionType = typeof list[number]; // 'a'|'b'|'c'.
希望对您有所帮助。
【讨论】:
当我需要运行时检查(使用元组)和使用类型联合进行编译时检查时,这是我经常遇到的问题的绝佳解决方案。任何人都知道是否有努力为语言添加对隐式元组类型的支持? 试过你的解决方案,但它不适用于cont xs = ['a','b','c']; const list = tuple(...xs);
它不应该与它一起工作,因为当您执行 const xs = ['a','b','c']
时,编译器已经将 xs
扩大到 string[]
并且完全忘记了具体值。至少从 TS3.2 开始,我无法帮助这种行为(尽管未来可能会有 as const
表示法有效)。无论如何,我认为目前的答案仍然尽可能正确(我在那里提到['a','b','c']
被推断为string[]
)所以我不确定你还需要什么。
有人能解释一下[number]
在list[number]
中的作用吗?
它被解析为(typeof list)[number]
...而不是typeof (list[number])
。类型T[K]
是一个lookup type,它获取T
的属性类型,其键为K
。在(typeof list)[number]
中,您将获得(typeof list)
的属性类型,其键为number
。像typeof list
这样的数组有numeric index signatures,所以它们的number
键产生所有数字索引属性的联合。【参考方案2】:
TypeScript 3.4 更新:
一种名为 "const contexts" 的新语法将出现在 TypeScript 3.4 中,这将允许一个更简单的解决方案,不需要如演示的函数调用。此功能目前正在审核as seen in this PR。
简而言之,此语法允许创建具有窄类型(即类型 ['a', 'b', 'c']
而不是 ('a' | 'b' | 'c')[]
或 string[]
)的不可变数组。这样,我们就可以从字面量创建联合类型,如下所示:
const MY_VALUES = <const> ['a', 'b', 'c']
type MyType = typeof MY_VALUES[number]
替代语法:
const MY_VALUES = ['a', 'b', 'c'] as const
type MyType = typeof MY_VALUES[number]
【讨论】:
您好,刚刚在发布此问题后找到了您的答案。如果你想回答,我会让你赚点业力;)***.com/questions/56113411/…【参考方案3】:Array 无法做到这一点。
原因是,即使你将变量声明为 const,数组的内容仍然可以改变,因此@jonrsharpe 提到这是运行时。
根据您的需要,最好将interface
与keyof
一起使用:
interface X
a: string,
b: string
type Y = keyof X // Y: 'a' | 'b'
或enum
:
enum X a, b, c
【讨论】:
有没有办法排除某些字段?例如,如果我只想要a
字段..
您可以使用Pick<T, U>
。【参考方案4】:
如果使用对象来存储“常量”,这是实现相同想法的一种方式:
(注意 'as const' 将 keyOne 和 keyTwo 的类型从字符串更改为文字。)
const configObj =
keyOne: 'literalTypeValueOne' as const,
keyTwo: 'literalTypeValueTwo' as const,
;
const typeValues = [configObj.keyOne, configObj.keyTwo] as const;
type MyType = typeof typeValues[number];
【讨论】:
【参考方案5】:我假设您现在在 2019 年 3 月之后使用 TypeScript。 (现在是 2021 年 11 月)
我只是用一个可导出的实用函数扩展the top answer:
// Your handmade utils' library file
export type UnionOfArrayElements<ARR_T extends Readonly<unknown[]>> = ARR_T[number];
// 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
【讨论】:
以上是关于打字稿从元组/数组值派生联合类型的主要内容,如果未能解决你的问题,请参考以下文章