Skip to content

Commit

Permalink
feat(eslint-plugin): add default-param-last rule (#1418)
Browse files Browse the repository at this point in the history
  • Loading branch information
a-tarasyuk authored and bradzacher committed Jan 10, 2020
1 parent b139540 commit a37ff9f
Show file tree
Hide file tree
Showing 6 changed files with 640 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/eslint-plugin/README.md
Expand Up @@ -105,6 +105,7 @@ Pro Tip: For larger codebases you may want to consider splitting our linting int
| [`@typescript-eslint/class-name-casing`](./docs/rules/class-name-casing.md) | Require PascalCased class and interface names | :heavy_check_mark: | | |
| [`@typescript-eslint/consistent-type-assertions`](./docs/rules/consistent-type-assertions.md) | Enforces consistent usage of type assertions | :heavy_check_mark: | | |
| [`@typescript-eslint/consistent-type-definitions`](./docs/rules/consistent-type-definitions.md) | Consistent with type definition either `interface` or `type` | | :wrench: | |
| [`@typescript-eslint/default-param-last`](./docs/rules/default-param-last.md) | Enforce default parameters to be last | | | |
| [`@typescript-eslint/explicit-function-return-type`](./docs/rules/explicit-function-return-type.md) | Require explicit return types on functions and class methods | :heavy_check_mark: | | |
| [`@typescript-eslint/explicit-member-accessibility`](./docs/rules/explicit-member-accessibility.md) | Require explicit accessibility modifiers on class properties and methods | | | |
| [`@typescript-eslint/func-call-spacing`](./docs/rules/func-call-spacing.md) | Require or disallow spacing between function identifiers and their invocations | | :wrench: | |
Expand Down
29 changes: 29 additions & 0 deletions packages/eslint-plugin/docs/rules/default-param-last.md
@@ -0,0 +1,29 @@
# Enforce default parameters to be last (`default-param-last`)

## Rule Details

This rule enforces default or optional parameters to be the last of parameters.

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

```ts
/* eslint @typescript-eslint/default-param-last: ["error"] */

function f(a = 0, b: number) {}
function f(a: number, b = 0, c?: number) {}
function f(a: number, b = 0, c: number) {}
function f(a: number, b?: number, c: number) {}
```

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

```ts
/* eslint @typescript-eslint/default-param-last: ["error"] */

function f(a = 0) {}
function f(a: number, b = 0) {}
function f(a: number, b?: number) {}
function f(a: number, b?: number, c = 0) {}
```

<sup>Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/default-param-last.md)</sup>
1 change: 1 addition & 0 deletions packages/eslint-plugin/src/configs/all.json
Expand Up @@ -13,6 +13,7 @@
"@typescript-eslint/class-name-casing": "error",
"@typescript-eslint/consistent-type-assertions": "error",
"@typescript-eslint/consistent-type-definitions": "error",
"@typescript-eslint/default-param-last": "error",
"@typescript-eslint/explicit-function-return-type": "error",
"@typescript-eslint/explicit-member-accessibility": "error",
"func-call-spacing": "off",
Expand Down
76 changes: 76 additions & 0 deletions packages/eslint-plugin/src/rules/default-param-last.ts
@@ -0,0 +1,76 @@
import { createRule } from '../util';
import {
TSESTree,
AST_NODE_TYPES,
} from '@typescript-eslint/experimental-utils';

export default createRule({
name: 'default-param-last',
meta: {
type: 'suggestion',
docs: {
description: 'Enforce default parameters to be last',
category: 'Best Practices',
recommended: false,
},
schema: [],
messages: {
shouldBeLast: 'Default parameters should be last.',
},
},
defaultOptions: [],
create(context) {
/**
* checks if node is optional parameter
* @param node the node to be evaluated
* @private
*/
function isOptionalParam(node: TSESTree.Parameter): boolean {
return 'optional' in node && node.optional === true;
}

/**
* checks if node is plain parameter
* @param node the node to be evaluated
* @private
*/
function isPlainParam(node: TSESTree.Parameter): boolean {
return !(
node.type === AST_NODE_TYPES.AssignmentPattern ||
node.type === AST_NODE_TYPES.RestElement ||
isOptionalParam(node)
);
}

function checkDefaultParamLast(
node:
| TSESTree.ArrowFunctionExpression
| TSESTree.FunctionDeclaration
| TSESTree.FunctionExpression,
): void {
let hasSeenPlainParam = false;
for (let i = node.params.length - 1; i >= 0; i--) {
const param = node.params[i];

if (isPlainParam(param)) {
hasSeenPlainParam = true;
continue;
}

if (
hasSeenPlainParam &&
(isOptionalParam(param) ||
param.type === AST_NODE_TYPES.AssignmentPattern)
) {
context.report({ node: param, messageId: 'shouldBeLast' });
}
}
}

return {
ArrowFunctionExpression: checkDefaultParamLast,
FunctionDeclaration: checkDefaultParamLast,
FunctionExpression: checkDefaultParamLast,
};
},
});
2 changes: 2 additions & 0 deletions packages/eslint-plugin/src/rules/index.ts
Expand Up @@ -8,6 +8,7 @@ import camelcase from './camelcase';
import classNameCasing from './class-name-casing';
import consistentTypeAssertions from './consistent-type-assertions';
import consistentTypeDefinitions from './consistent-type-definitions';
import defaultParamLast from './default-param-last';
import explicitFunctionReturnType from './explicit-function-return-type';
import explicitMemberAccessibility from './explicit-member-accessibility';
import funcCallSpacing from './func-call-spacing';
Expand Down Expand Up @@ -87,6 +88,7 @@ export default {
'class-name-casing': classNameCasing,
'consistent-type-assertions': consistentTypeAssertions,
'consistent-type-definitions': consistentTypeDefinitions,
'default-param-last': defaultParamLast,
'explicit-function-return-type': explicitFunctionReturnType,
'explicit-member-accessibility': explicitMemberAccessibility,
'func-call-spacing': funcCallSpacing,
Expand Down

0 comments on commit a37ff9f

Please sign in to comment.