Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does TypeScript allow duplicate component while implements?

Why does TypeScript allow duplicate component while 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

But it throwing duplicate identifier error while component extends.

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

like image 322
Ramesh Rajendran Avatar asked Nov 15 '17 12:11

Ramesh Rajendran


3 Answers

To understand why the first code is not an issue, but the second is, you need to understand the difference between a class and an interface. An interface is a guarantee that its implementer will at least provide the interface members. It does not provide any actual functionality. A class however can contain implementation code; you can inherit from a class to reuse that code and change its behavior by adding new code or changing existing implementations.

That means implements and extends have different meanings. implements says: I can guarantee to every consumer of this class, that it will have at least the members of the interface. Since there is no actual implementation in the interface, it is no problem to make multiple of these guarantees, as long as the class implements all of them. You are right that it does not make any sense to add the same guarantee multiple times, but it does ot really hurt either. The creators of TypeScript could have forbidden to implement the interface multiple times. We can speculate why they didn't; my guess is that since TypeScript is based on JavaScript and JS is quite forgiving, they didn't want to forbid something that doesn't do any damage. Note that TS is a type layer on top of JS and all type information will be eventually dropped during compilation to JS. In this context, dropping multiple repetitive interface implementations does not really hurt, since the result will be exactly the same.

Other than implements, extends is a different story. While there are some languages that allow multiple inheritance (for example C++), multiple inheritance comes with a lot of difficult implementation details (such as the diamond problem or calling base class constructors), so many languages do not support it with the idea that it causes more problems than it possibly solves. TypeScript does not allow multiple inheritance, which means you can not use extends with more than one base class on general principle. Unlike implementing an interface, inheriting a class has consequences on how the program works and the compiler will do much more than just stripping the type information. That's why it makes sense to raise an error there.

like image 141
Sefe Avatar answered Oct 20 '22 07:10

Sefe


The existing answers do a good job of explaining:

  • Class vs. interface.
  • multiple inheritance vs. implementing multiple interfaces.
  • Interfaces being erased by compilation meaning the compiled code is no different with the redundant interfaces in the implements clause.

Yet some confusion remains. To come at the question from another angle:

which situation we need to use it?

You don't. There is no situation that you need to declare redundant interfaces. It reminds me of something like this:

var v = v = v = 66

Yes, it's fine as far as the compiler goes. No, you don't ever need to do this.

Why is it accepted?

It's easy to see why someone (especially someone with a Java background) might be confused by the lack of a warning. After all, Eclipse has been warning me about this for years (Hello, Serializable!).

Having the same interface named multiple times in a single class definition is a little weird. It might help to consider a redundant interface example that is more likely to actually happen:

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());

You can (loosely) think of the GrandChild class like this:

public class GrandChild implements StringProducer, StringProducer, StringProducer {

since a class implements all its interfaces and those of its ancestors.

Should the compiler (or linter, perhaps) bark about this? Should I be forced to remove the implements clause from Child and GrandChild?

I think this is largely a matter of preference. For example, when I have GrandChild open in my IDE, I might want to see in that file all the interfaces the class implements. On the other hand, I might feel like this is just noise, and want a warning.

The compiler certainly doesn't care, and doesn't need to. But I can see why you might want a lint warning for this. The question at the end of the day seems (to me) to be "Why isn't there a tslint rule for redundant interfaces?". That's a reasonable question, that I can't answer. You could always write such a rule and share it with the rest of us.

like image 37
Mike Patrick Avatar answered Oct 20 '22 09:10

Mike Patrick


TypeScript doesn't allow multiple inheritance, much like Java, C# etc. So the second example won't work firstly because TSC thinks you're trying to extend more than one class, before it fails because it's the same class.

For the first case, I agree it should say something because it's probably a small mistake. On the other hand, it's not semantically wrong. When you're implementing the methods in the first OnInit, you're implementing it for the second and third as well, so you should be covered.

like image 1
Horia Coman Avatar answered Oct 20 '22 08:10

Horia Coman