'Convert binary string to flags in typescript

I have a binary string which I want to convert to the equivalent flag enum value. My attempt:

const permissionNum = parseInt(this.auth.decodedToken.permissions, 2);
const userPermissions: PermissionFlags = PermissionFlags[PermissionFlags[permissionNum]];

and this is the enum:

export enum PermissionFlags {
  None = 0,
  RemoveMember = 1 << 0,
  Invite = 1 << 1,
  EditArticleSettings = 1 << 2,
  CheckOut = 1 << 3,
  CheckIn = 1 << 4,
  CanView = 1 << 5,
  IsOwner = 1 << 6,
  xxx = 1 << 7
}

This does work as long as it is a single flag. So if the string is 00111000 this is parsed to 56 but userPermissionsstays undefined. Is that not possible in Typescript?



Solution 1:[1]

Enums in TypeScript work a bit differently compared to those in C#. After all, they are compiled down to a simple JavaScript object with indexing based on the names and values. Therefor, that object will not contain a valid entry for the index that is the combination of multiple flags, but only for the individual flag values.

For example, your enum declaration will compile to the following JavaScript:

export var PermissionFlags;
(function (PermissionFlags) {
    PermissionFlags[PermissionFlags["None"] = 0] = "None";
    PermissionFlags[PermissionFlags["RemoveMember"] = 1] = "RemoveMember";
    PermissionFlags[PermissionFlags["Invite"] = 2] = "Invite";
    PermissionFlags[PermissionFlags["EditArticleSettings"] = 4] = "EditArticleSettings";
    PermissionFlags[PermissionFlags["CheckOut"] = 8] = "CheckOut";
    PermissionFlags[PermissionFlags["CheckIn"] = 16] = "CheckIn";
    PermissionFlags[PermissionFlags["CanView"] = 32] = "CanView";
    PermissionFlags[PermissionFlags["IsOwner"] = 64] = "IsOwner";
    PermissionFlags[PermissionFlags["xxx"] = 128] = "xxx";
})(PermissionFlags || (PermissionFlags = {}));

If you now index into the PermissionFlags object with the index 56, you get undefined as no value is defined for that specific index.

I would rather use the approach in the following code snippet:

export enum PermissionFlags {
  None = 0,
  RemoveMember = 1 << 0,
  Invite = 1 << 1,
  EditArticleSettings = 1 << 2,
  CheckOut = 1 << 3,
  CheckIn = 1 << 4,
  CanView = 1 << 5,
  IsOwner = 1 << 6,
  xxx = 1 << 7
}

export function hasPermission(userPermissions: number | string, flags: PermissionFlags): boolean {
  const perm = typeof userPermissions === "number" ? userPermissions : parseInt(userPermissions, 2);
  return (perm & flags) !== 0;
}

// Check for permissions by specifying the user permission mask and the target permission flags
const canInvite = hasPermission("00111000", PermissionFlags.Invite);
const canCheckInAndIsOwner = hasPermission(56, PermissionFlags.CheckIn | PermissionFlags.IsOwner);
// Could even call it with PermissionFlags as first argument as those are basically numbers
const canCheckOut = hasPermission(PermissionFlags.CheckOut | PermissionFlags.IsOwner, PermissionFlags.CheckOut);

Solution 2:[2]

function getPermissions(val :number| PermissionFlags) : number[] {
var res = Object.keys(PermissionFlags)
.filter(t=> typeof t !== "number" && val & +t)
.map(t=>+t);
return res; 
}
var r = PermissionFlags.CheckIn | PermissionFlags.IsOwner;
var q = getPermissions(r);
console.log(q[0] === PermissionFlags.CheckIn)
console.log(q[1] === PermissionFlags.IsOwner)

Basically get the values of enum. apply has flag with "val & t" expression return the matched values. You may not want to return an array so instead you can make sth like this:

let flags =0;
res.forEach(r=> flags|=r);
return flags;

funny part of it if you know your number is valid you can directly cast it to enum.

Solution 3:[3]

Anon answer is great but be careful, there is a security issue inside it. the line

const canCheckInAndIsOwner = hasPermission(56, PermissionFlags.CheckIn | PermissionFlags.IsOwner);

won't do what it says, in fact it will be canCheckInOrIsOwner. If you want it to be and and not or, you should replace the line :

return (perm & flags) !== 0;

by

return (perm & flags) === flags;

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Anon
Solution 2
Solution 3 macaque