打字稿泛型中的自定义枚举过于复杂
Custom enum in typescript generics is overcomplicated
如果有更简单的方法来写下它,因为这是非常重复的,而且看起来非常错误......
const FolderVisibility = new Enum<{
PUBLIC: 'public',
PRIVATE: 'private'
}>({
PUBLIC: 'public',
PRIVATE: 'private'
}) as Enum<{
PUBLIC: 'public',
PRIVATE: 'private'
}> & {
PUBLIC: 'public',
PRIVATE: 'private'
}
我希望 IDE 能够告诉我 FolderVisibility.PUBLIC == 'public'
作为参数无论如何都是只读的。
这里是Enum
class,它有几个自己的属性和一个功能
export default class Enum<T extends { [index: string]: string }> {
private readonly map: T;
public readonly values: (T[keyof T])[];
constructor(enums: T) {
Object.defineProperty(this, 'map', { value: {} });
for (let prop in enums) {
if (enums.hasOwnProperty(prop)) {
const value = enums[prop]
if(typeof value != 'string'){
throw new EnumError(value)
}
this.map[prop] = value
Object.defineProperty(this, prop, { value });
}
}
Object.defineProperty(this, 'values', { value: Object.values(this.map) });
}
isValid(text: any) {
if (!text) return true
return this.values.includes(text)
}
}
关键是,如果我复制构造函数中使用的对象 4 次,它甚至会告诉我 FolderVisibility.values
是 'public' | 'private'
类型
PS:我试过这个,但它会给我 string
换 FolderVisibility.values
。而且,它仍然很长。
常量数据 = {
PUBLIC: 'public',
私人:'private'
}
const FolderVisibility = new Enum<typeof data>(data) as Enum<typeof data> & typeof data
对象字面量和字面量类型的问题是您无法让编译器推断对象字面量的字面量类型 属性。这就是为什么需要指定泛型类型参数的原因。
您的方法中有一部分绝对可以简化,那就是枚举之后的转换。不要使用构造函数,使用一个简单的函数,因为它具有更大的灵活性 return:
function Enum<T extends{ [P in keyof T]: string }>(enums: T) {
let map : { [index: string]: string } = {}
for (let prop in enums) {
if (enums.hasOwnProperty(prop)) {
const value = enums[prop]
if(typeof value != 'string'){
throw new EnumError(value)
}
map[prop] = value;
}
}
let result = Object.assign({}, enums , {
values: Object.values(map),
isValid(text: string) {
if (!text) return true
return this.values.includes(text)
}
});
// maybe frees the enum so no values are chanegd ?
return Object.freeze(result);
}
const FolderVisibility = Enum<{
PUBLIC: 'public',
PRIVATE: 'private'
}>({
PUBLIC: 'public',
PRIVATE: 'private'
});
console.log(FolderVisibility.isValid("")) // Works
console.log(FolderVisibility.PRIVATE === "private" ) // And const fields of string literal type
我们还可以使用上面的函数来扩充实际的枚举,而无需显式键入:
enum FolderVisibilityProto {
PUBLIC ='public',
PRIVATE= 'private'
}
const FolderVisibility = Enum(FolderVisibilityProto);
或者我们可以稍微更改 Enun
函数以获取在内部创建枚举的回调,以便永远无法访问非扩充枚举:
function Enum<T extends{ [P in keyof T]: string }>(enumsCreator: () => T) {
let enums = enumsCreator();
…
}
const FolderVisibility = Enum(()=>
{
enum FolderVisibility {
PUBLIC ='public',
PRIVATE= 'private'
}
return FolderVisibility;
});
如果有更简单的方法来写下它,因为这是非常重复的,而且看起来非常错误......
const FolderVisibility = new Enum<{
PUBLIC: 'public',
PRIVATE: 'private'
}>({
PUBLIC: 'public',
PRIVATE: 'private'
}) as Enum<{
PUBLIC: 'public',
PRIVATE: 'private'
}> & {
PUBLIC: 'public',
PRIVATE: 'private'
}
我希望 IDE 能够告诉我 FolderVisibility.PUBLIC == 'public'
作为参数无论如何都是只读的。
这里是Enum
class,它有几个自己的属性和一个功能
export default class Enum<T extends { [index: string]: string }> {
private readonly map: T;
public readonly values: (T[keyof T])[];
constructor(enums: T) {
Object.defineProperty(this, 'map', { value: {} });
for (let prop in enums) {
if (enums.hasOwnProperty(prop)) {
const value = enums[prop]
if(typeof value != 'string'){
throw new EnumError(value)
}
this.map[prop] = value
Object.defineProperty(this, prop, { value });
}
}
Object.defineProperty(this, 'values', { value: Object.values(this.map) });
}
isValid(text: any) {
if (!text) return true
return this.values.includes(text)
}
}
关键是,如果我复制构造函数中使用的对象 4 次,它甚至会告诉我 FolderVisibility.values
是 'public' | 'private'
PS:我试过这个,但它会给我 string
换 FolderVisibility.values
。而且,它仍然很长。
常量数据 = {
PUBLIC: 'public',
私人:'private'
}
const FolderVisibility = new Enum<typeof data>(data) as Enum<typeof data> & typeof data
对象字面量和字面量类型的问题是您无法让编译器推断对象字面量的字面量类型 属性。这就是为什么需要指定泛型类型参数的原因。
您的方法中有一部分绝对可以简化,那就是枚举之后的转换。不要使用构造函数,使用一个简单的函数,因为它具有更大的灵活性 return:
function Enum<T extends{ [P in keyof T]: string }>(enums: T) {
let map : { [index: string]: string } = {}
for (let prop in enums) {
if (enums.hasOwnProperty(prop)) {
const value = enums[prop]
if(typeof value != 'string'){
throw new EnumError(value)
}
map[prop] = value;
}
}
let result = Object.assign({}, enums , {
values: Object.values(map),
isValid(text: string) {
if (!text) return true
return this.values.includes(text)
}
});
// maybe frees the enum so no values are chanegd ?
return Object.freeze(result);
}
const FolderVisibility = Enum<{
PUBLIC: 'public',
PRIVATE: 'private'
}>({
PUBLIC: 'public',
PRIVATE: 'private'
});
console.log(FolderVisibility.isValid("")) // Works
console.log(FolderVisibility.PRIVATE === "private" ) // And const fields of string literal type
我们还可以使用上面的函数来扩充实际的枚举,而无需显式键入:
enum FolderVisibilityProto {
PUBLIC ='public',
PRIVATE= 'private'
}
const FolderVisibility = Enum(FolderVisibilityProto);
或者我们可以稍微更改 Enun
函数以获取在内部创建枚举的回调,以便永远无法访问非扩充枚举:
function Enum<T extends{ [P in keyof T]: string }>(enumsCreator: () => T) {
let enums = enumsCreator();
…
}
const FolderVisibility = Enum(()=>
{
enum FolderVisibility {
PUBLIC ='public',
PRIVATE= 'private'
}
return FolderVisibility;
});