new EventEmitter() vs new EventEmitter<Something>()

3

假设我有这段代码:

export class ProductsListComponent {
  @Output() onProductSelected: EventEmitter<Product>;

  constructor() { 
    this.onProductSelected = new EventEmitter();
  }
}

这是一些关于EventEmitter用法的例子。我不明白为什么我们首先要显式声明onProductSelect是一个携带产品实例的EventEmitter,然后再用new EventEmitter()实例化它。为什么不用new EventEmitter<Product>()
我认为在C#中,我必须采用第二种方式,因为如果EventEmitter是泛型,否则它将无法编译。为什么TypeScript不要求这样做呢?
//编辑: 进一步解释我的问题。 以下两者有何区别:
@Output() onProductSelected: EventEmitter<Product>;
this.onProductSelected = new EventEmitter();

并且

@Output() onProductSelected: EventEmitter;
this.onProductSelected = new EventEmitter();

可能是 https://dev59.com/G1oU5IYBdhLWcg3wYWMM 的重复问题。 - Estus Flask
不,不完全是。另一个问题是关于如何创建泛型类的实例,其中一个泛型类型在这种实例中并不真正需要。我的问题是关于在指定泛型类型和不指定泛型类型的情况下实例化的区别。 - Loreno
2个回答

5

文档章节所述,当泛型类或函数没有指定类型时,类型参数推断会发生。

函数可以从其参数或返回类型推断T类型,类可以从构造函数参数或返回类型推断T类型:

function foo<T>(v: T) { return v }
foo(1); // T inferred to number

class Foo<T> {
  constructor(v: T) {}
}
new Foo(1); // T inferred to number

如果没有可以推断的内容,T会被推断为空对象{}由于某种原因
class Foo<T> {
  foo(v: T) {}
}

new Foo().foo(1); // T inferred to {}

为了避免对 {} 的干扰,可以提供默认类型:
class Foo<T = string> {
  foo(v: T) {}
}

new Foo().foo(1); // type error

如果通用的类或函数不应该使用默认类型,可以指定一些不可能的类型:
class Foo<T = never> {
  foo(v: T) {}
}

new Foo(); // no type error, T isn't involved
new Foo().foo(<any>1); // type error

由于EventEmitter通用类没有指定默认类型,因此后者被推断为{}。通常不会出现问题,因为emit受泛型类型影响的唯一方法。由于所有非空类型都可以强制转换为对象类型,这通常不会导致类型错误-只要忽略了空类型。 strictNullChecks编译器选项将成为具有默认类型和空值的EventEmitter的问题:
const ee = new EventEmitter();
ee.emit(null); // type error

因此,对于全方位的EventEmitter,它不应该依赖默认类型并应该按如下方式实例化:

const ee = new EventEmitter<any>();

3

EventEmitter() 将与 EventEmitter<any>() 相同。 如果您在 ProductsListComponents 的选择器上绘制此内容的HTML, 那么您可以侦听 onProductSelected 事件并在发生时分配一个类似于 onSelected 的操作。默认情况下,您将获得一个新的 EventEmitter<any>(),而在 TypeScript 中定义一个 any 类型的变量是一种通用的方法。

<product-list (onProductSelected)="onSelected($event)"> </product-list>

所以每次你叫孩子的名字时,
this.onProductSelected.emit("hello");
this.onProductSelected.emit(1);

父组件的函数onSelected($event)将被调用,您可以对该数据进行任何操作。


如果您只期望一种类型的数据输出给父组件以便进一步处理,则需要坚持特定的数据类型。

onProductSelected: Product = new EventEmitter<Product>(); 
products: Product[];

在你的代码中的某个位置,你可以触发“emit”操作。

this.onProductSelected.emit(this.products[1]);

在 stackblitz 中添加了一个示例(点击此处)

关于您更新的问题

@Output() onProductSelected: EventEmitter;是一个错误,因为在这里您正在声明一种类型(在:之后,在=之前),而不是定义一种类型(在=之后)。当您将它声明为EventEmitter时,您需要一个参数<type>

如果您声明EventEmitter的类型,那么编译器将确保您不会发出任何其他类型的产品或您声明的任何其他类型。

@Output() onProductSelected: EventEmitter<Product>;

谢谢。我不明白一件事:onProductSelected: Product = new EventEmitter<Product>(); 为什么这里的onProductSelected的类型是Product,并被赋值为一个新的EventEmitter<Product>对象? - Loreno
我在原来的问题上添加了一个“编辑”。 - Loreno
它并不等同于 EventEmitter<any>()。 - Estus Flask
@estus 至少使用了 Angular 的 EventEmitter。这个答案是针对 Angular 框架的,因为有这个标签。更新答案以显示它确实可以工作。 - Iancovici
1
我非常清楚这是一个Angular问题。我不是说它不起作用。我是说EventEmitter()并不等同于EventEmitter<any>()。它等同于EventEmitter<{}>(),这是一件不同的事情,可能会产生后果。请参见我的答案以获取解释。 - Estus Flask

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接