fix(core): ensure the type `T` of `EventEmitter<T>` can be inferred (#40644)

The `AsyncPipe.transform<T>(emitter)` method must infer the `T`
type from the `emitter` parameter. Since we changed the `AsyncPipe`
to expect a `Subscribable<T>` rather than `Observable<T>` the
`EventEmitter.subscribe()` method needs to have a tighter signature.
Otherwise TypeScript struggles to infer the type and ends up making
it `unknown`.

Fixes #40637

PR Close #40644
This commit is contained in:
Pete Bacon Darwin 2021-01-30 16:59:37 +00:00 committed by Misko Hevery
parent 0e152fae7a
commit 1579df243d
5 changed files with 48 additions and 23 deletions

View File

@ -314,7 +314,8 @@ export declare class ErrorHandler {
export declare interface EventEmitter<T> extends Subject<T> {
new (isAsync?: boolean): EventEmitter<T>;
emit(value?: T): void;
subscribe(generatorOrNext?: any, error?: any, complete?: any): Subscription;
subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): Subscription;
subscribe(observerOrNext?: any, error?: any, complete?: any): Subscription;
}
export declare const EventEmitter: {

View File

@ -129,6 +129,16 @@ import {SpyChangeDetectorRef} from '../spies';
});
});
describe('Subscribable', () => {
it('should infer the type from the subscribable', () => {
const ref = new SpyChangeDetectorRef() as any;
const pipe = new AsyncPipe(ref);
const emitter = new EventEmitter<{name: 'T'}>();
// The following line will fail to compile if the type cannot be inferred.
const name = pipe.transform(emitter)?.name;
});
});
describe('Promise', () => {
const message = {};
let pipe: AsyncPipe;

View File

@ -90,7 +90,10 @@ export const CUSTOM_ELEMENTS_SCHEMA: any = false;
export const NO_ERRORS_SCHEMA: any = false;
export class EventEmitter<T> {
subscribe(generatorOrNext?: any, error?: any, complete?: any): unknown {
subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void):
unknown;
subscribe(observerOrNext?: any, error?: any, complete?: any): unknown;
subscribe(observerOrNext?: any, error?: any, complete?: any): unknown {
return null;
}
}

View File

@ -97,7 +97,8 @@ export function angularCoreDts(): TestFile {
}
export declare class EventEmitter<T> {
subscribe(generatorOrNext?: any, error?: any, complete?: any): unknown;
subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): unknown;
subscribe(observerOrNext?: any, error?: any, complete?: any): unknown;
}
export declare type NgIterable<T> = Array<T> | Iterable<T>;

View File

@ -81,15 +81,25 @@ export interface EventEmitter<T> extends Subject<T> {
* @param value The value to emit.
*/
emit(value?: T): void;
/**
* Registers handlers for events emitted by this instance.
* @param generatorOrNext When supplied, a custom handler for emitted events.
* @param error When supplied, a custom handler for an error notification
* from this emitter.
* @param complete When supplied, a custom handler for a completion
* notification from this emitter.
* @param next When supplied, a custom handler for emitted events.
* @param error When supplied, a custom handler for an error notification from this emitter.
* @param complete When supplied, a custom handler for a completion notification from this
* emitter.
*/
subscribe(generatorOrNext?: any, error?: any, complete?: any): Subscription;
subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void):
Subscription;
/**
* Registers handlers for events emitted by this instance.
* @param observerOrNext When supplied, a custom handler for emitted events, or an observer
* object.
* @param error When supplied, a custom handler for an error notification from this emitter.
* @param complete When supplied, a custom handler for a completion notification from this
* emitter.
*/
subscribe(observerOrNext?: any, error?: any, complete?: any): Subscription;
}
class EventEmitter_ extends Subject<any> {
@ -104,38 +114,38 @@ class EventEmitter_ extends Subject<any> {
super.next(value);
}
subscribe(generatorOrNext?: any, error?: any, complete?: any): Subscription {
subscribe(observerOrNext?: any, error?: any, complete?: any): Subscription {
let schedulerFn: (t: any) => any;
let errorFn = (err: any): any => null;
let completeFn = (): any => null;
if (generatorOrNext && typeof generatorOrNext === 'object') {
if (observerOrNext && typeof observerOrNext === 'object') {
schedulerFn = this.__isAsync ? (value: any) => {
setTimeout(() => generatorOrNext.next(value));
setTimeout(() => observerOrNext.next(value));
} : (value: any) => {
generatorOrNext.next(value);
observerOrNext.next(value);
};
if (generatorOrNext.error) {
if (observerOrNext.error) {
errorFn = this.__isAsync ? (err) => {
setTimeout(() => generatorOrNext.error(err));
setTimeout(() => observerOrNext.error(err));
} : (err) => {
generatorOrNext.error(err);
observerOrNext.error(err);
};
}
if (generatorOrNext.complete) {
if (observerOrNext.complete) {
completeFn = this.__isAsync ? () => {
setTimeout(() => generatorOrNext.complete());
setTimeout(() => observerOrNext.complete());
} : () => {
generatorOrNext.complete();
observerOrNext.complete();
};
}
} else {
schedulerFn = this.__isAsync ? (value: any) => {
setTimeout(() => generatorOrNext(value));
setTimeout(() => observerOrNext(value));
} : (value: any) => {
generatorOrNext(value);
observerOrNext(value);
};
if (error) {
@ -157,8 +167,8 @@ class EventEmitter_ extends Subject<any> {
const sink = super.subscribe(schedulerFn, errorFn, completeFn);
if (generatorOrNext instanceof Subscription) {
generatorOrNext.add(sink);
if (observerOrNext instanceof Subscription) {
observerOrNext.add(sink);
}
return sink;