no-useless-predicate
January 13, 2021 ยท View on GitHub
:mag: requires type information
Detects redundant conditions that are either always true or always false.
Rationale
This rule consists of 5 distinct checks to detect conditions that are always truthy or always falsy:
- Expressions used as condition are checked for being always truthy or always falsy. TypeScript doesn't even try to detect if a condition always results in the same branch being chosen. This check detects when you forget to call a method or await a Promise, for example
if (checkAuthentication) {}instead ofif (checkAuthentication()) {}. Not usingstrictNullChecksmakes this check almost useless. - Comparing non-nullable values with
nullorundefined. TypeScript already detects invalid comparisons. However, it always allows comparing withnullorundefinedeven if the type indicates that such a value can never occur. This check is disabled withoutstrictNullChecks. - Comparing
typeof vwith a type that can never occur. TypeScript only ensures you compare with a valid type. - Using
key in objwherekeyis known to be always present inobj. This check is disabled withoutstrictNullChecks. - Comparing a literal type with itself, e.g.
true === trueor'a' !== 'a'. This check is disabled withoutstrictNullChecks.
:warning: Limitations
Types like {toString(): string} could contain primitive values. Due to a lack of type relationship APIs this rule is currently unable to detect these cases and assumes such types to be always truthy and typeof to return "object".
This rule works best when noUncheckIndexedAccess compilerOption is enabled, otherwise TypeScript doesn't include undefined in the type of index signatures (see Microsoft/TypeScript#13778). This rule doesn't know whether a certain value came from an index signature. There's some special handling to treat property access in conditions as potentially undefined. Unfortunately there's no reliable way to do the same if the value is assigned to an intermediate variable before using it in a condition:
declare let arr: Array<Date>;
typeof arr[0] === 'object'; // correctly detected as possibly undefined
arr[0] === undefined; // correctly detected as possibly undefined
const v0 = arr[0];
v0 === undefined; // false positive: the same check as above using a variable doesn't work
Examples
:thumbsdown: Examples of incorrect code
declare function returnTrue(): true;
while (returnTrue()) {} // condition is always truthy
!returnTrue() ? 'false' : 'true'; // condition is always falsy
declare function checkCondition(): boolean;
if (checkCondition) {} // condition is always truthy, forgot to call the function?
declare function isAuthenticated(): Promise<boolean>;
if (isAuthenticated()) {} // condition is always truthy, forgot to 'await' the Promise
declare var v: string | null;
v === undefined; // always falsy
typeof v === 'number'; // always falsy
declare var arr: string[];
'length' in arr; // property 'length' is always present on arrays
returnTrue() === true; // always truthy
:thumbsup: Examples of correct code
while (true) {} // infinite loops with 'true' as condition are always allowed
declare function checkCondition(): boolean;
if (checkCondition()) {} // can be truthy or falsy
declare function isAuthenticated(): Promise<boolean>;
async function fn() {
if (await isAuthenticated()) {} // 'await' your Promises
}
declare var v: string | null;
v === null;
v == undefined; // '== undefined' also includes 'null'
typeof v === 'object'; // typeof null === 'object'
declare var arr: string[];
1 in arr; // testing for existence of an index in the array
'foo' in arr; // property 'foo' could theoretically be present at runtime
Boolean() === true;