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

Enhancement: [strict-boolean-expressions] Check truthiness assertion functions #9009

Open
4 tasks done
alecmev opened this issue Apr 27, 2024 · 6 comments · May be fixed by #9074
Open
4 tasks done

Enhancement: [strict-boolean-expressions] Check truthiness assertion functions #9009

alecmev opened this issue Apr 27, 2024 · 6 comments · May be fixed by #9074
Labels
accepting prs Go ahead, send a pull request that resolves this issue enhancement: plugin rule option New rule option for an existing eslint-plugin rule package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin

Comments

@alecmev
Copy link

alecmev commented Apr 27, 2024

Before You File a Proposal Please Confirm You Have Done The Following...

My proposal is suitable for this project

  • I believe my proposal would be useful to the broader TypeScript community (meaning it is not a niche proposal).

Link to the rule's documentation

https://typescript-eslint.io/rules/strict-boolean-expressions/

Description

For example, ok from node:assert has the following shape:

function ok(value: unknown, message?: string | Error): asserts value;

It appears that currently the rule just looks at value, sees unknown, and moves on. So it's possible to write this:

const foo: string | undefined = 'bar';
if (foo) ... // Error
ok(foo); // Fine

Instead, it would be nice if it also looked at the return type and took asserts value into account, treating it as if (!value) throw. There's more to asserts than just checking truthiness, but handling the simplest case (no is) would already go a long way, as that covers all ok-like assertion functions out there.

Fail

const foo: string | undefined = 'bar';
ok(foo);

Pass

const foo: string | undefined = 'bar';
ok(foo !== undefined);

Additional Info

No response

@alecmev alecmev added enhancement: plugin rule option New rule option for an existing eslint-plugin rule package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin triage Waiting for maintainers to take a look labels Apr 27, 2024
@kirkwaiblinger
Copy link
Member

kirkwaiblinger commented May 7, 2024

I'm +0.5 to this, since I've run into this as well, and been annoyed by it. The +0.5 rather than +1 is due to the fact that it's an easy and obvious place to use aneslint-disable comment, since I can only imagine this coming up in the first place in isolated, conspicuous scenarios.

EDIT - I crossed wires with something else when writing that. Reading more closely, I'm fully +1 on this. An assertion function argument is a boolean context, complete with narrowing.

We'll see what others think!

@bradzacher
Copy link
Member

bradzacher commented May 7, 2024

I'd never really thought about it like this.
I guess that asserts foo is defined by the type system to mean if not foo then throw - regardless of the underlying implementation of the assertion function.
And it looks like TS does essentially treat it like a boolean context where the code after the call is the "condition was true" branch.

It may be a bit weird for people to think of assertions like this but I think there's value in it.

Though note that we would only check asserts foo functions and not asserts foo is bar functions.

@bradzacher bradzacher added accepting prs Go ahead, send a pull request that resolves this issue and removed triage Waiting for maintainers to take a look labels May 7, 2024
@kirkwaiblinger
Copy link
Member

kirkwaiblinger commented May 11, 2024

Question - do we think that we should check this context analogously in no-unnecessary-condition? @bradzacher @alecmev
(can spin off a new issue if it warrants nontrivial discussion)

(playground)

function assert(x: unknown): asserts x {
  // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
  if (!x) {
    throw new Error('assertion');
  }
}

const truthyString = 'string';
if (truthyString) { }
    ~~~~~~~~~~~~

const alsoTruthyString = 'string';
assert(alsoTruthyString)

I'm of the opinion that we should, though it's less easy of a call to make than for the strict-boolean-expressions case, and I think it might warrant an option in that rule. In any case, we can dig into that further after seeing what people's initial impressions are.

@bradzacher
Copy link
Member

Sounds like a valuable thing yeah.
Part of me thinks it might want an option for that rule though - I could see people using assertion functions to enforce "always true" invariants.

@kirkwaiblinger
Copy link
Member

Sounds like a valuable thing yeah. Part of me thinks it might want an option for that rule though - I could see people using assertion functions to enforce "always true" invariants.

Yep, that's exactly my reservation as well, having used assertions myself to defend against regressions due to types at runtime not matching their declared types. Will file dedicated issue for that.

Do you think there ought to be an option for this rule as well?

@bradzacher
Copy link
Member

nah this rule is straight-forward. It's not going to ban an assertion - just ensure you're using it strictly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
accepting prs Go ahead, send a pull request that resolves this issue enhancement: plugin rule option New rule option for an existing eslint-plugin rule package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin
Projects
None yet
3 participants