在 TypeScript 中转换 FlaggedEnum

Convert FlaggedEnum in TypeScript

我有一个这样的枚举,

enum Traits {
    None = 0,
    Friendly = 1 << 0, // 0001 
    Mean = 1 << 1,     // 0010
    Funny = 1 << 2,    // 0100
    Boring = 1 << 3,   // 1000
    All = ~(~0 << 4)   // 1111
}


let traits = Traits.Mean | Traits.Funny 
console.log(traits); 

此控制台将记录 6。反过来怎么办? 如果我通过 6 。我想得到 Traits.Mean , Traits.Funny.

你可以这样做:

for (var prop in Traits) {
  if(Traits[prop] & 6) {
    console.log(prop);
  }
}

虽然 enum 的定义值的名称在运行时出现在表示 enum 类型的对象中,但复合值不存在。我们可以编写一个函数来计算复合显示字符串:

enum Traits {
    None = 0,
    Friendly = 1 << 0, // 0001 
    Mean = 1 << 1,     // 0010
    Funny = 1 << 2,    // 0100
    Boring = 1 << 3,   // 1000
    FunnyBoringCustom = Funny | Boring, // Defined compund
    All = ~(~0 << 4)   // 1111
}


let traits = Traits.Mean | Traits.Funny 

function enumToString<T extends { [name: string]: any }>(enumType: T, enumValue: T[keyof T], separator = ','){
    // Predefined value
    if(enumType[enumValue]!== undefined){
        return enumType[enumValue];
    }

    let values = Object
        .getOwnPropertyNames(enumType)
        // We only take numeric values, the enum contains both direct and reverse lookup
        .filter(x => Number.isInteger(enumType[x]) && enumType[x] != 0)
        // Sort the values, so that compound values will be before their constituents  
        .sort((a,b) => enumType[b] - enumType[a]);

    var result = "";
    for(let e of values){
        if((enumValue & enumType[e]) == enumType[e] ){
            result += result !==  "" ? separator: "";
            result+=e;
            // Exclude the bits that were in this value, to treat defined compound values correcty.
            enumValue = enumValue & (~enumType[e]);
        }
    }

    return result;
}
// Usage
console.log(enumToString(Traits, traits)); // Funny,Mean
console.log(enumToString(Traits, 6)); // Funny,Mean
console.log(enumToString(Traits, Traits.Friendly | Traits.Boring)) //Boring,Friendly
console.log(enumToString(Traits, Traits.Funny | Traits.Boring)) //FunnyBoringCustom
console.log(enumToString(Traits, Traits.All)); // All

编辑

正如我们所看到的,该函数做了相当多的工作,我们可以缓存结果。

我们可以在 enum 对象本身中这样做:

function enumToString<T extends { [name: string]: any }>(enumType: T, enumValue: T[keyof T], separator = ','){
    ...
    enumType[enumValue] = result;
    return result;
}

或者我们可以创建一个包装版本的函数,为每种类型缓存在不同的对象中:

function cachedEnumToString<T extends { [name: string]: any }>(enumType: T, separator = ',') {
    let cache: { [name: string]: string } = {};
    return function forValue(enumValue: T[keyof T]) {
        var result = cache[enumValue];
        if (!result) {
            result = enumToString(enumType, enumValue, separator);
            cache[enumValue] = result;
        }
        return result
    }
}
// Usage
const traitsToString = cachedEnumToString(Traits);

console.log(traitsToString(traits));
console.log(traitsToString(6));
console.log(traitsToString(Traits.Friendly | Traits.Boring))
console.log(traitsToString(Traits.Funny | Traits.Boring))
console.log(traitsToString(Traits.All));