Loreno
Loreno

Reputation: 668

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

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

Answers (2)

Iancovici
Iancovici

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

Estus Flask
Estus Flask

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

Related Questions