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

时间:2018-02-03 10:57:47

标签: javascript angular typescript generics

Let's say I have this code:

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

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

This is some example of EventEmitter usage. I don't understand why first we declare onProductSelect explicitly stating that it is EventEmitter that carries Product instance, and then we instantiate it with just new EventEmitter(). Why not new EventEmitter<Product>()?

I think that in C# I would have to go with the second way, because otherwise it wouldn't compile if EventEmitter was generic. Why doesn't TypeScript require that?


//EDIT:

Further clarification fo my question. What's the difference between:

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

and

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

2 个答案:

答案 0 :(得分:4)

documentation chapter中所述,当没有为泛型类或函数指定type时,会发生类型参数推断。

函数可以从其参数或返回类型推断出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被推断为空对象{} for some reason

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泛型类没有指定默认类型,后者被推断为{}。通常不会出现问题,因为emitthe only method that is affected by generic type。由于所有非nully类型都可以强制转换为对象类型,因此通常不会导致类型错误 - 只要忽略nully类型。

strictNullChecks compiler option对于具有默认类型和无效值的EventEmitter将是一个问题:

const ee = new EventEmitter();
ee.emit(null); // type error

因此对于全面的EventEmitter,它不应该依赖于默认类型并被实例化为:

const ee = new EventEmitter<any>();

答案 1 :(得分:2)

EventEmitter() will work the same as EventEmitter<any>(). If you draw this in HTML on the selector of ProductsListComponents, then you can listen to onProductSelected event and assign an action like onSelected when that happens. By default you'll get a new EventEmitter<any>(), and defining a variable of any type is the generic approach in Typescript.

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

So every time in your child you call

this.onProductSelected.emit("hello");
this.onProductSelected.emit(1);

Parent's function onSelected($event) will get called and you can do anything with that data.


If you're only expecting one type of data to get outputted to the parent so it can further handle it, then you'd want to stick to a specific data type.

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

then somewhere in your code you can trigger to emit

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

Added an example in stackblitz

Regarding your updated question

@Output() onProductSelected: EventEmitter; is an error, because here you're declaring a type (after :, before =), as oppose to defining a type (after =), when you declare it as an EventEmitter you do need an argument <type>

If you declare the type of EventEmitter, then the compiler will make sure you don't emit anything other than Product type, or what type you declare it to be.

@Output() onProductSelected: EventEmitter<Product>;