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
[strict-boolean-expression] Rework options #1515
Comments
I see this rule as being analogous to flow's inbuilt So expanding on @phaux's cases, I see the following cases that we need to care about:
With those cases listed out, this is what I think should happen:
I.e. I propose the following options interface Options {
// default = true
allowString?: boolean;
allowNumber?: boolean;
allowNullableObject?: boolean;
// default = false
allowNullableBoolean?: boolean;
allowNullableString?: boolean;
allowNullableNumber?: boolean;
allowAny?: boolean;
} Adding in extra information about the old
HOWEVER, when this same code is used in the context of a place expecting a boolean like Currently, the rule option doesn't do that, which makes the rule a lot less safe. |
BTW with these options, we can end up with a logical expression where all the operands are allowed, but its resulting type is something we don't allow. E.g. |
It's not mentioned on way or another in this issue, and it's not part of #1480; but I think the Instead of always or never checking the right hand side of a boolean logical operator depending on the option, the right-hand side should be checked only when it's in a "conditional context": // Non-conditional contexts - y isn't checked.
const a = x && y;
return x && y;
// Conditional contexts - y is checked
if(x && y) {}
const b = x && y && z;
// Special case (#1038)
[1,2,3].filter(x => x && y) |
agree! example of flow's sketchy-null check doing the same contextual check: /* @flow */
// flowlint sketchy-null:error
declare var x: ?string;
if (x) {} // Error: Sketchy null check on string [1] which is potentially an empty string. Perhaps you meant to check for null or undefined [2]?
const y = true && x; // no error
if (true && x) {} // Error: Sketchy null check on string [1] which is potentially an empty string. Perhaps you meant to check for null or undefined [2]? |
So the logic should be:
In any case, the logical expression itself should not be checked, only it's operands. Is that correct? |
I was wondering if options like allow number/string should work in the left-hand side of logical expressions in a non-conditional context. If they do then it makes a foot gun which is very common in JSX for example: <div>{quantity && <p>You have {quantity} items</p>}</div> If we don't explicitly compare One way to solve this would be to specify the context for every "allow type" option, then the defaults would be: {
"allowNumber": "conditional",
"allowString": "conditional",
"allowNullableObject": "always", // this is pretty safe even in JSX
"allowNullableBoolean": "never",
...
} Or alternatively hardcode the string/number options to not work in non-conditional context. That would disallow |
I think your description sounds mostly right, though I'm not sure about "recursing to the outermost operands": just to hopefully be clear here's a sketch of what the code in function checkNode(node) {
if(node.type === AST_NODE.LogicalExpression) {
// only the rhs needs to be checked, the lhs is already checked
return checkNode(node.right);
}
// report error if node is (unnecessary condition)/(non-strict boolean)
}
function checkLogicalExpression(node) {
// only check the left when logical expression AST node is encountered
checkNode(node.left);
}
return {
IfStatement: (node) => checkNode(node.test),
ForStatement: (node) => checkNode(node.test),
LogicalExpression: checkLogicalExpression,
WhileStatement: (node) => checkNode(node.test),
}; FWIW, I'm willing to implement this logic - might be easier for me, and would help the implementation to be consistent with I don't think I understand the most recent comment, about "allowing number/string in the left-hand-side in a non-conditional context"; because in my usage of the term, the left hand side is always a conditional context. |
An additional suggestion in #1785 literal string/number types |
Can we do this after 3.0 in a separate PR? It wouldn't be a breaking change. |
In regards to treating literal types as safe, the way I see it, even falsey literal types are safe, as they have a distinct truthiness value. In other words, To illustrate, take if I have a variable |
New here and having a wee bit of a hard time following all the nuances here. I'm coming from a .NET background, new-ish to TypeScript. What I think I want the linter to do is:
e.g. Is that a reasonable point of view? Would that be supported in the new options for Also, I'm beginning to see that TypeScript's |
I've summarised all of this discussion in the top comment: Short answer is yes. |
Really if you're checking nullishness or undefinedness you'd do: if (foo == null) {
throw new Error("foo is null or undefined")
} The double equals checks against both |
Original comment by @phaux in #1480
Here are the cases that I encounter often and could have an option to allow them:
boolean
– always allowedboolean | null | undefined
aka optional/nullable boolean – developers often want to treat the nullish case the same as false instead of explicitly sayingvalue ?? false
orvalue == null ? false : value
allowNullable
string | number
– developers often want to test for zero/empty string without explicit comparisonvalue != 0
etc.allowPrimitive
or somethingobject | function | null | undefined
aka nullable object/function – developers often check against the nullish case without explicitly comparing to null. I've met a lot of people that don't even know thatvalue == null
also checks for undefined.allowSafe
if we can fix it, orallowNullableObject
or somethingWe've learned a lot about this rule and how people want to use it. We should look into reworking the options so that it matches how people want to use the rule.
The text was updated successfully, but these errors were encountered: