В следующем примере создается экземпляр интерфейса EventEmitter, однако я считаю, что интерфейсы не могут быть созданы, а классы.
@Output() notify: EventEmitter<string> = new EventEmitter<string>();
Полный код ниже:
import { Component, EventEmitter, Output } from '@angular/core';
@Component({
selector: 'app-child',
templateUrl: './child.component.html',
styles: [
]
})
export class ChildComponent {
@Output() sharedData: EventEmitter<string> = new EventEmitter<string>;
shareData() {
this.sharedData.emit('Hello, world!!!');
}
}
@kellermat на самом деле VS CODE говорит, что это интерфейс, когда вы наводите на него указатель мыши, и показывает всплывающую подсказку: (псевдоним) интерфейс EventEmitter<T> import EventEmitter
Я знаю, что вы имеете в виду, и я тоже немного сбит с толку. На самом деле мой источник был: angular.io/api/core/EventEmitter
Когда вы пишете new XXX, вы имеете в виду значение с именем XXX, а не тип с именем XXX, даже если такой тип может быть в области видимости. Таким образом, EventEmitter предположительно является именем как значения конструктора, так и типа интерфейса. Подобные вещи происходят автоматически с объявлениями class (class Foo {} создает как значение с именем Foo, так и тип с именем Foo), но это можно сделать отдельно (например, interface Bar {}, а затем declare const Bar: new () => Bar).
Не могли бы вы отредактировать , чтобы предоставить минимальный воспроизводимый пример, демонстрирующий вашу проблему в автономной среде IDE, включая любые import утверждения? Если вы сможете это сделать, я смогу точно определить, где находятся два определения EventEmitter и почему вещи ведут себя так, как они себя ведут. Однако сейчас все, что у меня есть, это то, что EventEmitter не определено. Если вы сделаете такое редактирование и хотите, чтобы я взглянул еще раз, пожалуйста, упомяните @jcalz в комментарии, чтобы уведомить меня.
@jcalz спасибо за ваше время. Я обновил вопрос и поставил полный код. Это простой вложенный компонент, который генерирует событие.





В дальнейшем важно понимать разницу в TypeScript между типами, которые существуют только во время компиляции и стираются при компиляции кода в JavaScript; и значения, которые передаются в JavaScript и, следовательно, существуют во время выполнения. Значение может иметь тип, но это не одно и то же, и они существуют в разных синтаксических контекстах в коде TypeScript. В чем-то вроде
const foo: Bar = baz<Qux>();
Bar и Qux — типы, а foo и baz — значения. Когда этот код скомпилируется в JavaScript, он, вероятно, станет
const foo = baz();
Поскольку они существуют в разных синтаксических контекстах, вы можете фактически иметь типы и значения, использующие одни и те же имена, не конфликтуя друг с другом. Итак, вы могли бы:
const A: B = B<A>();
где первый B и второй A — типы, а первый A и второй B — значения. Между значением с именем A и типом с именем A нет необходимой связи. Приведенный выше код в точности аналогичен const foo: Bar = baz<Qux>();, просто вещи переименованы. Это может сбить с толку человека, но компилятор не запутается и знает из контекста синтаксиса, какие из них являются значениями, а какие являются типами. Вышеприведенное будет скомпилировано в
const A = B();
где значения остаются, а типы исчезают.
Для вашего примера кода соответствующие определения находятся в event_emitter.ts Angular. Если вы наведете курсор на импортированный EventEmitter в IDE с поддержкой IntelliSense, например
Площадка TS, вы должны увидеть что-то вроде этого:
/*
(alias) interface EventEmitter<T>
(alias) const EventEmitter: {
new (isAsync?: boolean | undefined): EventEmitter<any>;
new <T>(isAsync?: boolean | undefined): EventEmitter<T>;
readonly prototype: EventEmitter<any>;
}
*/
Это означает, что есть две отдельные вещи с именами EventEmitter. Первый — это interface EventEmitter<T>, названный общий тип. Если бы это было единственным объявлением EventEmitter, вы не смогли бы создать его экземпляр. Второй — const EventEmitter, именованное значение, тип которого содержит некоторые сигнатуры конструкций, одна из которых является универсальной. Если вы вызываете сигнатуру универсальной конструкции, возвращаемое значение имеет тип EventEmitter<T>;
Итак, в строке
@Output() sharedData: EventEmitter<string> = new EventEmitter<string>;
// ^ ^
// (alias) interface EventEmitter<T> |
// (alias) new EventEmitter<string>(isAsync?: boolean | undefined): EventEmitter<string>
первый EventEmitter<string> является универсальным типом интерфейса, созданным с помощью string, а последний EventEmitter<string> является частью вызова универсальной функции-конструктора.
Обратите внимание, что когда у вас есть объявление класса , оно вводит как именованный тип (тип экземпляра класса), так и именованное значение (значение конструктора класса) с тем же именем. Это полезно, но создает ошибочное впечатление, что типы и значения с общими именами неразрывно связаны друг с другом.
В любом случае, отношение именованного значения и типа объявления class по сути такое же, как отношение с EventEmitter в Angular. В самом деле, они могли бы предоставить по существу те же типы с чем-то вроде:
declare class EventEmitter<T = any> {
constructor(isAsync?: boolean | undefined);
new(isAsync?: boolean): EventEmitter<T>;
emit(value?: T): void;
subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void):
Subscription;
subscribe(observerOrNext?: any, error?: any, complete?: any): Subscription;
}
И тогда код вроде
const xxx: EventEmitter<string> = new EventEmitter<string>;
// ^ ^
// class EventEmitter<T = any> |
// constructor EventEmitter<string>(isAsync?: boolean | undefined): EventEmitter<string>
будет делать то же самое, за исключением того, что IntelliSense будет использовать class и constructor для ссылки на тип и значение вместо interface и new.
Затем вы можете спросить, если это одно и то же, почему они не использовали объявление class вместо пары interface/const? Это хороший вопрос, но, вероятно, не в тему. В общем, есть вещи, которые вы не можете адекватно описать с помощью объявления class, и в этих случаях была бы необходима пара тип/значение, так что, возможно, это было сделано для большей гибкости.
Действительно, интерфейсы не могут быть созданы. Но EventEmitter — это класс, а не интерфейс.