Skip to content

Commit d19e512

Browse files
bradzacherJamesHenry
authored andcommitted
fix(eslint-plugin): explicit-func-return-type: support object types and as expressions (#459)
1 parent 4318a8b commit d19e512

File tree

3 files changed

+121
-7
lines changed

3 files changed

+121
-7
lines changed

packages/eslint-plugin/docs/rules/explicit-function-return-type.md

+17
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,23 @@ let arrowFn: FuncType = () => 'test';
9898
let funcExpr: FuncType = function() {
9999
return 'test';
100100
};
101+
102+
let asTyped = (() => '') as () => string;
103+
104+
interface ObjectType {
105+
foo(): number;
106+
}
107+
let objectProp: ObjectType = {
108+
foo: () => 1,
109+
};
110+
111+
interface ObjectType {
112+
foo(): number;
113+
}
114+
115+
let asObjectProp = {
116+
foo: () => 1,
117+
} as ObjectType;
101118
```
102119

103120
## When Not To Use It

packages/eslint-plugin/src/rules/explicit-function-return-type.ts

+37-7
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ export default util.createRule<Options, MessageIds>({
5757

5858
/**
5959
* Checks if a node is a setter.
60-
* @param parent The node to check
6160
*/
6261
function isSetter(node: TSESTree.Node): boolean {
6362
return (
@@ -67,7 +66,6 @@ export default util.createRule<Options, MessageIds>({
6766

6867
/**
6968
* Checks if a node is a variable declarator with a type annotation.
70-
* @param node The node to check
7169
*/
7270
function isVariableDeclaratorWithTypeAnnotation(
7371
node: TSESTree.Node,
@@ -78,6 +76,38 @@ export default util.createRule<Options, MessageIds>({
7876
);
7977
}
8078

79+
/**
80+
* Checks if a node belongs to:
81+
* const x: Foo = { prop: () => {} }
82+
*/
83+
function isPropertyOfObjectVariableDeclaratorWithTypeAnnotation(
84+
node: TSESTree.Node,
85+
): boolean {
86+
let parent = node.parent;
87+
if (!parent || parent.type !== AST_NODE_TYPES.Property) {
88+
return false;
89+
}
90+
parent = parent.parent;
91+
if (!parent || parent.type !== AST_NODE_TYPES.ObjectExpression) {
92+
return false;
93+
}
94+
parent = parent.parent;
95+
return !!parent && isVariableDeclaratorWithTypeAnnotation(parent);
96+
}
97+
98+
function isPropertyOfObjectInAsExpression(node: TSESTree.Node): boolean {
99+
let parent = node.parent;
100+
if (!parent || parent.type !== AST_NODE_TYPES.Property) {
101+
return false;
102+
}
103+
parent = parent.parent;
104+
if (!parent || parent.type !== AST_NODE_TYPES.ObjectExpression) {
105+
return false;
106+
}
107+
parent = parent.parent;
108+
return !!parent && parent.type === AST_NODE_TYPES.TSAsExpression;
109+
}
110+
81111
/**
82112
* Checks if a function declaration/expression has a return type.
83113
* @param node The node representing a function.
@@ -117,15 +147,15 @@ export default util.createRule<Options, MessageIds>({
117147
* @param {ASTNode} node The node representing a function.
118148
*/
119149
function checkFunctionExpressionReturnType(
120-
node:
121-
| TSESTree.ArrowFunctionExpression
122-
| TSESTree.FunctionDeclaration
123-
| TSESTree.FunctionExpression,
150+
node: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression,
124151
): void {
125152
if (
126153
options.allowTypedFunctionExpressions &&
127154
node.parent &&
128-
isVariableDeclaratorWithTypeAnnotation(node.parent)
155+
(isVariableDeclaratorWithTypeAnnotation(node.parent) ||
156+
isPropertyOfObjectVariableDeclaratorWithTypeAnnotation(node) ||
157+
node.parent.type === AST_NODE_TYPES.TSAsExpression ||
158+
isPropertyOfObjectInAsExpression(node))
129159
) {
130160
return;
131161
}

packages/eslint-plugin/tests/rules/explicit-function-return-type.test.ts

+67
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,29 @@ var funcExpr: Foo = function() { return 'test'; };
120120
},
121121
],
122122
},
123+
{
124+
filename: 'test.ts',
125+
code: `const x = (() => {}) as Foo`,
126+
options: [{ allowTypedFunctionExpressions: true }],
127+
},
128+
{
129+
filename: 'test.ts',
130+
code: `
131+
const x = {
132+
foo: () => {},
133+
} as Foo
134+
`,
135+
options: [{ allowTypedFunctionExpressions: true }],
136+
},
137+
{
138+
filename: 'test.ts',
139+
code: `
140+
const x: Foo = {
141+
foo: () => {},
142+
}
143+
`,
144+
options: [{ allowTypedFunctionExpressions: true }],
145+
},
123146
],
124147
invalid: [
125148
{
@@ -260,5 +283,49 @@ class Test {
260283
},
261284
],
262285
},
286+
287+
{
288+
filename: 'test.ts',
289+
code: `const x = (() => {}) as Foo`,
290+
options: [{ allowTypedFunctionExpressions: false }],
291+
errors: [
292+
{
293+
messageId: 'missingReturnType',
294+
line: 1,
295+
},
296+
],
297+
},
298+
{
299+
filename: 'test.ts',
300+
code: `
301+
interface Foo {}
302+
const x = {
303+
foo: () => {},
304+
} as Foo
305+
`,
306+
options: [{ allowTypedFunctionExpressions: false }],
307+
errors: [
308+
{
309+
messageId: 'missingReturnType',
310+
line: 4,
311+
},
312+
],
313+
},
314+
{
315+
filename: 'test.ts',
316+
code: `
317+
interface Foo {}
318+
const x: Foo = {
319+
foo: () => {},
320+
}
321+
`,
322+
options: [{ allowTypedFunctionExpressions: false }],
323+
errors: [
324+
{
325+
messageId: 'missingReturnType',
326+
line: 4,
327+
},
328+
],
329+
},
263330
],
264331
});

0 commit comments

Comments
 (0)