字符串枚举的反向映射

Posted

技术标签:

【中文标题】字符串枚举的反向映射【英文标题】:Reverse-Mapping for String Enums 【发布时间】:2017-12-06 13:47:55 【问题描述】:

我想在打字稿中使用字符串枚举,但我看不到它支持反向映射。 我有一个这样的枚举:

enum Mode 
    Silent = "Silent",
    Normal = "Normal",
    Deleted = "Deleted"

我需要像这样使用它:

let modeStr: string;
let mode: Mode = Mode[modeStr];

是的,我不知道modeStr 字符串中有什么,我需要将其解析为枚举,或者如果枚举定义中未显示该字符串,则在运行时解析失败。 我怎样才能做到尽可能整洁? 提前谢谢

【问题讨论】:

Create an enum with string values in Typescript的可能重复 @ponury-kostek 从技术上讲,该问题并未解决进行反向映射的问题。即使是这样,它也可能是隐藏在底部的众多答案之一,因此很难找到解决方案。我说如果没有更好的副本,让我们保留这个。 【参考方案1】:

我们可以让Mode成为一个类型和一个值在同一类型。

type Mode = string;
let Mode = 
    Silent: "Silent",
    Normal: "Normal",
    Deleted: "Deleted"


let modeStr: string = "Silent";
let mode: Mode;

mode = Mode[modeStr]; // Silent
mode = Mode.Normal; // Normal
mode = "Deleted"; // Deleted
mode = Mode["unknown"]; // undefined
mode = "invalid"; // "invalid"

更严格的版本:

type Mode = "Silent" | "Normal" | "Deleted";
const Mode = 
    get Silent(): Mode  return "Silent"; ,
    get Normal(): Mode  return "Normal"; ,
    get Deleted(): Mode  return "Deleted"; 


let modeStr: string = "Silent";
let mode: Mode;

mode = Mode[modeStr]; // Silent
mode = Mode.Normal; // Normal
mode = "Deleted"; // Deleted
mode = Mode["unknown"]; // undefined
//mode = "invalid"; // Error

字符串枚举为this answer:

enum Mode 
    Silent = <any>"Silent",
    Normal = <any>"Normal",
    Deleted = <any>"Deleted"


let modeStr: string = "Silent";
let mode: Mode;

mode = Mode[modeStr]; // Silent
mode = Mode.Normal; // Normal
//mode = "Deleted"; // Error
mode = Mode["unknown"]; // undefined

【讨论】:

谢谢...虽然它可以在打字稿中实现。 tslint 将在该 Enum 示例上引发错误:元素隐式具有“任何”类型,因为索引表达式不是“数字”类型。我想问题是在 TS 字符串枚举中不能反向映射,请参阅 typescriptlang.org/docs/handbook/release-notes/… 的 String-Enum 示例中的注释 - 这对于引入 String-Enum 的 TS 2.4 来说似乎是正确的,但我也得到了TS 2.6.2 中的错误 @masi 从 2.4 开始,TS 支持字符串枚举,因此您无需添加 &lt;any&gt; 演员表。 @RodrigoPedrosa 我知道这一点。有和没有 演员的错误都会发生。有一个与该问题(github.com/Microsoft/TypeScript/issues/9234)相关的 GIT 问题打开,但它已关闭为“按预期工作”。似乎 TS 开发人员从未使用过任何其他支持 String-Evals 的语言,否则他们不会声称它按预期工作。充其量它对于试图将类型带入另一个语言的语言“尽可能好”(我还没有检查技术。到目前为止认为的原因)。 @masi 我不确定你在说什么。反向映射在操场上有效。您的问题与 tslint 相关吗?【参考方案2】:

目前我发现的最干净的方法是制作一张二级地图:

let reverseMode = new Map<string, Mode>();
Object.keys(Mode).forEach((mode: Mode) => 
    const modeValue: string = Mode[<any>mode];
    reverseMode.set(modeValue, mode);
);

这样你就可以let mode: Mode = reverseMode.get('Silent');

