我对试图欺骗编译器生成
reverse mappings
对于字符串枚举
<any>
类型断言。反向字符串映射是故意省略的,因为
@RyanCavanaugh
在A中说
relevant GitHub issue
:
如果我们自动提供反向映射(顺便说一下,这不一定是不含糊的!)除非在运行时创建了一个完全独立的对象,否则您将无法区分键和值。如果需要,编写一个构造反向映射的函数是很简单的;我们的目标是尽可能少地发出代码,因此只发出所需的数据(保持代码大小较小)并让您根据需要构造反向映射是有意义的。
我认为如果你继续使用这个技巧,你会发现自己需要像现在这样做,如果未来的某些版本的typescript完全破坏了它,你不应该感到惊讶。
但是,如果typescript的开发主管说“编写一个构造反向映射的函数很简单”,那么我们可以尝试一下,看看它是如何发展的。你会怎么做?在运行时,您实际上只需要遍历枚举对象条目,并通过键和值的切换生成一个新的对象。如果你想在同一个对象中同时使用正向和反向映射,你可以
merge
从常规枚举到反向枚举的属性:
type Entries<T extends object> = { [K in keyof T]: [K, T[K]] }[keyof T]
function reverseEnum<E extends Record<keyof E, string | number>>(
e: E
): { [K in E[keyof E]]: Extract<Entries<E>, [any, K]>[0] };
function reverseEnum(
e: Record<string | number, string | number>
): Record<string | number, string | number> {
const ret: Record<string | number, string | number> = {};
Object.keys(e).forEach(k => ret[e[k]] = k);
return ret;
}
function twoWayEnum<E extends Record<keyof E, string | number>>(e: E) {
return Object.assign(reverseEnum(e), e);
}
的签名
reverseEnum()
有点像玩杂耍。类型函数
Entries<T>
转换对象类型
T
进入键值对的并集,例如,
Entries<{a: string, b: number}>
评估为
["a",string] | ["b",number]
. 然后返回类型
回复枚举()
是一个
mapped type
其键来自枚举
价值观
,其值来自对应项的键
extracting
它。让我们看看它是否有效:
enum AttackType {
MELEE = 'close',
RANGED = 'far'
}
const TwoWayAttackType = twoWayEnum(AttackType);
// const TwoWayAttackType = {
// close: "MELEE";
// far: "RANGED";
// } & typeof AttackType
// might as well make a type called TwoWayAttackType also,
// corresponding to the property values of the TwoWayAttackType object
type TwoWayAttackType = AttackType | keyof typeof AttackType
console.log(TwoWayAttackType.close); // "MELEE"
console.log(TwoWayAttackType[TwoWayAttackType.far]) // "far"
你可以看到这个值
TwoWayAttackType
与具有相同类型的
AttackType
枚举常量,具有额外属性
{close: "MELEE", far: "RANGED"}
. 一个问题是typescript不会自动生成名为
TwowayAttackType型
对应的属性类型
TwowayAttackType型
常数,所以如果你想要一个,我们必须手工制作,正如我上面所做的。
现在,您应该能够使您的类符合需要,没有类型错误:
class Hero {
attackType: TwoWayAttackType;
constructor() {
this.attackType = TwoWayAttackType['close'];
this.attackType = TwoWayAttackType.MELEE;
this.attackType = TwoWayAttackType[TwoWayAttackType['close']];
}
}
请注意,如果此方法对您有效,您可以始终重命名上面的值/类型,以便我调用
TwowayAttackType型
只是
攻击类型
(然后也许是我的电话
攻击类型
会有点像
OneWayAttackType
或
BaseAttackType
)
好吧,希望有帮助,祝你好运!