实现
类似ts的enum,双向映射,同时可用于for...of循环
2022-07-25 更新
根据类型体操4179-Flip,翻转对象有更简单直接的实现
/** 翻转对象键与值 */
export type Flip<Obj extends Record<string, string | number>> = {
[Key in keyof Obj as Obj[Key]]: Key
}
/** 可用于 `${}` 的类型 */
export type Stringable = string | number | bigint | boolean | null | undefined
/** 更宽松的键值 */
export type FlipLoose<Obj extends Record<string | number, Stringable>> = {
[Key in keyof Obj as Obj[Key] extends keyof any ? Obj[Key] : Obj[Key] extends Stringable ? `${Obj[Key]}` : never]: Key extends Stringable ? `${Key}` : never
}
// 以下注释部分被 Flip 取代
/** 获取 value 类型 */
// export type ValueOf<O extends Record<keyof any, any>> = O[keyof O]
/** 工具类型 根据值获取键 */
// type KeysMatching<Obj, Val> = { [Key in keyof Obj]-?: Obj[Key] extends Val ? Key : never }[keyof Obj]
/** 工具类型 把值转换成键类型 */
// type SetValuesTokeyType<T extends Record<keyof any, keyof any | boolean | null | undefined>> = {
// [K in keyof T]: T[K] extends keyof any
// ? T[K]
// : T[K] extends bigint | boolean | undefined | null
// ? `${T[K]}`
// : never
// }
/** 工具类型 反转对象值与键 */
// export type Reverse<Obj extends Record<keyof Obj, Obj[keyof Obj]>> = { [Val in ValueOf<Obj>]: KeysMatching<Obj, Val> }
// export type ReverseLoose<T extends Record<keyof any, keyof any | boolean | null | undefined>> = Reverse<SetValuesTokeyType<T>>
/** 生成双向映射 */
export function useEnum<T extends Record<string | number, string | number | boolean | null | undefined>>(obj: T)
: Readonly<
& T
& FlipLoose<T>
& { [Symbol.iterator](): IterableIterator<[keyof T, T[keyof T]]> }
> {
const newObj = Object.create(null)
// 创建自定义迭代器 可以 for(const [key, val] of myEnum) { }
Object.defineProperty(newObj, Symbol.iterator, {
enumerable: false,
value: () => Object.entries(obj)[Symbol.iterator]()
})
for (const key in obj) {
newObj[String(newObj[key] = obj[key])] = key
}
return Object.freeze(newObj)
}
使用
const myEnum = useEnum({
'数学': 1,
'语文': 2,
'英语': 3,
'化学': false,
'物理': true,
'历史': undefined,
'地理': null
} as const)
console.log(myEnum[1])
for (const [key, val] of myEnum) {
console.log(key, val)
}
效果如图
在vue组件中使用
<template>
<!-- 选项 -->
<el-select clearable v-model="optionValue">
<el-option
v-for="[label, value] of myEnum"
:key="label"
:value="(value as any)"
:label="label"
/>
</el-select>
<!-- 使用 -->
<div>{{ myEnum[optionValue] }}</div>
</template>
<script lang="ts" setup>
const optionValue = ref('')
const myEnum = useEnum({
'数学': 1,
'语文': 2,
'英语': 3,
'化学': false,
'物理': true,
'历史': undefined,
'地理': null
} as const)
</script>