Skip to content

Commit 01f9966

Browse files
feat(no-misused-observables): interface declarations
1 parent 9882bd9 commit 01f9966

File tree

3 files changed

+193
-1
lines changed

3 files changed

+193
-1
lines changed

src/etc/get-type-services.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ export function getTypeServices<
4444
|| ts.isMethodSignature(tsNode)
4545
) {
4646
tsTypeNode = tsNode.type;
47+
} else if (
48+
ts.isPropertySignature(tsNode)
49+
) {
50+
// TODO(#66): this doesn't work for functions assigned to class properties, variables, params.
4751
}
4852
return Boolean(
4953
tsTypeNode

src/rules/no-misused-observables.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export const noMisusedObservablesRule = ruleCreator({
5353
JSXAttribute: checkJSXAttribute,
5454
ClassDeclaration: checkClassLikeOrInterfaceNode,
5555
ClassExpression: checkClassLikeOrInterfaceNode,
56-
// TSInterfaceDeclaration: checkClassLikeOrInterfaceNode,
56+
TSInterfaceDeclaration: checkClassLikeOrInterfaceNode,
5757
// Property: checkProperty,
5858
// ReturnStatement: checkReturnStatement,
5959
// AssignmentExpression: checkAssignment,

tests/rules/no-misused-observables.test.ts

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ ruleTester({ types: true }).run('no-misused-observables', noMisusedObservablesRu
8484
const Baz = class extends Foo {
8585
foo(): Observable<number> { return of(42); }
8686
}
87+
88+
interface Qux extends Foo {
89+
foo(): Observable<number>;
90+
}
8791
`,
8892
options: [{ checksVoidReturn: false }],
8993
},
@@ -375,6 +379,190 @@ ruleTester({ types: true }).run('no-misused-observables', noMisusedObservablesRu
375379
}
376380
`,
377381
),
382+
fromFixture(
383+
stripIndent`
384+
// void return inherited method; interface; extends class
385+
import { Observable } from "rxjs";
386+
387+
class Foo {
388+
foo(): void {}
389+
}
390+
391+
interface Bar extends Foo {
392+
foo(): Observable<number>;
393+
~~~~~~~~~~~~~~~~~~~~~~~~~~ [forbiddenVoidReturnInheritedMethod { "heritageTypeName": "Foo" }]
394+
}
395+
`,
396+
),
397+
fromFixture(
398+
stripIndent`
399+
// void return inherited method; interface; extends abstract
400+
import { Observable } from "rxjs";
401+
402+
abstract class Foo {
403+
abstract foo(): void;
404+
}
405+
406+
interface Bar extends Foo {
407+
foo(): Observable<number>;
408+
~~~~~~~~~~~~~~~~~~~~~~~~~~ [forbiddenVoidReturnInheritedMethod { "heritageTypeName": "Foo" }]
409+
}
410+
`,
411+
),
412+
fromFixture(
413+
stripIndent`
414+
// void return inherited method; interface; extends interface
415+
import { Observable } from "rxjs";
416+
417+
interface Foo {
418+
foo(): void;
419+
}
420+
421+
interface Bar extends Foo {
422+
foo(): Observable<number>;
423+
~~~~~~~~~~~~~~~~~~~~~~~~~~ [forbiddenVoidReturnInheritedMethod { "heritageTypeName": "Foo" }]
424+
}
425+
`,
426+
),
427+
fromFixture(
428+
stripIndent`
429+
// void return inherited method; interface; extends conditional type
430+
import { Observable } from "rxjs";
431+
432+
type Foo<IsRx extends boolean = true> = IsRx extends true
433+
? { foo(): Observable<void> }
434+
: { foo(): void };
435+
436+
interface Bar extends Foo<false> {
437+
foo(): Observable<void>;
438+
~~~~~~~~~~~~~~~~~~~~~~~~ [forbiddenVoidReturnInheritedMethod { "heritageTypeName": "{ foo(): void; }" }]
439+
}
440+
`,
441+
),
442+
fromFixture(
443+
stripIndent`
444+
// void return inherited method; interface; extends multiple
445+
import { Observable } from "rxjs";
446+
447+
interface Foo {
448+
foo(): void;
449+
}
450+
451+
interface Bar {
452+
foo(): void;
453+
}
454+
455+
interface Baz extends Foo, Bar {
456+
foo(): Observable<void>;
457+
~~~~~~~~~~~~~~~~~~~~~~~~ [forbiddenVoidReturnInheritedMethod { "heritageTypeName": "Foo" }]
458+
~~~~~~~~~~~~~~~~~~~~~~~~ [forbiddenVoidReturnInheritedMethod { "heritageTypeName": "Bar" }]
459+
}
460+
`,
461+
),
462+
fromFixture(
463+
stripIndent`
464+
// void return inherited method; interface; extends multiple classes
465+
import { Observable } from "rxjs";
466+
467+
class Foo {
468+
foo(): void {}
469+
}
470+
471+
class Bar {
472+
foo(): void {}
473+
}
474+
475+
interface Baz extends Foo, Bar {
476+
foo(): Observable<void>;
477+
~~~~~~~~~~~~~~~~~~~~~~~~ [forbiddenVoidReturnInheritedMethod { "heritageTypeName": "Foo" }]
478+
~~~~~~~~~~~~~~~~~~~~~~~~ [forbiddenVoidReturnInheritedMethod { "heritageTypeName": "Bar" }]
479+
}
480+
`,
481+
),
482+
fromFixture(
483+
stripIndent`
484+
// void return inherited method; interface; extends typeof class
485+
import { Observable } from "rxjs";
486+
487+
const Foo = class {
488+
foo(): void {}
489+
}
490+
491+
type Bar = typeof Foo;
492+
493+
interface Baz extends Bar {
494+
foo(): Observable<void>;
495+
~~~~~~~~~~~~~~~~~~~~~~~~ [forbiddenVoidReturnInheritedMethod { "heritageTypeName": "typeof Foo" }]
496+
}
497+
`,
498+
),
499+
fromFixture(
500+
stripIndent`
501+
// void return inherited method; interface; extends function, index, constructor
502+
import { Observable } from "rxjs";
503+
504+
interface Foo {
505+
(): void;
506+
(arg: string): void;
507+
new (): void;
508+
[key: string]: () => void;
509+
[key: number]: () => void;
510+
myMethod(): void;
511+
}
512+
513+
interface Bar extends Foo {
514+
(): Observable<void>;
515+
(arg: string): Observable<void>;
516+
new (): Observable<void>;
517+
[key: string]: () => Observable<void>;
518+
[key: number]: () => Observable<void>;
519+
myMethod(): Observable<void>;
520+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [forbiddenVoidReturnInheritedMethod { "heritageTypeName": "Foo" }]
521+
}
522+
`,
523+
),
524+
fromFixture(
525+
stripIndent`
526+
// void return inherited method; interface; extends multiple function, index, constructor
527+
import { Observable } from "rxjs";
528+
529+
interface Foo {
530+
(): void;
531+
(arg: string): void;
532+
}
533+
534+
interface Bar {
535+
[key: string]: () => void;
536+
[key: number]: () => void;
537+
}
538+
539+
interface Baz {
540+
new (): void;
541+
new (arg: string): void;
542+
}
543+
544+
interface Qux {
545+
doSyncThing(): void;
546+
doOtherSyncThing(): void;
547+
syncMethodProperty: () => void;
548+
}
549+
550+
interface Quux extends Foo, Bar, Baz, Qux {
551+
(): void;
552+
(arg: string): Observable<void>;
553+
new (): void;
554+
new (arg: string): void;
555+
[key: string]: () => Observable<void>;
556+
[key: number]: () => void;
557+
doSyncThing(): Observable<void>;
558+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [forbiddenVoidReturnInheritedMethod { "heritageTypeName": "Qux" }]
559+
doRxThing(): Observable<void>;
560+
syncMethodProperty: () => Observable<void>;
561+
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [forbiddenVoidReturnInheritedMethod { "heritageTypeName": "Qux" }]
562+
// TODO(#66): couldReturnType doesn't work for properties.
563+
}
564+
`,
565+
),
378566
// #endregion invalid; void return inherited method
379567
// #region invalid; spread
380568
fromFixture(

0 commit comments

Comments
 (0)