Reputation: 668
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();
Upvotes: 3
Views: 8547
Reputation: 5731
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>;
Upvotes: 3
Reputation: 222528
As explained in documentation chapter, type argument inference occurs when type isn't specified for generic class or function.
A function can infer T
type from its argument or return types, and a class can infer T
type from constructor argument or return types:
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
If there's nothing to infer, T
is inferred to empty object {}
for some reason:
class Foo<T> {
foo(v: T) {}
}
new Foo().foo(1); // T inferred to {}
In order to avoid inference to {}
, default type can be provided:
class Foo<T = string> {
foo(v: T) {}
}
new Foo().foo(1); // type error
If generic class or function isn't supposed to be used with default type, some impossible type can be specified:
class Foo<T = never> {
foo(v: T) {}
}
new Foo(); // no type error, T isn't involved
new Foo().foo(<any>1); // type error
Since EventEmitter
generic class has no default type specified, the latter is inferred to {}
. There usually won't be problems with that, because emit
is the only method that is affected by generic type. Since all non-nully types can be coerced to object type, this usually won't cause type errors - as long as nully types are ignored.
strictNullChecks
compiler option will be a problem for EventEmitter
with default type and nully values:
const ee = new EventEmitter();
ee.emit(null); // type error
So for all-round EventEmitter
it shouldn't rely on default type and be instantiated as:
const ee = new EventEmitter<any>();
Upvotes: 5