diff --git a/src/operators/of-type.spec.ts b/src/operators/of-type.spec.ts index 7fccef68..0e3a322f 100644 --- a/src/operators/of-type.spec.ts +++ b/src/operators/of-type.spec.ts @@ -3,10 +3,17 @@ import { ofType } from './of-type'; import { IEvent } from '../interfaces'; describe('operators/ofType', () => { - class A implements IEvent {} - class B implements IEvent {} + class A implements IEvent { + keyAOrB: string = 'keyAOrB'; + keyAOnly: string = 'keyAOnly'; + } + class B implements IEvent { + keyAOrB: string = 'keyAOrB'; + } class SubA extends A {} - class C implements IEvent {} + class C implements IEvent { + keyCOptional?: string; + } let stream: Subject; let output: IEvent[]; @@ -32,7 +39,7 @@ describe('operators/ofType', () => { expectedResults.push(new A()); stream.next(new B()); - stream.next(...expectedResults); + expectedResults.forEach((event) => stream.next(event)); stream.next(new Date()); expect(output).toEqual(expectedResults); @@ -46,4 +53,35 @@ describe('operators/ofType', () => { expect(output).toEqual(expectedResults); }); + + // TypeScript TSC test, if this compiles it passes + it('should infer return types of similar unions unions', (done) => { + stream.pipe(ofType(A, B)).subscribe((event) => { + event.keyAOrB; + // @ts-expect-error -- A | B cannot reference a B only key + event.keyBOnly; + done(); + }); + + stream.next(new A()); + }); + + // TypeScript TSC test, if this compiles it passes + it('should infer return types of disparate unions', (done) => { + stream.pipe(ofType(A, C)).subscribe((event) => { + // @ts-expect-error -- A | C cannot reference a key that is not on C + event.keyAOnly; + + if (event instanceof A) { + event.keyAOnly; + } + + if (event instanceof C) { + event.keyCOptional; + } + done(); + }); + + stream.next(new A()); + }); }); diff --git a/src/operators/of-type.ts b/src/operators/of-type.ts index f8f937bb..20e1be6f 100644 --- a/src/operators/of-type.ts +++ b/src/operators/of-type.ts @@ -11,11 +11,13 @@ import { IEvent } from '../interfaces'; * * @return A stream only emitting the filtered instances. */ -export function ofType( - ...types: Type[] +export function ofType[]>( + ...types: T ) { - const isInstanceOf = (event: IEvent): event is TOutput => + type InstanceOf = T extends Type ? I : never; + const isInstanceOf = (event: IEvent): event is InstanceOf => !!types.find((classType) => event instanceof classType); - return (source: Observable): Observable => + + return (source: Observable): Observable> => source.pipe(filter(isInstanceOf)); }