为什么 TypeScript 在实现时允许重复组件?

Why does TypeScript allow duplicate component while implements?

为什么 TypeScript 在 implements 时允许重复组件?

import { Component,OnInit } from '@angular/core';

export class CreateVersionComponent implements OnInit, OnInit, OnInit { }// no error
export class CreateVersionComponent extends OnInit, OnInit, OnInit { }// getting error

但在组件扩展 时抛出 重复标识符错误

So what is the reason for typescript accepting duplicate component while implements? which situation we need to use it?

TypeScript 不允许多重继承,很像 Java、C# 等。所以第二个示例首先不起作用,因为 TSC 认为您正在尝试扩展多个 class , 在它失败之前因为它是相同的 class.

对于第一种情况,我同意它应该说些什么,因为它可能是一个小错误。另一方面,它在语义上并没有错。当您在第一个 OnInit 中实现方法时,您也在为第二个和第三个实现它,因此您应该被覆盖。

要理解为什么第一个代码不是问题,但第二个是问题,您需要了解 class 和接口之间的区别。接口保证其实现者将至少提供接口成员。它不提供任何实际功能。但是 class 可以包含实现代码;您可以继承 class 以重用该代码并通过添加新代码或更改现有实现来更改其行为。

也就是说implementsextends有不同的含义。 implements 说:我可以向这个 class 的每个消费者保证,它至少有接口的成员。由于接口中没有实际实现,因此只要 class 实现所有这些保证,就可以做出多个保证。你是对的,多次添加相同的保证没有任何意义,但它也没有真正的伤害。 TypeScript 的创建者可能禁止多次实现该接口。我们可以推测他们为什么不这样做;我的猜测是,由于 TypeScript 基于 JavaScript 并且 JS 相当宽容,他们不想禁止不会造成任何损害的东西。请注意,TS 是 JS 之上的类型层,所有类型信息最终将在编译为 JS 时被删除。在这种情况下,删除多个重复的接口实现并没有真正的伤害,因为结果将完全相同。

除了implementsextends是另一回事。虽然有一些语言允许多重继承(例如 C++),但多重继承伴随着许多困难的实现细节(例如 diamond problem 或调用基础 class 构造函数),因此许多语言不允许支持它的想法是它导致的问题多于它可能解决的问题。 TypeScript 不允许多重继承,这意味着根据一般原则,您不能将 extends 与多个基础 class 一起使用。与实现接口不同,继承 class 会对程序的工作方式产生影响,编译器将做的不仅仅是剥离类型信息。这就是为什么在那里提出错误是有意义的。

现有答案很好地解释了:

  • Class 对比界面
  • 多重继承与实现多个接口。
  • 接口被编译擦除意味着编译后的代码与 implements 子句中的冗余接口没有区别。

然而,一些困惑仍然存在。换个角度来看问题:

which situation we need to use it?

你不知道。在任何情况下都不需要声明冗余接口。它让我想起了这样的事情:

var v = v = v = 66

,就编译器而言没问题。 ,您永远不需要这样做。

Why is it accepted?

很容易理解为什么有人(尤其是具有 Java 背景的人)可能会因缺少警告而感到困惑。毕竟,Eclipse 多年来一直在警告我(你好,Serializable!)。

在单个 class 定义中多次命名同一个接口有点奇怪。考虑一个更有可能实际发生的冗余接口示例可能会有所帮助:

interface StringProducer {
    getString: () => string;
}

class Parent implements StringProducer {
    getString =  function(): string {
        return 'x';
    }
}

class Child extends Parent implements StringProducer {
    getString = function() : string {
        return 'y';
    }
}

class GrandChild extends Child implements StringProducer {
    getString = function(): string {
        return 'z';
    }
}

console.log(new Parent().getString());
console.log(new Child().getString());
console.log(new GrandChild().getString());

你可以(松散地)想像这样的 GrandChild class:

public class GrandChild implements StringProducer, StringProducer, StringProducer {

因为 class 实现了它的所有接口及其祖先的接口。

编译器(或者 linter,也许)应该对此大喊大叫吗?我是否应该被迫ChildGrandChild中删除implements子句?

我认为这在很大程度上是一个偏好问题。例如,当我在 IDE 中打开 GrandChild 时,我可能希望在该文件中看到 class 实现的所有接口。另一方面,我可能觉得这只是噪音,需要警告。

编译器当然不关心,也不需要关心。但我明白为什么您可能需要为此发出 lint 警告。一天结束时的问题(对我来说)似乎是 "Why isn't there a tslint rule for redundant interfaces?"。这是一个合理的问题,我无法回答。 You could always write such a rule 并与我们分享。

答案很简单。

Typescript 不允许多重继承

也就是说,一个class不能继承多个class。 (这是为了 'extends')。

现在让我们进入'implements'

在这种情况下,我们讨论的是接口。您可以实现所需数量的接口。

请记住,实现接口并不意味着继承。这只是意味着您要在 class 上实施不同的 模型 ,您的 class 定义必须遵守这些模型。

因此,扩展错误(因为多重继承,这是不允许的) 实现上没有错误(因为允许实现多个接口)