Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug: [no-unsafe-enum-comparison] does not work with bit masks #6909

Closed
4 tasks done
mrazauskas opened this issue Apr 15, 2023 · 2 comments
Closed
4 tasks done

Bug: [no-unsafe-enum-comparison] does not work with bit masks #6909

mrazauskas opened this issue Apr 15, 2023 · 2 comments
Labels
bug Something isn't working package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin wontfix This will not be worked on

Comments

@mrazauskas
Copy link
Contributor

mrazauskas commented Apr 15, 2023

Before You File a Bug Report Please Confirm You Have Done The Following...

  • I have tried restarting my IDE and the issue persists.
  • I have updated to the latest version of the packages.
  • I have searched for related issues and found none that matched my issue.
  • I have read the FAQ and my problem is not listed.

Playground Link

https://typescript-eslint.io/play/#ts=5.0.3&sourceType=module&code=KYDwDg9gTgLgBAYwgOwM72MgrgWzgFQE8xgAxAGwEMBzVOAbwCg44BBZQuAXjgEY4APALgAGADTM4AVWQBrZBADuybn0HDeElgGUYUAJbJqq-kLgAmLXAByuAEbAoJ9XADMVgEIQI5YJRU8psIALIwAvoyMoJCwcABmWMgIMPoocAAWlKhEJBQ0ABSoEFhQCMAAXATEZFS0YnAwlFDUwDCVOTU0qACUDJJQrSUqhcWlwHAAZA1NLTC9XAvTza0A3OFAA&eslintrc=N4KABGBEBOCuA2BTAzpAXGUFvcgAQBcBPABxQGNoBLEggWhXioDsCB6ZgeztmeQEMAZogbNYAWzrlO4kv2rJOzdFETRonaJHBgAviF1A&tsconfig=N4KABGBEDGD2C2AHAlgGwKYCcDyiAuysAdgM6QBcYoEEkJemy0eAcgK6qoDCAFutAGsylBm3TgwAXxCSgA

Repro Code

export const enum TypeFlags {
  Any = 1 << 0,
  Unknown = 1 << 1,
  String = 1 << 2,
  Number = 1 << 3,
  Boolean = 1 << 4
}

export function hasTypeFlag(source: TypeFlags, target: TypeFlags) {
  return (source & target) === target;
}

ESLint Config

module.exports = {
  "rules": {
        "@typescript-eslint/no-unsafe-enum-comparison": "error"
  }
}

tsconfig

{
  "compilerOptions": {
    "strictNullChecks": true
  }
}

Expected Result

TypeScript compiler is using bit mask pattern internally. It is rare, but useful thing. A bitwise operator is used in comparison, but otherwise enum is compared with itself. Should this be allowed or at least have a config option, perhaps? What do you think?


Also possible to rework the code. It felt more readable with ===, but could be like this too:

export function hasTypeFlag(source: TypeFlags, target: TypeFlags) {
  return Boolean(source & target);
}

Actual Result

Good idea to add the no-unsafe-enum-comparison. As you can see in reproduction, currently it does not work with bit mask pattern. Might be it was simply overlooked use case.

Additional Info

No response

@mrazauskas mrazauskas added bug Something isn't working package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin triage Waiting for maintainers to take a look labels Apr 15, 2023
@bradzacher
Copy link
Member

The result of var & var is always number - thus you're comparing number === TypeFlags, hence the rule is erroring.

We don't generally like doing deep inspections of expressions like suggested because it means code may or may not be linted before/after refactoring.
For example

// this might be fine
(source & target) === target

// but if I extract it to a variable...
const left = source & target
left === target // error - comparing number to enum

It creates inconsistencies that are hard for users to reconcile and thus are frustrating.


Our general advice is that really flags enums are just difficult to treat safely and generally are just an unsound pattern. That's not to say they're bad and you shouldn't use them - it's just that the way they work within TS is entirely unsafe and impossible to safely type using TS.

For example it's common and correct to work with flags using | - however | will create values outside the allowed values of the enum. The expression TypeFlags.Number | TypeFlags.Boolean means that both flags are set, but the resulting number is 24, which is not an allowed value of TypeFlags. Which makes it weird when you write code like hasTypeFlag(TypeFlags.Number | TypeFlags.Boolean, TypeFlags.Boolean) - the result of your function call is true, but inspecting types it's actually not strictly correct or safe.

You might think that & should always result in a valid TypeFlags number - however that's not the case. For example (TypeFlags.Any | TypeFlags.Unknown | TypeFlags.String) & (TypeFlags.Any | TypeFlags.Unknown) results in the value 5, which is still outside the allowed values of TypeFlags.


For the above reasons we don't think it's a good idea to do deeper checks on flags enums to support them in the rule. Thanks for filing!

@bradzacher bradzacher closed this as not planned Won't fix, can't repro, duplicate, stale Apr 15, 2023
@bradzacher bradzacher added wontfix This will not be worked on and removed triage Waiting for maintainers to take a look labels Apr 15, 2023
@mrazauskas
Copy link
Contributor Author

Thanks for the details! Looks complex indeed.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 23, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin wontfix This will not be worked on
Projects
None yet
Development

No branches or pull requests

2 participants