简体   繁体   English

Typescript泛型中的自定义枚举过于复杂

[英]Custom enum in typescript generics is overcomplicated

if there is simpler way to write this down as this is extremely repetitive and just seems very wrong... 如果有更简单的方式写下来,因为这是非常重复的,而且看起来非常错误...

const FolderVisibility = new Enum<{
    PUBLIC: 'public',
    PRIVATE: 'private'
}>({
    PUBLIC: 'public',
    PRIVATE: 'private'
}) as Enum<{
    PUBLIC: 'public',
    PRIVATE: 'private'
}> & {
    PUBLIC: 'public',
    PRIVATE: 'private'
}

I want the IDE to be able to tell me that the FolderVisibility.PUBLIC == 'public' as the parameter is readonly anyway. 我希望IDE能够告诉我FolderVisibility.PUBLIC == 'public'因为该参数无论如何都是只读的。

Here is Enum class, it has few of its own properties and one function 这是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)
    }
}

The point is, that if I copy the object used in constructor 4 times, it will even tell me that FolderVisibility.values is of type 'public' | 'private' 关键是,如果我复制构造函数中使用的对象4次,它甚至会告诉我FolderVisibility.values的类型为'public' | 'private' 'public' | 'private'

PS: I have tried this, but it will give me string for FolderVisibility.values . PS:我已经尝试过了,但是它将为我提供FolderVisibility.values string also, it is still pretty long. 而且,它仍然很长。 const data = { PUBLIC: 'public', PRIVATE: 'private' } const data = {PUBLIC:“公共”,PRIVATE:“私有”}

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

The problem with object literals and literal types is that you can't get the compiler to infer a literal type for an object literal property. 对象文字和文字类型的问题在于,您无法让编译器为对象文字属性推断文字类型。 This is why specifying the generic type argument is necessary. 这就是为什么必须指定泛型类型参数的原因。

There is a part of your approach that can definitely be simplified, and that is the cast after the enum. 您的方法的一部分绝对可以简化,那就是枚举之后的强制转换。 Don't use a constructor, use a simple function as that has greater flexibility as to what it can 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

We can also use the function above to augment an actual enum ,with less explicit typing needed: 我们还可以使用上面的函数来扩展实际的枚举,而无需进行显式键入:

enum FolderVisibilityProto {
    PUBLIC ='public',
    PRIVATE=  'private'
}
const FolderVisibility = Enum(FolderVisibilityProto);

Or we can change the Enun function a bit to take a callback that creates the enum internally so as not to ever have access to the non-augmented enum: 或者,我们可以Enun更改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;
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM