如何为两个枚举创建映射类型?

时间:2019-02-20 16:29:58

标签: typescript

我正在寻找一种连接这两种类型并具有映射的方法:

type MappingForEnum<T extends string, A extends string, B extends string> = {
  [key in T]: {[key in A]: B};
  [key in T]: {[key in B]: A};
};

enum Greek {
  ALPHA = 'A',
  BETA = 'B',
}

enum English {
  A = 'A',
  B = 'B',
}

enum types{
  TO_ENGLISH = 'toEnglish',
  TO_GREEK = 'toGreek',
}

const mapping: MappingForEnum<Types, Greek, English> = {
  toEnglish: {
    [Greek.ALPHA]: English.A,
    [Greek.BETA]: English.B,
  },
  toGreek: {
    [English.A]: Greek.ALPHA,
    [English.B]: Greek.BETA,
  },
};

如何修改MappingForEnum使其起作用?

更新

有没有一种方法可以设置这种PureMapping并将其包装起来以匹配下面的注释。

type PureMapping = {
  toGreek: Greek,
  toEnglish: English,
};

type Mapping = WrapMapping<PureMapping>;

// type Mapping = {
//   toGreek: MappingForEnum<Greek, English>,
//   toEnglish: MappingForEnum<English, Greek>,
// };

1 个答案:

答案 0 :(得分:0)

问题是您的types枚举不能保证只有两个成员。此外,也无法知道哪个成员将英语映射为希腊语,哪个成员将反向映射。如果types只有一个或三个成员,怎么办?

一种解决方案是摆脱第一个泛型类型参数,而仅依赖于硬编码的属性名称(例如,选择比convert / reverse更好的名称):

type MappingForEnum<A extends string, B extends string> = {
  convert: {[key in A]: B};
  reverse: {[key in B]: A};
};

...

const mapping: MappingForEnum<Greek, English> = {
  convert: {
    [Greek.ALPHA]: English.A,
    [Greek.BETA]: English.B,
  },
  reverse: {
    [English.A]: Greek.ALPHA,
    [English.B]: Greek.BETA,
  },
};

或者,您可以将类型分解为较小的单向映射,然后将它们组合在一起:

type MappingForEnum<A extends string, B extends string> = { [key in A]: B };

type GreekEnglishMapping = {
  toEnglish: MappingForEnum<Greek, English>;
  toGreek: MappingForEnum<English, Greek>;
};

...

const mapping: GreekEnglishMapping = {
  toEnglish: {
    [Greek.ALPHA]: English.A,
    [Greek.BETA]: English.B,
  },
  toGreek: {
    [English.A]: Greek.ALPHA,
    [English.B]: Greek.BETA,
  },
};

请注意,使用第二种解决方案,获得与原始types枚举类似的联合类型非常容易:

type supportedMappings = keyof GreekEnglishMapping; // "toEnglish" | "toGreek"

这是另一种方法:

type MappingForEnum<A extends string, B extends string> = { [key in A]: B };
type TwoWayMapping<T extends { [0]: string, [1]: string }, A extends string, B extends string> =
    { [key in T[0]]: MappingForEnum<A, B> } &
    { [key in T[1]]: MappingForEnum<B, A> };

type GreekEnglishMapping = TwoWayMapping<[ 'toEnglish', 'toGreek' ], Greek, English>;

遗憾的是,即使您定义

enum MappingNames {
  toEnglish,
  toGreek,
}

在运行时定义了MappingNames[0]的情况下,编译器将不了解它,因此以下代码将不起作用:

type GreekEnglishMapping = TwoWayMapping<MappingNames, Greek, English>;
// Type 'MappingNames' does not satisfy the constraint '{ [0]: string; [1]: string; }'.

再刺一击:

type MappingForEnum<A extends string, B extends string> = { [key in A]: B };
type TwoWayMapping<T extends { [k: string]: A | B }, A extends string, B extends string> = { 
    [key in keyof T]: T[key] extends A ? MappingForEnum<A, B> : MappingForEnum<B, A>
};

type PureMapping = {
  toEnglish: Greek,
  toGreek: English,
};
type GreekEnglishMapping = TwoWayMapping<PureMapping, Greek, English>;

我觉得这可能与您最初想要的差不多。我认为不必两次指定Greek / English(一次在映射类型中,一次作为通用参数)。