优点:不需要重复值,提供枚举枚举的方法,让 TSLint 快乐...

编辑:我最初写的是Mode[mode],但后来 TS 可能会在这一行抛出错误 TS7015,所以我添加了演员表。

【讨论】:

【参考方案3】:

如果您可以使用代理,我制作了一个更通用、更紧凑的版本:

stringEnum.ts

export type StringEnum<T extends string> = [K in T]: K
const proxy = new Proxy(, 
  get(target, property) 
    return property;
  
)
export default function stringEnum<T extends string>(): StringEnum<T> 
  return proxy as StringEnum<T>;

用法:

import stringEnum from './stringEnum';
type Mode = "Silent" | "Normal" | "Deleted";
const Mode = stringEnum<Mode>();

【讨论】:

【参考方案4】:

没有一个答案真的对我有用,所以我必须恢复正常循环。 我的枚举是

enum SOME_CONST
      STRING1='value1', 
      STRING2 ='value2',
      .....and so on



getKeyFromValue =(value:string)=> Object.entries(SOME_CONST).filter((item)=>item[1]===value)[0][0];

【讨论】:

【参考方案5】:

这个答案来自@PhiLho,

我没有足够的代表来评论他的消息。

let reverseMode = new Map<string, Mode>();
Object.keys(Mode).forEach((mode: Mode) => 
    const modeValue: string = Mode[<any>mode];
    reverseMode.set(modeValue, mode);
);

但是,.set 的第一个参数应该是键,第二个应该是值。

reverseMode.set(modeValue, mode);

应该是……

reverseMode.set(mode, modeValue);

导致这个...

let reverseMode = new Map<string, ErrorMessage>();
Object.keys(ErrorMessage).forEach((mode: ErrorMessage) => 
    const modeValue: string = ErrorMessage[<any>mode];
    reverseMode.set(mode, modeValue);
);

@PhiLho 可能错过了这一点的原因是因为 OP 提供的原始对象的键和值是相同的。

【讨论】:

【参考方案6】:

对于仍在为此苦苦挣扎的所有人来说,这是完成此任务的一种快速而肮脏的方式。

enum Mode 
    Silent = "Silent",
    Normal = "Normal",
    Deleted = "Deleted"


const currMode = 'Silent',
  modeKey = Object.keys(Mode)[(Object.values(Mode) as string[]).indexOf(currMode)];

但是,您应该注意一些警告!

Microsoft 将 Git 错误标记为“按预期工作”的最可能原因可能是因为它处理的是字符串文字,而枚举最初设计为可索引的。虽然上面的代码可以使用 TSLint 并 lints,但它有两个问题。对象在转换为数组时不能保证保持预期的顺序,我们正在处理可能是唯一的也可能不是唯一的字符串文字。

看看这个 StackBlitz,其中有两个 Mode 的值为“Silent”。一旦查找完成,您零保证它不会返回“忽略”而不是“静默”。

https://stackblitz.com/edit/typescript-hyndj1?file=index.ts

【讨论】:

【参考方案7】:

标准类型实现了这个功能——不过只检查未定义和句柄。

type Mode = "Silent" | "Normal" | "Deleted";
const a = Mode["something else"];
//a will be undefined

此外,您可以在上面添加记录类型以扩展“枚举”,例如将短枚举键转换为更详细的输出 (https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type):

const VerboseMode: Record<Mode, string> = 
  Silent: "Silent mode",
  Normal: "Normal mode",
  Deleted: "This thing has been deleted"

【讨论】:

以上是关于字符串枚举的反向映射的主要内容,如果未能解决你的问题,请参考以下文章

TypeScript入门七:TypeScript的枚举

枚举反向查找[重复]

枚举与字符串映射

如何使用 AutoMapper 将 json 请求 dto 中的 OData 枚举字符串映射到实体枚举属性

作为映射键的字符串枚举不适用于 ReturnType

Angular:将字符串数组映射到http get调用中的枚举值数组