Расширение определений событий - интерфейс неправильно реализует интерфейс - PullRequest
3 голосов
/ 19 февраля 2020

Я пытаюсь применить определения событий в моем коде, используя интерфейс и класс с тем же именем, что и в следующем примере:

export declare interface ClientEvents {
    on(event: "event_name", data: (data: string) => void) : this;
    emit(event: "event_name", data: string) : boolean;
}

export class ClientEvents extends EventEmitter {}

Это хорошо работает само по себе для завершения IDE и обеспечения того, чтобы только эти события передаются объектом события.

Проблема возникает, когда я пытаюсь расширить этот интерфейс, как показано ниже:

export declare interface SpecificClientEvents extends ClientEvents {
    on(event: "child_event_name", data: (data: string) => void) : this;
    emit(event: "child_event_name", data: string) : boolean;
}

export class SpecificClientEvents extends ClientEvents {}

Я получаю следующие ошибки:

TS2430: Interface 'SpecificClientEvents' incorrectly extends interface 'ClientEvents'. для интерфейса

TS2415: Class 'SpecificClientEvents' incorrectly extends base class 'ClientEvents'. для класса

Я также попытался разделить класс и интерфейс на отдельные определения, как показано в следующем примере:

export interface ClientEventDefinitions {
    on(event: "event_name", data: (data: string) => void) : this;
    emit(event: "event_name", data: string) : boolean;
}

export class ClientEvents extends EventEmitter implements ClientEventDefinitions {}

export interface SpecificClientEventDefinitions {
    on(event: "child_event_name", data: (data: string) => void) : this;
    emit(event: "child_event_name", data: string) : boolean;
}

export class SpecificClientEvents extends ClientEvents implements SpecificClientEventDefinitions {}

Это компилируется нормально, но моя IDE не не обнаруживать имена событий, как это происходит, когда класс и интерфейс имеют одно и то же имя. Это вероятно проблема с IDE? Это не работает как для родительского, так и для дочернего классов.

Я также могу выполнить new SpecificClientEvents().emit("an event that isn't defined"); без ошибок, используя 2-й пример.

EDIT:

Если я вручную добавляю методы из родительского интерфейса в дочерний интерфейс, это работает абсолютно нормально. Но какой смысл расширять родительский объект, если мне нужно добавить методы вручную?

РЕДАКТИРОВАТЬ 2:

Я решил использовать строгий пакет генератора событий со следующим кодом для удовлетворения моих потребностей

export type ClientEventEmitter = StrictEventEmitter<EventEmitter, ClientEvents>;

export interface ClientEvents {
    event_name: (data: string) => void;
}

export type SpecificClientEventsEmitter = StrictEventEmitter<EventEmitter, SpecificClientEvents>;

export interface SpecificClientEvents extends ClientEvents {
    child_event_name: (data: string) => void;
}

Ответы [ 2 ]

4 голосов
/ 24 февраля 2020

Я думаю, что вы, возможно, не знаете, что вы используете функцию, называемую строковые литералы типов , что означает, что ваши два интерфейса имеют несовместимые типы, а ваш первый пример имеет ожидаемое поведение.

Вот упрощенная версия для ясности:

interface A {
  prop: 'String literal type';
}

interface B extends A {
  prop: 'Another string literal type';
}

Это даст вам сообщение об ошибке: Type '"Another string literal type"' is not assignable to type '"String literal type"'

Я также могу выполнить новый SpecificClientEvents (). emit («событие, которое не определено»); без каких-либо ошибок, используя 2-й пример.

Похоже, это связано с тем, что implements не применяет строковый литерал типа из SpecificClientEventDefinitions, а класс SpecificClientEvents вместо этого использует типы из EventEmitter (string | symbol).

Вот альтернативный подход с использованием обобщенного c класса, который, я думаю, решит вашу проблему:

export class ClientEvents<T extends string> extends EventEmitter {
  on(event: T, data: (data: string) => void): this {
    return super.on(event, data);
  }
  emit(event: T, data: string): boolean {
    return super.emit(event, data);
  }
}

С этим кодом при создании нового эмиттера с new ClientEvents<'special_event_name'>() для функций on и emit потребуются события типа 'special_event_name'.

3 голосов
/ 25 февраля 2020

Этот ответ является лучшим из того, что вы можете получить для своих точных потребностей, но если вам нужно более сложное отображение между именем события и его типом данных, вы можете использовать что-то вроде этого

export interface ClientEvents<T> {
    on(event: keyof T, data: (d: T[typeof event]) => void) : this;
    emit(event: keyof T, data: T[typeof event]) : boolean;
}

type SpecificEvents = {
    eventName1: string,
    eventName2: number
}

class SpecificClientEvents implements ClientEvents<SpecificEvents> {
    on(event: keyof SpecificEvents, data: (d: SpecificEvents[typeof event]) => void) : this {
        switch (event) {
            case "eventName1":
                // compiler forces you to pass string
                data("string value");
                break;
            case "eventName2":
                // compiler forces you to pass number
                data(2);
                break;
            default:
                break;
        }

        return this;
    }

    emit(event: keyof SpecificEvents, data: SpecificEvents[typeof event]): boolean {
        switch (event) {
            case "eventName1":
                // data is of type string
                break;
            case "eventName2":
                // data is of type number
                break;
            default:
                break;
        }

        return true;
    }
}

или более простой встроенный тип, в котором большинство IDE будет автозаполнять реализацию и исправлять типы аргументов.

class SimpleEvents implements ClientEvents<{ eventName: number }> {
    on(event: "eventName", data: (d: number) => void): this {
        throw new Error("Method not implemented.");
    }

    emit(event: "eventName", data: number): boolean {
        throw new Error("Method not implemented.");
    }
}

Вот ссылка на площадку

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...