首页 文章

TypeScript String Union to String Array

提问于
浏览
15

我有一个字符串联合类型,如下所示:

type Suit = 'hearts' | 'diamonds' | 'spades' | 'clubs';

我想要一种类型安全的方法来获取可以在此字符串联合中使用的所有可能值 . 但由于接口主要是设计时构造,我能做的最好的就是:

export const ALL_SUITS = getAllStringUnionValues<Suit>({
    hearts: 0,
    diamonds: 0,
    spades: 0,
    clubs: 0
});

export function getAllStringUnionValues<TStringUnion extends string>(valuesAsKeys: { [K in TStringUnion]: 0 }): TStringUnion[] {
    const result = Object.getOwnPropertyNames(valuesAsKeys);
    return result as any;
}

这没关系,该函数确保我总是传递一个对象,其中每个键是字符串union中的一个元素,并且每个元素都包含在内,并返回所有元素的字符串数组 . 因此,如果字符串union更改,则在编译时对此函数的调用将发生错误(如果还未更新) .

the problem is 常量 ALL_SUITS 的类型签名是 ('hearts' | 'diamonds' | 'spades' | 'clubs')[] . 换句话说,TypeScript认为它是一个包含没有或多个这些值的数组,可能带有重复项,而不是只包含所有值的数组,例如 ['hearts', 'diamonds', 'spades', 'clubs'] .

我真正喜欢的是我的通用 getAllStringUnionValues 函数指定它返回 ['hearts', 'diamonds', 'spades', 'clubs'] 的方法 .

如何尽可能DRY实现这个 generically

1 回答

  • 16

    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 ALL_SUITS = tuple('hearts', 'diamonds', 'spades', 'clubs');
    type SuitTuple = typeof ALL_SUITS;
    type Suit = SuitTuple[number];  // union type
    

    2017年8月更新

    自从我发布这个答案后,如果你愿意为你的库添加一个函数,我找到了一种推断元组类型的方法 . 查看tuple.ts中的函数 tuple() .

    export type Lit = string | number | boolean | undefined | null | void | {};
    
    // infers a tuple type for up to twelve values (add more here if you need them)
    export function tuple<A extends Lit, B extends Lit, C extends Lit, D extends Lit, E extends Lit, F extends Lit, G extends Lit, H extends Lit, I extends Lit, J extends Lit, K extends Lit, L extends Lit>(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L): [A, B, C, D, E, F, G, H, I, J, K, L];
    export function tuple<A extends Lit, B extends Lit, C extends Lit, D extends Lit, E extends Lit, F extends Lit, G extends Lit, H extends Lit, I extends Lit, J extends Lit, K extends Lit>(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K): [A, B, C, D, E, F, G, H, I, J, K];
    export function tuple<A extends Lit, B extends Lit, C extends Lit, D extends Lit, E extends Lit, F extends Lit, G extends Lit, H extends Lit, I extends Lit, J extends Lit>(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J): [A, B, C, D, E, F, G, H, I, J];
    export function tuple<A extends Lit, B extends Lit, C extends Lit, D extends Lit, E extends Lit, F extends Lit, G extends Lit, H extends Lit, I extends Lit>(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I): [A, B, C, D, E, F, G, H, I];
    export function tuple<A extends Lit, B extends Lit, C extends Lit, D extends Lit, E extends Lit, F extends Lit, G extends Lit, H extends Lit>(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H): [A, B, C, D, E, F, G, H];
    export function tuple<A extends Lit, B extends Lit, C extends Lit, D extends Lit, E extends Lit, F extends Lit, G extends Lit>(a: A, b: B, c: C, d: D, e: E, f: F, g: G): [A, B, C, D, E, F, G];
    export function tuple<A extends Lit, B extends Lit, C extends Lit, D extends Lit, E extends Lit, F extends Lit>(a: A, b: B, c: C, d: D, e: E, f: F): [A, B, C, D, E, F];
    export function tuple<A extends Lit, B extends Lit, C extends Lit, D extends Lit, E extends Lit>(a: A, b: B, c: C, d: D, e: E): [A, B, C, D, E];
    export function tuple<A extends Lit, B extends Lit, C extends Lit, D extends Lit>(a: A, b: B, c: C, d: D): [A, B, C, D];
    export function tuple<A extends Lit, B extends Lit, C extends Lit>(a: A, b: B, c: C): [A, B, C];
    export function tuple<A extends Lit, B extends Lit>(a: A, b: B): [A, B];
    export function tuple<A extends Lit>(a: A): [A];
    export function tuple(...args: any[]): any[] {
      return args;
    }
    

    使用它,您可以编写以下内容而不必重复:

    const ALL_SUITS = tuple('hearts', 'diamonds', 'spades', 'clubs');
    type SuitTuple = typeof ALL_SUITS;
    type Suit = SuitTuple[number];  // union type
    

    你怎么看?


    原始答案

    获得所需内容的最简单方法是明确指定元组类型并从中派生联合,而不是试图强制TypeScript执行相反的操作,即doesn't know how to do . 例如:

    type SuitTuple = ['hearts', 'diamonds', 'spades', 'clubs'];
    const ALL_SUITS: SuitTuple = ['hearts', 'diamonds', 'spades', 'clubs']; // extra/missing would warn you
    type Suit = SuitTuple[number];  // union type
    

    请注意,您仍然会将文字写出两次,一次作为 SuitTuple 中的类型,一次作为 ALL_SUITS 中的值;你没有很好的方法可以避免以这种方式重复自己,因为当前无法将TypeScript告知infer tuples,它将从元组类型生成运行时数组never .

    这里的优点是您不需要在运行时对虚拟对象进行密钥枚举 . 如果你仍然需要它们,你当然可以用西装作为键来构建类型:

    const symbols: {[K in Suit]: string} = {
      hearts: '♥', 
      diamonds: '♦', 
      spades: '♠', 
      clubs: '♣'
    }
    

    希望有所帮助 .

相关问题