Skip to content

Commit

Permalink
feat(eslint-plugin): add no-non-null-asserted-nullish-coalescing rule
Browse files Browse the repository at this point in the history
fixes #2853
  • Loading branch information
sonallux committed May 4, 2021
1 parent 1c1b572 commit 6e00a08
Show file tree
Hide file tree
Showing 6 changed files with 302 additions and 82 deletions.
165 changes: 83 additions & 82 deletions packages/eslint-plugin/README.md

Large diffs are not rendered by default.

@@ -0,0 +1,38 @@
# Disallows using a non-null assertion in the left operand of the nullish coalescing operator (`no-non-null-asserted-nullish-coalescing`)

## Rule Details

The nullish coalescing operator is designed to provide a default value when dealing with `null` or `undefined`.
Using non-null assertions in the left operand of the nullish coalescing operator is redundant.

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

```ts
/* eslint @typescript-eslint/no-non-null-asserted-nullish-coalescing: "error" */

foo! ?? bar;
foo.bazz! ?? bar;
foo!.bazz! ?? bar;
foo()! ?? bar;
```

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

```ts
/* eslint @typescript-eslint/no-non-null-asserted-nullish-coalescing: "error" */

foo ?? bar;
foo ?? bar!;
foo!.bazz ?? bar;
foo!.bazz ?? bar!;
foo() ?? bar;
```

## When Not To Use It

If you are not using TypeScript 3.7 (or greater), then you will not need to use this rule, as the operator is not supported.

## Further Reading

- [TypeScript 3.7 Release Notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html)
- [Nullish Coalescing Proposal](https://github.com/tc39/proposal-nullish-coalescing)
1 change: 1 addition & 0 deletions packages/eslint-plugin/src/configs/all.ts
Expand Up @@ -81,6 +81,7 @@ export = {
'@typescript-eslint/no-misused-new': 'error',
'@typescript-eslint/no-misused-promises': 'error',
'@typescript-eslint/no-namespace': 'error',
'@typescript-eslint/no-non-null-asserted-nullish-coalescing': 'error',
'@typescript-eslint/no-non-null-asserted-optional-chain': 'error',
'@typescript-eslint/no-non-null-assertion': 'error',
'@typescript-eslint/no-parameter-properties': 'error',
Expand Down
2 changes: 2 additions & 0 deletions packages/eslint-plugin/src/rules/index.ts
Expand Up @@ -53,6 +53,7 @@ import noMagicNumbers from './no-magic-numbers';
import noMisusedNew from './no-misused-new';
import noMisusedPromises from './no-misused-promises';
import noNamespace from './no-namespace';
import noNonNullAssertedNullishCoalescing from './no-non-null-asserted-nullish-coalescing';
import noNonNullAssertedOptionalChain from './no-non-null-asserted-optional-chain';
import noNonNullAssertion from './no-non-null-assertion';
import noParameterProperties from './no-parameter-properties';
Expand Down Expand Up @@ -171,6 +172,7 @@ export default {
'no-misused-new': noMisusedNew,
'no-misused-promises': noMisusedPromises,
'no-namespace': noNamespace,
'no-non-null-asserted-nullish-coalescing': noNonNullAssertedNullishCoalescing,
'no-non-null-asserted-optional-chain': noNonNullAssertedOptionalChain,
'no-non-null-assertion': noNonNullAssertion,
'no-parameter-properties': noParameterProperties,
Expand Down
@@ -0,0 +1,44 @@
import { TSESTree, TSESLint } from '@typescript-eslint/experimental-utils';
import * as util from '../util';

export default util.createRule({
name: 'no-non-null-asserted-nullish-coalescing',
meta: {
type: 'problem',
docs: {
description:
'Disallows using a non-null assertion in the left operand of the nullish coalescing operator',
category: 'Possible Errors',
recommended: false,
suggestion: true,
},
messages: {
noNonNullAssertedNullishCoalescing:
'The nullish coalescing operator is designed to handle undefined and null - using a non-null assertion is not needed.',
suggestRemovingNonNull: 'You should remove the non-null assertion.',
},
schema: [],
},
defaultOptions: [],
create(context) {
return {
'LogicalExpression[operator = "??"] > TSNonNullExpression.left'(
node: TSESTree.TSNonNullExpression,
): void {
context.report({
node,
messageId: 'noNonNullAssertedNullishCoalescing',
// use a suggestion instead of a fixer, because this can obviously break type checks
suggest: [
{
messageId: 'suggestRemovingNonNull',
fix(fixer): TSESLint.RuleFix {
return fixer.removeRange([node.range[1] - 1, node.range[1]]);
},
},
],
});
},
};
},
});
@@ -0,0 +1,134 @@
import rule from '../../src/rules/no-non-null-asserted-nullish-coalescing';
import { RuleTester } from '../RuleTester';

const ruleTester = new RuleTester({
parser: '@typescript-eslint/parser',
});

ruleTester.run('no-non-null-asserted-nullish-coalescing', rule, {
valid: [
'foo ?? bar;',
'foo ?? bar!;',
'foo.bazz ?? bar;',
'foo.bazz ?? bar!;',
'foo!.bazz ?? bar;',
'foo!.bazz ?? bar!;',
'foo() ?? bar;',
'foo() ?? bar!;',
'(foo ?? bar)!;',
],
invalid: [
{
code: 'foo! ?? bar;',
errors: [
{
messageId: 'noNonNullAssertedNullishCoalescing',
suggestions: [
{
messageId: 'suggestRemovingNonNull',
output: 'foo ?? bar;',
},
],
},
],
},
{
code: 'foo! ?? bar!;',
errors: [
{
messageId: 'noNonNullAssertedNullishCoalescing',
suggestions: [
{
messageId: 'suggestRemovingNonNull',
output: 'foo ?? bar!;',
},
],
},
],
},
{
code: 'foo.bazz! ?? bar;',
errors: [
{
messageId: 'noNonNullAssertedNullishCoalescing',
suggestions: [
{
messageId: 'suggestRemovingNonNull',
output: 'foo.bazz ?? bar;',
},
],
},
],
},
{
code: 'foo.bazz! ?? bar!;',
errors: [
{
messageId: 'noNonNullAssertedNullishCoalescing',
suggestions: [
{
messageId: 'suggestRemovingNonNull',
output: 'foo.bazz ?? bar!;',
},
],
},
],
},
{
code: 'foo!.bazz! ?? bar;',
errors: [
{
messageId: 'noNonNullAssertedNullishCoalescing',
suggestions: [
{
messageId: 'suggestRemovingNonNull',
output: 'foo!.bazz ?? bar;',
},
],
},
],
},
{
code: 'foo!.bazz! ?? bar!;',
errors: [
{
messageId: 'noNonNullAssertedNullishCoalescing',
suggestions: [
{
messageId: 'suggestRemovingNonNull',
output: 'foo!.bazz ?? bar!;',
},
],
},
],
},
{
code: 'foo()! ?? bar;',
errors: [
{
messageId: 'noNonNullAssertedNullishCoalescing',
suggestions: [
{
messageId: 'suggestRemovingNonNull',
output: 'foo() ?? bar;',
},
],
},
],
},
{
code: 'foo()! ?? bar!;',
errors: [
{
messageId: 'noNonNullAssertedNullishCoalescing',
suggestions: [
{
messageId: 'suggestRemovingNonNull',
output: 'foo() ?? bar!;',
},
],
},
],
},
],
});

0 comments on commit 6e00a08

Please sign in to comment.