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();
答案 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
泛型类没有指定默认类型,后者被推断为{}
。通常不会出现问题,因为emit
是the 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>;