TypeScript - Is there an option to disallow using "! in front of anything except a boolean?
Asked Answered
V

1

9

I understand this may be a classical javascript issue, but I too often found myself using :

if (!something) {
    //...
}

to validate that this something is not undefined or null, in TypeScript.

This is very error-prone! When used on a number "0" will match, and when used on an enum the first item will match too (by default, the first item has a value of "0")!

Is there a way to deal with this in TypeScript? Is there a way to configure TypeScript to disallow an exclamation mark in front of anything except a boolean (and any)? Would this configuration make sense or am I missing something trivial?

Should :

if (something === null || something === undefined) {
    //...
}

be used instead, to validate that something is defined or not? And is there a way to enforce this in a team?

Violette answered 30/3, 2017 at 14:51 Comment(4)
For some reason "classical javascript" makes me shudder. Personally, I'd consider typescript to be more "classical".Bloem
You could perhaps write a tslint rule for it.Neat
You could write a custom lint rule to prevent this. It wouldn't catch it until you run the linting task though unless you have it set to watch. At least you would get notified that it has happened though and then you can go fix it. We have our linting tasks setup to fail builds if it finds any errors. That way whoever wrote it is forced to fix it before they can merge.Neglect
palantir.github.io/tslint/usage/custom-rulesNeat
D
2

You can use the strict-boolean-expressions TSLint rule to disallow this sort of thing.

You can see some examples of the rule here, but this is an excerpt that's particularly relevant to your question. Places where the rule would find errors are marked with ~~~ and the error message is written next to that marking:

/*** PrefixUnary Expressions ***/
/*** Invalid ***/
!!numType;
  ~~~~~~~  [This type is not allowed in the operand for the '!' operator because it is a number. Only booleans are allowed.]
!strType;
 ~~~~~~~  [This type is not allowed in the operand for the '!' operator because it is a string. Only booleans are allowed.]
!objType;
 ~~~~~~~  [This type is not allowed in the operand for the '!' operator because it is always truthy. Only booleans are allowed.]
!enumType;
 ~~~~~~~~  [This type is not allowed in the operand for the '!' operator because it is an enum. Only booleans are allowed.]
!!classType;
  ~~~~~~~~~  [This type is not allowed in the operand for the '!' operator because it is always truthy. Only booleans are allowed.]
!bwrapType;
 ~~~~~~~~~  [This type is not allowed in the operand for the '!' operator because it is always truthy. Only booleans are allowed.]
!!undefined;
 ~~~~~~~~~~  [This type is not allowed in the operand for the '!' operator because it is always truthy. Only booleans are allowed.]
  ~~~~~~~~~  [This type is not allowed in the operand for the '!' operator because it is always falsy. Only booleans are allowed.]

/*** Valid ***/
!!boolFn();
!boolExpr;
!!boolType;

/*** If Statement ***/
/*** Invalid ***/
if (numType) { /* statements */ }
    ~~~~~~~                       [This type is not allowed in the 'if' condition because it is a number. Only booleans are allowed.]
if (objType) { /* statements */ }
    ~~~~~~~                       [This type is not allowed in the 'if' condition because it is always truthy. Only booleans are allowed.]
if (strType) { /* statements */ }
    ~~~~~~~                       [This type is not allowed in the 'if' condition because it is a string. Only booleans are allowed.]
if (bwrapType) { /* statements */ }
    ~~~~~~~~~                       [This type is not allowed in the 'if' condition because it is always truthy. Only booleans are allowed.]
if (strFn()) { /* statements */ }
    ~~~~~~~                       [This type is not allowed in the 'if' condition because it is a string. Only booleans are allowed.]
if (MyEnum.A) { /* statements */ }
    ~~~~~~~~                       [This type is not allowed in the 'if' condition because it is an enum. Only booleans are allowed.]
if (classType) { /* statements */ }
    ~~~~~~~~~                       [This type is not allowed in the 'if' condition because it is always truthy. Only booleans are allowed.]

To answer your other side question briefly, the below code snipped is a nice way to check if something is defined or not:

if (something == null) {
    // will enter here if `something === null || something === undefined`
}

See here for more details on the above

Diverticulitis answered 31/3, 2017 at 14:15 Comment(1)
This is clearly the answer, thanks. Sadly I would like this rule to work properly in VSCode, but since it requires type checking it seems it can only be run manually ( github.com/Microsoft/vscode-tslint/issues/70 ). But this is another story!Violette

© 2022 - 2024 — McMap. All rights reserved.