diff --git a/src/rules/no-misused-observables.ts b/src/rules/no-misused-observables.ts
index 9c7a9225..50303c9e 100644
--- a/src/rules/no-misused-observables.ts
+++ b/src/rules/no-misused-observables.ts
@@ -180,11 +180,21 @@ export const noMisusedObservablesRule = ruleCreator({
}
function checkJSXAttribute(node: es.JSXAttribute): void {
- if (!node.value || !isJSXExpressionContainer(node.value)) {
+ if (
+ node.value == null
+ || !isJSXExpressionContainer(node.value)
+ ) {
return;
}
-
- if (couldReturnObservable(node.value.expression)) {
+ const expressionContainer = esTreeNodeToTSNodeMap.get(
+ node.value,
+ );
+ const contextualType = checker.getContextualType(expressionContainer);
+ if (
+ contextualType != null
+ && isVoidReturningFunctionType(contextualType)
+ && couldReturnObservable(node.value.expression)
+ ) {
context.report({
messageId: 'forbiddenVoidReturnAttribute',
node: node.value,
@@ -336,8 +346,8 @@ export const noMisusedObservablesRule = ruleCreator({
const tsNode = esTreeNodeToTSNodeMap.get(node);
if (
tsNode.initializer == null
- || !node.init
- || !node.id.typeAnnotation
+ || node.init == null
+ || node.id.typeAnnotation == null
) {
return;
}
diff --git a/tests/rules/no-misused-observables.test.ts b/tests/rules/no-misused-observables.test.ts
index 23737a32..d0f06da1 100644
--- a/tests/rules/no-misused-observables.test.ts
+++ b/tests/rules/no-misused-observables.test.ts
@@ -49,30 +49,43 @@ ruleTester({ types: true }).run('no-misused-observables', noMisusedObservablesRu
{
code: stripIndent`
// void return attribute; explicitly allowed
- import { Observable, of } from "rxjs";
- import React, { FC } from "react";
+ import { of } from "rxjs";
- const Component: FC<{ foo: () => void }> = () =>
;
- const App = () => {
- return (
- of(42)} />
- );
- };
+ interface Props {
+ foo: () => void;
+ }
+ declare function Component(props: Props): any;
+
+ const _ = of(42)} />;
`,
options: [{ checksVoidReturn: { attributes: false } }],
languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } },
},
+ {
+ code: stripIndent`
+ // void return attribute; not void
+ import { Observable, of } from "rxjs";
+
+ interface Props {
+ foo: () => Observable;
+ }
+ declare function Component(props: Props): any;
+
+ const _ = of(42)} />;
+ `,
+ languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } },
+ },
{
code: stripIndent`
// void return attribute; unrelated
- import React, { FC } from "react";
- const Component: FC<{ foo: () => void, bar: boolean }> = () => ;
- const App = () => {
- return (
- 42} bar />
- );
- };
+ interface Props {
+ foo: () => void;
+ bar: boolean;
+ }
+ declare function Component(props: Props): any;
+
+ const _ = 42} bar />;
`,
languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } },
},
@@ -339,15 +352,14 @@ ruleTester({ types: true }).run('no-misused-observables', noMisusedObservablesRu
stripIndent`
// void return attribute; block body
import { Observable, of } from "rxjs";
- import React, { FC } from "react";
-
- const Component: FC<{ foo: () => void }> = () => ;
- const App = () => {
- return (
- => { return of(42); }} />
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [forbiddenVoidReturnAttribute]
- );
- };
+
+ interface Props {
+ foo: () => void;
+ }
+ declare function Component(props: Props): any;
+
+ const _ = => { return of(42); }} />;
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [forbiddenVoidReturnAttribute]
`,
{
languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } },
@@ -357,15 +369,14 @@ ruleTester({ types: true }).run('no-misused-observables', noMisusedObservablesRu
stripIndent`
// void return attribute; inline body
import { Observable, of } from "rxjs";
- import React, { FC } from "react";
-
- const Component: FC<{ foo: () => void }> = () => ;
- const App = () => {
- return (
- of(42)} />
- ~~~~~~~~~~~~~~ [forbiddenVoidReturnAttribute]
- );
- };
+
+ interface Props {
+ foo: () => void;
+ }
+ declare function Component(props: Props): any;
+
+ const _ = of(42)} />;
+ ~~~~~~~~~~~~~~ [forbiddenVoidReturnAttribute]
`,
{
languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } },