【问题标题】:new EventEmitter() vs new EventEmitter<Something>()新的 EventEmitter() 与新的 EventEmitter<Something>()
【发布时间】:2018-02-03 10:57:47
【问题描述】:

假设我有这个代码:

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

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

这是EventEmitter 用法的一些示例。我不明白为什么我们首先声明 onProductSelect 明确声明它是带有 Product 实例的 EventEmitter,然后我们只用 new EventEmitter() 实例化它。为什么不new EventEmitter&lt;Product&gt;()

我认为在 C# 中我必须采用第二种方式,否则如果 EventEmitter 是通用的,它将无法编译。 为什么 TypeScript 不需要这个?


//编辑:

进一步澄清我的问题。 有什么区别:

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

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

【问题讨论】:

  • 不,不是。另一个问题是关于如何创建一个泛型类的实例,其中一个泛型类型在这种实例中并不真正需要。我的问题是关于指定和不指定泛型类型的实例化差异。

标签: javascript angular typescript generics


【解决方案1】:

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

函数可以从其参数或返回类型推断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。由于所有非空类型都可以强制转换为对象类型,因此这通常不会导致类型错误——只要忽略空类型即可。

strictNullChecks compiler option 将成为 EventEmitter 默认类型和空值的问题:

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

所以对于全能的EventEmitter,它不应该依赖默认类型而被实例化为:

const ee = new EventEmitter<any>();

【讨论】:

    【解决方案2】:

    EventEmitter() 的工作方式与EventEmitter&lt;any&gt;() 相同。 如果你在ProductsListComponents 的选择器上用 HTML 绘制它, 然后您可以收听onProductSelected 事件并在发生这种情况时分配一个类似onSelected 的操作。默认情况下你会得到一个新的EventEmitter&lt;any&gt;(),并且定义一个any类型的变量是Typescript中的通用方法。

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

    所以每次你给孩子打电话

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

    将调用父函数onSelected($event),您可以使用该数据执行任何操作。


    如果您只希望将一种类型的数据输出到父级以便它可以进一步处理它,那么您需要坚持使用特定的数据类型。

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

    然后你可以在代码中的某个地方触发发射

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

    添加了example in stackblitz

    关于您更新的问题

    @Output() onProductSelected: EventEmitter; 是一个错误,因为在这里你声明了一个类型(: 之后,= 之前),而不是定义一个类型(= 之后),当你将它声明为 EventEmitter 时你确实需要一个参数&lt;type&gt;

    如果您声明 EventEmitter 的类型,那么编译器将确保您不会发出除 Product 类型或您声明的类型之外的任何内容。

    @Output() onProductSelected: EventEmitter<Product>;
    

    【讨论】:

    • 谢谢。我不明白一件事: onProductSelected: Product = new EventEmitter();为什么 onProductSelected 在这里有一个 Product 类型并被分配了一个 EventEmitter 类型的新对象?
    • 我在原始问题中添加了 EDIT
    • 它不等同于 EventEmitter().
    • @estus 它至少在使用 Angular 的 EventEmitter。由于标签,此答案针对 Angular Framework。更新了答案以显示它确实有效
    • 我很清楚这是 Angular 问题。我不是说它不起作用。我是说 EventEmitter() 不等同于 EventEmitter()。它相当于EventEmitter(),这是另一回事,可能有它的后果。请参阅我的答案以获得解释。
    猜你喜欢
    • 2016-09-10
    • 1970-01-01
    • 2021-12-30
    • 2014-06-06
    • 2015-03-27
    • 1970-01-01
    • 1970-01-01
    • 2012-05-03
    • 2016-09-18
    相关资源
    最近更新 更多