字符串枚举的反向映射
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 支持字符串枚举,因此您无需添加<any>
演员表。
@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"
【讨论】:
以上是关于字符串枚举的反向映射的主要内容,如果未能解决你的问题,请参考以下文章