Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
fix(eslint-plugin): [embt] fix allowTypedFunctionExpressions (#1553)
  • Loading branch information
bradzacher committed Feb 2, 2020
1 parent 45ae0b9 commit 9e7d161
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 96 deletions.
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,
};

0 comments on commit 9e7d161

Please sign in to comment.