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

fix(eslint-plugin): [embt] fix allowTypedFunctionExpressions #1553

Merged
merged 1 commit into from Feb 2, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
65 changes: 52 additions & 13 deletions packages/eslint-plugin/docs/rules/explicit-module-boundary-types.md
Expand Up @@ -65,13 +65,26 @@ The rule accepts an options object with the following properties:

```ts
type Options = {
// if true, type annotations are also allowed on the variable of a function expression rather than on the function directly
/**
* If true, type annotations are also allowed on the variable of a function expression
* rather than on the function arguments/return value directly.
*/
allowTypedFunctionExpressions?: boolean;
// if true, functions immediately returning another function expression will not be checked
/**
* If true, functions immediately returning another function expression will not
* require an explicit return value annotation.
* You must still type the parameters of the function.
*/
allowHigherOrderFunctions?: boolean;
// if true, body-less arrow functions are allowed to return an object as const
/**
* If true, body-less arrow functions that return an `as const` type assertion will not
* require an explicit return value annotation.
* You must still type the parameters of the function.
*/
allowDirectConstAssertionInArrowFunctions?: boolean;
// an array of function/method names that will not be checked
/**
* An array of function/method names that will not have their arguments or their return values checked.
*/
allowedNames?: string[];
};

Expand Down Expand Up @@ -118,6 +131,8 @@ export let funcExpr = function() {
export let objectProp = {
foo: () => 1,
};

export const foo = bar => {};
```

Examples of additional **correct** code for this rule with `{ allowTypedFunctionExpressions: true }`:
Expand Down Expand Up @@ -146,6 +161,9 @@ export let objectPropAs = {
export let objectPropCast = <ObjectType>{
foo: () => 1,
};

type FooType = (bar: string) => void;
export const foo: FooType = bar => {};
```

### `allowHigherOrderFunctions`
Expand All @@ -158,6 +176,10 @@ export var arrowFn = () => () => {};
export function fn() {
return function() {};
}

export function foo(outer) {
return function(inner): void {};
}
```

Examples of **correct** code for this rule with `{ allowHigherOrderFunctions: true }`:
Expand All @@ -168,17 +190,15 @@ export var arrowFn = () => (): void => {};
export function fn() {
return function(): void {};
}

export function foo(outer: string) {
return function(inner: string): void {};
}
```

### `allowDirectConstAssertionInArrowFunctions`

Examples of additional **correct** code for this rule with `{ allowDirectConstAssertionInArrowFunctions: true }`:

```ts
export const func = (value: number) => ({ type: 'X', value } as const);
```

Examples of additional **incorrect** code for this rule with `{ allowDirectConstAssertionInArrowFunctions: true }`:
Examples of **incorrect** code for this rule with `{ allowDirectConstAssertionInArrowFunctions: true }`:

```ts
export const func = (value: number) => ({ type: 'X', value });
Expand All @@ -187,15 +207,34 @@ export const foo = () => {
bar: true,
} as const;
};
export const bar = () => 1;
export const baz = arg => arg as const;
```

Examples of **correct** code for this rule with `{ allowDirectConstAssertionInArrowFunctions: true }`:

```ts
export const func = (value: number) => ({ type: 'X', value } as const);
export const foo = () =>
({
bar: true,
} as const);
export const bar = () => 1 as const;
export const baz = (arg: string) => arg as const;
```

### `allowedNames`

You may pass function/method names you would like this rule to ignore, like so:

```cjson
```json
{
"@typescript-eslint/explicit-module-boundary-types": ["error", { "allowedName": ["ignoredFunctionName", "ignoredMethodName"] }]
"@typescript-eslint/explicit-module-boundary-types": [
"error",
{
"allowedName": ["ignoredFunctionName", "ignoredMethodName"]
}
]
}
```

Expand Down
Expand Up @@ -6,6 +6,7 @@ import * as util from '../util';
import {
checkFunctionExpressionReturnType,
checkFunctionReturnType,
isTypedFunctionExpression,
} from '../util/explicitReturnTypeUtils';

