打字稿泛型中的自定义枚举过于复杂

Posted

技术标签:

【中文标题】打字稿泛型中的自定义枚举过于复杂【英文标题】:Custom enum in typescript generics is overcomplicated 【发布时间】:2018-09-28 20:53:24 【问题描述】:

如果有更简单的方法来写下来,因为这非常重复并且看起来非常错误......

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类,它自己的属性很少,只有一个功能

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:我已经尝试过了,但它会给我stringFolderVisibility.values。此外,它仍然很长。 常量数据 = 公共:'公共', 私人:'私人'

const FolderVisibility = new Enum<typeof data>(data) as Enum<typeof data> & typeof data

【问题讨论】:

你为什么不能只使用一个简单的打字稿枚举 enum FolderVisibilityEnum PUBLIC = 'public', PRIVATE = 'private' ?您可以为枚举创建这些方法,它看起来会更好.. 我们有多个枚举,它们都共享同一个类,因此使用相同的函数似乎是合适的。另外,如果添加枚举不需要太多努力,那就太好了。 第二个好处是可以更好地学习如何在 TS 中使用泛型类型。有这么多功能,但很难找到它们的教程 【参考方案1】:

对象字面量和字面量类型的问题在于,您无法让编译器推断出对象字面量属性的字面量类型。这就是为什么需要指定泛型类型参数的原因。

你的方法中有一部分是绝对可以简化的,那就是枚举之后的转换。不要使用构造函数,使用简单的函数,因为它可以更灵活地返回什么:

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;
);

【讨论】:

以上是关于打字稿泛型中的自定义枚举过于复杂的主要内容,如果未能解决你的问题,请参考以下文章

打字稿泛型返回类型

typescript 打字稿泛型示例

typescript 打字稿泛型

打字稿泛型类型 T toString

带有约束的打字稿泛型不能分配给泛型接口

打字稿泛型类参数