microsoft / microsoft/TypeScript

[bug] A method return type of 'this' is incorrectly converted to 'any' in declaration output

Aperta
#29,122 3 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Bug Domain: Declaration Emit
Lingua principale
Go
Stelle
111k
Fork
14.3k
Merge medio
1g 19h
PR unite (30g)
117

Descrizione

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:

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Direzione di ricerca

Riproduci il problema con TypeScript 3.3.0-dev.20181129 usando l’esempio fornito di IEventEmitter e makeEventEmitterClass, quindi confronta l’output delle dichiarazioni generato. Traccia la generazione delle dichiarazioni per i tipi restituiti inferiti dei metodi; il lavoro è completato quando i metodi generati conservano il tipo restituito IEventEmitter invece di any, con una copertura di regressione per questo caso.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
typescript
Ambito
compilers
Tipo di issue
Bug
Difficoltà
4/5
Tempo stimato
3-5 giorni
Stato di attività
Ferma
Chiarezza
Specificata chiaramente
Idoneità per principianti
30/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.