type Options = [
Expand Down Expand Up @@ -178,7 +179,11 @@ export default util.createRule<Options, MessageIds>({
return;
}

if (isAllowedName(node.parent) || isUnexported(node)) {
if (
isAllowedName(node.parent) ||
isUnexported(node) ||
isTypedFunctionExpression(node, options)
) {
return;
}

Expand Down
64 changes: 39 additions & 25 deletions packages/eslint-plugin/src/util/explicitReturnTypeUtils.ts
Expand Up @@ -5,6 +5,7 @@ import {
AST_TOKEN_TYPES,
} from '@typescript-eslint/experimental-utils';
import { isTypeAssertion, isConstructor, isSetter } from './astUtils';
import { nullThrows, NullThrowsReasons } from './nullThrows';

type FunctionNode =
| TSESTree.ArrowFunctionExpression
Expand Down Expand Up @@ -264,6 +265,26 @@ function checkFunctionReturnType(
report(getReporLoc(node, sourceCode));
}

function isTypedFunctionExpression(
node: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression,
options: Options,
): boolean {
const parent = nullThrows(node.parent, NullThrowsReasons.MissingParent);

if (!options.allowTypedFunctionExpressions) {
return false;
}

return (
isTypeAssertion(parent) ||
isVariableDeclaratorWithTypeAnnotation(parent) ||
isClassPropertyWithTypeAnnotation(parent) ||
isPropertyOfObjectWithType(parent) ||
isFunctionArgument(parent, node) ||
isConstructorArgument(parent)
);
}

/**
* Checks if a function declaration/expression has a return type.
*/
Expand All @@ -273,36 +294,25 @@ function checkFunctionExpressionReturnType(
sourceCode: TSESLint.SourceCode,
report: (loc: TSESTree.SourceLocation) => void,
): void {
// Should always have a parent; checking just in case
/* istanbul ignore else */ if (node.parent) {
if (options.allowTypedFunctionExpressions) {
if (
isTypeAssertion(node.parent) ||
isVariableDeclaratorWithTypeAnnotation(node.parent) ||
isClassPropertyWithTypeAnnotation(node.parent) ||
isPropertyOfObjectWithType(node.parent) ||
isFunctionArgument(node.parent, node) ||
isConstructorArgument(node.parent)
) {
return;
}
}
if (isTypedFunctionExpression(node, options)) {
return;
}

if (
options.allowExpressions &&
node.parent.type !== AST_NODE_TYPES.VariableDeclarator &&
node.parent.type !== AST_NODE_TYPES.MethodDefinition &&
node.parent.type !== AST_NODE_TYPES.ExportDefaultDeclaration &&
node.parent.type !== AST_NODE_TYPES.ClassProperty
) {
return;
}
const parent = nullThrows(node.parent, NullThrowsReasons.MissingParent);
if (
options.allowExpressions &&
parent.type !== AST_NODE_TYPES.VariableDeclarator &&
parent.type !== AST_NODE_TYPES.MethodDefinition &&
parent.type !== AST_NODE_TYPES.ExportDefaultDeclaration &&
parent.type !== AST_NODE_TYPES.ClassProperty
) {
return;
}

// https://github.com/typescript-eslint/typescript-eslint/issues/653
if (
node.type === AST_NODE_TYPES.ArrowFunctionExpression &&
options.allowDirectConstAssertionInArrowFunctions &&
node.type === AST_NODE_TYPES.ArrowFunctionExpression &&
returnsConstAssertionDirectly(node)
) {
return;
Expand All @@ -311,4 +321,8 @@ function checkFunctionExpressionReturnType(
checkFunctionReturnType(node, options, sourceCode, report);
}

export { checkFunctionReturnType, checkFunctionExpressionReturnType };
export {
checkFunctionReturnType,
checkFunctionExpressionReturnType,
isTypedFunctionExpression,
};