Skip to content

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

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

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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 52 additions & 13 deletions packages/eslint-plugin/docs/rules/explicit-module-boundary-types.md
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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,
};
Loading