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

时间:2017-07-22 06:54:51

标签: typescript

说我有名单 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

我想知道是否可以在不使用索引地图的情况下进行此操作。

4 个答案:

答案 0 :(得分:39)

2019年2月更新

TypeScript 3.4, which should be released in March 2019中,可以告诉编译器将文字元组的类型推断为文字元组,而不是像string[]那样,使用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'.

希望有所帮助。

答案 1 :(得分:6)

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]

答案 2 :(得分:2)

无法使用Array执行此操作。

原因是,即使你将变量声明为const,数组的内容仍然可以改变,因此@jonrsharpe提到这是运行时。

根据您的需要,将import { Observable } from 'rxjs/Observable'; import { Http } from '@angular/http'; import 'rxjs/add/operator/map'; export class ServiceMock { public http: Http; url: string; getGoogle(): Observable<any> { return this.http.get('./data.mock').map((res) => { res.json(); }); } } export class Data { name: string = 'Aditya'; profession: string = 'Developer'; } 一起使用可能更好:

interface

keyof

interface X {
    a: string,
    b: string
}

type Y = keyof X  // Y: 'a' | 'b'

答案 3 :(得分:0)

如果使用对象来存储“常量”,则这是实现相同思想的一种方式:

(请注意'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];