Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extending intersection types #10549

Closed
Neftedollar opened this issue Aug 26, 2016 · 4 comments
Closed

Extending intersection types #10549

Neftedollar opened this issue Aug 26, 2016 · 4 comments
Labels
Question An issue which isn't directly actionable in code

Comments

@Neftedollar
Copy link

Add support of extending intersection types

Example

class Person1 {
 foo() {
  retorn "person1";
 }
}

class Person2 {
 bar() {
  retorn "person2";
 }
}

class Person extends Person1 & Person2 { 
 foobar() {
  retorn "person3";
 }
}

// or 
// type Persons = Person1 & Person2
// class Person extend Persons {}

var person = new Person();
person.foobar() //person3
person.bar() //person2
person.foo() //person1

Use case

For example, I have a class AngularComponent

export class AngularComponent  {
    someLogic(){ 
         /* .. */
    }
    public goBack(){
        window.history.back();
    }
    public goForward(){
        window.history.forward();
    }
} 

This component use history API for navigation. This is not only component who use this logic, so I dont repeat myself and create HistoryNavigationClass

export class HistoryNavigationClass  {
    public goBack(){
        window.history.back();
    }
    public goForward(){
        window.history.forward();
    }
} 

but some of my components does not use goForward() method, so I split HistoryNavigationClass into two separated classes HistoryBackWalker and HistoryForwardWalker

export class HistoryBackWalker  {
    public goBack(){
        window.history.back();
    }
} 
export class HistoryForwardWalker  {
   public goForward(){
        window.history.forward();
    }
}

and then I try to extend AngularComponent from intersection of HistoryBackWalker and HistoryForwardWalker type.

//type HistoryWalker = HistoryBackWalker & HistoryForwardWalker;
export class AngularComponent  extends HistoryBackWalker & HistoryForwardWalker  { // or   extends HistoryWalker
    someLogic(){ 
         /* .. */
    }
} 

but I can't.

With JavaScript

I can do this:

var Person1 = function(){};
Person1.prototype.foo = function(){return "person1";};

var Person2 = function(){};
Person2.prototype.bar = function(){return "person2";};

var Person = function(){};
Person.prototype.foobar = function(){return "person3";};

Person.prototype = Object.assign(Person.prototype,Person1.prototype,Person2.prototype);

var person = new Person();
person.foo();  // "person1"
person.bar();  //"person2"
person.foobar(); // "person3"
@aravindarun
Copy link

Typescript does not support extending intersections or multiple inheritance, for that matter. Can't you work around this problem by using interfaces and explicit type constraints? Shouldn't something like this work for you?

export interface IHistoryBackWalker  {
    goBack: () => void;
} 

export interface IHistoryForwardWalker  {
   goForward: () => void;
}

export abstract class HistoryWalker implements IHistoryBackWalker, IHistoryForwardWalker {

    public goBack() {
        window.history.back();
    }

    public goForward() {
        window.history.forward();
    }
}

export class AngularComponent extends HistoryWalker {     
} 

(new AngularComponent() as IHistoryBackWalker).goBack();

@felixfbecker
Copy link
Contributor

Multiple inheritance will never work in TypeScript because it doesn't work in JavaScript. The prototype chain is a chain, not a tree. What you are looking for is language-level mixin support, there are open issues for this

@kitsonk
Copy link
Contributor

kitsonk commented Aug 28, 2016

Multiple inheritance will never work in TypeScript because it doesn't work in JavaScript.

Well, it could, if JavaScript supported it, so championing with TC39/ES Discuss would be the way to do that.

@Neftedollar while you can extend the prototype that way, you are breaking the prototype chain for Person, in ES6, this is valid:

class Person1 {
 foo() {
  return "person1";
 }
}

class Person extends Person1 { 
 foobar() {
  return "person3";
 }
}

console.log(Person.prototype.__proto__ === Person1.prototype); // true

You could of course change your example so that the prototype chain works for the first of whatever inheritance, but then you have a mixin problem where if the ancestor class changes at some point post the mixin, it would only be reflected in some class descendents, where the ancestor happened to be in the first "slot".

I suspect the TypeScript core team would need to have a compelling reason to break compatibility with ES6.

Also what would you propose to emit if targeting ES6 with TypeScript?

You are also avoiding the challenges that come with multiple inheritance, like the Diamond Problem and what sort of mitigation strategy should be used. There are many and in my opinion most of them cause surprises and confusion. Multiple inheritance is a can of worms, which is likely why ECMAScript has even avoided considering it.

@mhegazy mhegazy added the Question An issue which isn't directly actionable in code label Aug 29, 2016
@mhegazy
Copy link
Contributor

mhegazy commented Aug 29, 2016

Also related: #311 and #2919

@mhegazy mhegazy closed this as completed Sep 20, 2016
@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Question An issue which isn't directly actionable in code
Projects
None yet
Development

No branches or pull requests

5 participants