microsoft / microsoft/TypeScript
[bug] A method return type of 'this' is incorrectly converted to 'any' in declaration output
Dieses Issue hat noch niemand übernommen.
- Vorherrschende Sprache
- Go
- Sterne
- 111k
- Forks
- 14.4k
- Ø Merge
- 1 T. 19 Std.
- Gemergte PRs (30 T.)
- 117
Beschreibung
TypeScript Version: ^3.3.0-dev.20181129
Code
import * as events from 'events'
// EventArgs is used to create the event payload arguments for methods like
// EventEmitter.emit. This makes it possible to require a payload argument if
// the type is not `undefined`, or to not require an event payload argument if
// the type is `undefined`.
type EventArgs<
EventTypes,
K extends keyof EventTypes
> = EventTypes[K] extends undefined ? [] : [EventTypes[K]]
// our own version of EventEmitter with type-safe events (because the
// @types/node definition makes all events type `any`).
export interface IEventEmitter<EventTypes> {
new (): IEventEmitter<EventTypes>
addListener<EventName extends keyof EventTypes>(
event: EventName,
listener: (...args: EventArgs<EventTypes, EventName>) => void,
): this
on<EventName extends keyof EventTypes>(
event: EventName,
listener: (...args: EventArgs<EventTypes, EventName>) => void,
): this
once<EventName extends keyof EventTypes>(
event: EventName,
listener: (...args: EventArgs<EventTypes, EventName>) => void,
): this
removeListener<EventName extends keyof EventTypes>(
event: EventName,
listener: (...args: EventArgs<EventTypes, EventName>) => void,
): this
removeAllListeners<EventName extends keyof EventTypes>(
event?: EventName,
): this
emit<EventName extends keyof EventTypes>(
event: EventName,
...args: EventArgs<EventTypes, EventName>
): boolean
eventNames<EventName extends keyof EventTypes>(): Array<EventName>
setMaxListeners(n: number): this
getMaxListeners(): number
listeners<EventName extends keyof EventTypes>(
event: EventName,
): (...args: EventArgs<EventTypes, EventName>) => void[]
listenerCount<EventName extends keyof EventTypes>(type: EventName): number
prependListener<EventName extends keyof EventTypes>(
event: EventName,
listener: (...args: EventArgs<EventTypes, EventName>) => void,
): this
prependOnceListener<EventName extends keyof EventTypes>(
event: EventName,
listener: (...args: EventArgs<EventTypes, EventName>) => void,
): this
}
// This creates an EventEmitter class that uses our type definition. The
// implementation still comes from Node's EventEmitter.
//
// eslint-disable-next-line typescript/explicit-function-return-type
export function makeEventEmitterClass<EventTypes>() {
// merge the IEventEmitter interface and EventEmitter as IEventEmitter
// implementation into one.
// XXX Is there a better way?
const EventEmitter = class
extends ((events.EventEmitter as unknown) as IEventEmitter<EventTypes>) {}
// implements IEventEmitter<EventTypes> {}
return EventEmitter
}
Expected behavior:
Declaration file should not use any for method return types that return this.
I expect output like the following:
declare type EventArgs<EventTypes, K extends keyof EventTypes> = EventTypes[K] extends undefined ? [] : [EventTypes[K]];
export interface IEventEmitter<EventTypes> {
new (): IEventEmitter<EventTypes>;
addListener<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): IEventEmitter<EventTypes>;
on<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): IEventEmitter<EventTypes>;
once<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): IEventEmitter<EventTypes>;
removeListener<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): IEventEmitter<EventTypes>;
removeAllListeners<EventName extends keyof EventTypes>(event?: EventName): IEventEmitter<EventTypes>;
emit<EventName extends keyof EventTypes>(event: EventName, ...args: EventArgs<EventTypes, EventName>): boolean;
eventNames<EventName extends keyof EventTypes>(): Array<EventName>;
setMaxListeners(n: number): IEventEmitter<EventTypes>;
getMaxListeners(): number;
listeners<EventName extends keyof EventTypes>(event: EventName): (...args: EventArgs<EventTypes, EventName>) => void[];
listenerCount<EventName extends keyof EventTypes>(type: EventName): number;
prependListener<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): IEventEmitter<EventTypes>;
prependOnceListener<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): IEventEmitter<EventTypes>;
}
export declare function makeEventEmitterClass<EventTypes>(): {
new (): {
new (): IEventEmitter<EventTypes>;
addListener<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): IEventEmitter<EventTypes>;
on<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): IEventEmitter<EventTypes>;
once<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): IEventEmitter<EventTypes>;
removeListener<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): IEventEmitter<EventTypes>;
removeAllListeners<EventName extends keyof EventTypes>(event?: EventName | undefined): IEventEmitter<EventTypes>;
emit<EventName extends keyof EventTypes>(event: EventName, ...args: EventArgs<EventTypes, EventName>): boolean;
eventNames<EventName extends keyof EventTypes>(): EventName[];
setMaxListeners(n: number): IEventEmitter<EventTypes>;
getMaxListeners(): number;
listeners<EventName extends keyof EventTypes>(event: EventName): (...args: EventArgs<EventTypes, EventName>) => void[];
listenerCount<EventName extends keyof EventTypes>(type: EventName): number;
prependListener<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): IEventEmitter<EventTypes>;
prependOnceListener<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): IEventEmitter<EventTypes>;
};
addListener<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): IEventEmitter<EventTypes>;
on<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): IEventEmitter<EventTypes>;
once<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): IEventEmitter<EventTypes>;
removeListener<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): IEventEmitter<EventTypes>;
removeAllListeners<EventName extends keyof EventTypes>(event?: EventName | undefined): IEventEmitter<EventTypes>;
emit<EventName extends keyof EventTypes>(event: EventName, ...args: EventArgs<EventTypes, EventName>): boolean;
eventNames<EventName extends keyof EventTypes>(): EventName[];
setMaxListeners(n: number): IEventEmitter<EventTypes>;
getMaxListeners(): number;
listeners<EventName extends keyof EventTypes>(event: EventName): (...args: EventArgs<EventTypes, EventName>) => void[];
listenerCount<EventName extends keyof EventTypes>(type: EventName): number;
prependListener<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): IEventEmitter<EventTypes>;
prependOnceListener<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): IEventEmitter<EventTypes>;
};
export {};
//# sourceMappingURL=index.d.ts.map
Actual behavior:
Example of wrong output:
declare type EventArgs<EventTypes, K extends keyof EventTypes> = EventTypes[K] extends undefined ? [] : [EventTypes[K]];
export interface IEventEmitter<EventTypes> {
new (): IEventEmitter<EventTypes>;
addListener<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): this;
on<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): this;
once<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): this;
removeListener<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): this;
removeAllListeners<EventName extends keyof EventTypes>(event?: EventName): this;
emit<EventName extends keyof EventTypes>(event: EventName, ...args: EventArgs<EventTypes, EventName>): boolean;
eventNames<EventName extends keyof EventTypes>(): Array<EventName>;
setMaxListeners(n: number): this;
getMaxListeners(): number;
listeners<EventName extends keyof EventTypes>(event: EventName): (...args: EventArgs<EventTypes, EventName>) => void[];
listenerCount<EventName extends keyof EventTypes>(type: EventName): number;
prependListener<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): this;
prependOnceListener<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): this;
}
export declare function makeEventEmitterClass<EventTypes>(): {
new (): {
new (): IEventEmitter<EventTypes>;
// RIGHT HERE, the next lines have 'any'
addListener<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): any;
on<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): any;
once<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): any;
removeListener<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): any;
removeAllListeners<EventName extends keyof EventTypes>(event?: EventName | undefined): any;
emit<EventName extends keyof EventTypes>(event: EventName, ...args: EventArgs<EventTypes, EventName>): boolean;
eventNames<EventName extends keyof EventTypes>(): EventName[];
setMaxListeners(n: number): any;
getMaxListeners(): number;
listeners<EventName extends keyof EventTypes>(event: EventName): (...args: EventArgs<EventTypes, EventName>) => void[];
listenerCount<EventName extends keyof EventTypes>(type: EventName): number;
prependListener<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): any;
prependOnceListener<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): any;
};
addListener<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): IEventEmitter<EventTypes>;
on<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): IEventEmitter<EventTypes>;
once<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): IEventEmitter<EventTypes>;
removeListener<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): IEventEmitter<EventTypes>;
removeAllListeners<EventName extends keyof EventTypes>(event?: EventName | undefined): IEventEmitter<EventTypes>;
emit<EventName extends keyof EventTypes>(event: EventName, ...args: EventArgs<EventTypes, EventName>): boolean;
eventNames<EventName extends keyof EventTypes>(): EventName[];
setMaxListeners(n: number): IEventEmitter<EventTypes>;
getMaxListeners(): number;
listeners<EventName extends keyof EventTypes>(event: EventName): (...args: EventArgs<EventTypes, EventName>) => void[];
listenerCount<EventName extends keyof EventTypes>(type: EventName): number;
prependListener<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): IEventEmitter<EventTypes>;
prependOnceListener<EventName extends keyof EventTypes>(event: EventName, listener: (...args: EventArgs<EventTypes, EventName>) => void): IEventEmitter<EventTypes>;
};
export {};
//# sourceMappingURL=index.d.ts.map
This caused chaining method calls in my app not to work.
Playground Link: N/A
Related Issues:
Beitragsleitfaden
Erste Schritte
- Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
- Forke das Repository und arbeite in einem Branch.
- Öffne einen Pull Request, der die Issue-Nummer nennt.
Rechercherichtung
Reproduziere das Problem mit TypeScript 3.3.0-dev.20181129 anhand des bereitgestellten IEventEmitter- und makeEventEmitterClass-Beispiels und vergleiche anschließend die erzeugte Declaration-Ausgabe. Verfolge die Declaration-Generierung für die inferierten Rückgabetypen der Methoden; als erledigt gilt die Aufgabe, wenn die generierten Methoden den Rückgabetyp IEventEmitter statt any beibehalten und eine Regression-Testabdeckung für diesen Fall vorhanden ist.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Bewertung
- Tech-Stack
- typescript
- Bereich
- compilers
- Issue-Typ
- Bug
- Schwierigkeit
- 4/5
- Geschätzter Aufwand
- 3-5 Tage
- Aktivitätsstatus
- Veraltet
- Klarheit
- Klar beschrieben
- Anfängerfreundlichkeit
- 30/100