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

feat(eslint-plugin): add strict-type-predicates #738

Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/eslint-plugin/README.md
Expand Up @@ -167,6 +167,7 @@ Pro Tip: For larger codebases you may want to consider splitting our linting int
| [`@typescript-eslint/semi`](./docs/rules/semi.md) | Require or disallow semicolons instead of ASI | | :wrench: | |
| [`@typescript-eslint/space-before-function-paren`](./docs/rules/space-before-function-paren.md) | Enforces consistent spacing before function parenthesis | | :wrench: | |
| [`@typescript-eslint/strict-boolean-expressions`](./docs/rules/strict-boolean-expressions.md) | Restricts the types allowed in boolean expressions | | | :thought_balloon: |
| [`@typescript-eslint/strict-type-predicates`](./docs/rules/strict-type-predicates.md) | Disallow always true (or false) type predicates | | | :thought_balloon: |
| [`@typescript-eslint/triple-slash-reference`](./docs/rules/triple-slash-reference.md) | Sets preference level for triple slash directives versus ES6-style import declarations | :heavy_check_mark: | | |
| [`@typescript-eslint/type-annotation-spacing`](./docs/rules/type-annotation-spacing.md) | Require consistent spacing around type annotations | :heavy_check_mark: | :wrench: | |
| [`@typescript-eslint/typedef`](./docs/rules/typedef.md) | Requires type annotations to exist | | | |
Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-plugin/ROADMAP.md
Expand Up @@ -97,7 +97,7 @@ It lists all TSLint rules along side rules from the ESLint ecosystem that are th
| [`radix`] | 🌟 | [`radix`][radix] |
| [`restrict-plus-operands`] | ✅ | [`@typescript-eslint/restrict-plus-operands`] |
| [`strict-boolean-expressions`] | ✅ | [`@typescript-eslint/strict-boolean-expressions`] |
| [`strict-type-predicates`] | 🛑 | N/A |
| [`strict-type-predicates`] | | [`@typescript-eslint/strict-type-predicates`] |
| [`switch-default`] | 🌟 | [`default-case`][default-case] |
| [`triple-equals`] | 🌟 | [`eqeqeq`][eqeqeq] |
| [`typeof-compare`] | 🌟 | [`valid-typeof`][valid-typeof] |
Expand Down
56 changes: 56 additions & 0 deletions packages/eslint-plugin/docs/rules/strict-type-predicates.md
@@ -0,0 +1,56 @@
# Disallow always true (or false) type predicates (`strict-type-predicates`)

Warns for type predicates that are always true or always false. Works for
`typeof` comparisons to constants (e.g. `typeof foo === 'string'`), and equality
comparison to `null`/`undefined`. (TypeScript won’t let you compare `1 === 2`,
but it has an exception for `1 === undefined`.) Does not yet work for
`instanceof`. Does not warn for `if (x.y)` where `x.y` is always truthy. For
that, see [`strict-boolean-expressions`](./strict-boolean-expressions.md).

This rule requires `strictNullChecks` to be enabled.

Examples of **incorrect** code for this rule:

```ts
const numberOrNull: number | null = 0;
// Implicitly checks for `!== undefined`, which is always false.
if (numberOrNull != null) {
return;
}

const numberOrUndefined: number | undefined = 0;
// Implicitly checks for `!== null`, which is always false.
if (numberOrNull != undefined) {
return;
}

const number: number = 0;
// Always false.
if (typeof number === 'string') {
return;
}
```

Examples of **correct** code for this rule:

```ts
const numberOrNull: number | null = 0;
if (numberOrNull !== null) {
return;
}

const numberOrUndefined: number | undefined = 0;
if (numberOrNull !== undefined) {
return;
}

const number: number = 0;
// Always false.
if (typeof number === 'number') {
return;
}
Comment on lines +47 to +51
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't this be reported by the rule, because it's always true?

This should probably follow the above tests

const numberOrUndefined: number | undefined = 0;

Comment on lines +37 to +51
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit with these - just for correctness, typescript narrows the type of the const variables to just be number, because it knows they can't change, so they can't be undefined.

A good way around this is to either declare the variables:

declare const numberOrUndefined: number | undefined;
if (numberOrNull !== undefined) {
  return;
}

Or wrap your examples in a function:

function ex1(numberOrUndefined: number | undefined) {
  if (numberOrNull !== undefined) {
    return;
  }
}

```

## Related To

- TSLint: [strict-type-predicates](https://palantir.github.io/tslint/rules/strict-type-predicates)
1 change: 1 addition & 0 deletions packages/eslint-plugin/src/configs/all.json
Expand Up @@ -90,6 +90,7 @@
"space-before-function-paren": "off",
"@typescript-eslint/space-before-function-paren": "error",
"@typescript-eslint/strict-boolean-expressions": "error",
"@typescript-eslint/strict-type-predicates": "error",
"@typescript-eslint/triple-slash-reference": "error",
"@typescript-eslint/type-annotation-spacing": "error",
"@typescript-eslint/typedef": "error",
Expand Down
2 changes: 2 additions & 0 deletions packages/eslint-plugin/src/rules/index.ts
Expand Up @@ -70,6 +70,7 @@ import returnAwait from './return-await';
import semi from './semi';
import spaceBeforeFunctionParen from './space-before-function-paren';
import strictBooleanExpressions from './strict-boolean-expressions';
import strictTypePredicates from './strict-type-predicates';
import tripleSlashReference from './triple-slash-reference';
import typeAnnotationSpacing from './type-annotation-spacing';
import typedef from './typedef';
Expand Down Expand Up @@ -149,6 +150,7 @@ export default {
semi: semi,
'space-before-function-paren': spaceBeforeFunctionParen,
'strict-boolean-expressions': strictBooleanExpressions,
'strict-type-predicates': strictTypePredicates,
'triple-slash-reference': tripleSlashReference,
'type-annotation-spacing': typeAnnotationSpacing,
typedef: typedef,
Expand Down