Skip to content

Feature Request / Proposal: Traits #311

Description

ORIGINALLY PROPOSED IN http://typescript.codeplex.com/workitem/838

Traits, as "compile-time" partial classes, would perfectly fit with TS ideology and resolve problems of multiple inheritance/mixins.

Traits in Scala: http://en.wikibooks.org/wiki/Scala/Traits
Traits in PHP: http://php.net/trait

I propose minimal traits similar to PHP implementation.

trait FooTrait {
   // same as class definition, but no constructor allowed, e.g.:
   public foo() {
       return "foo";
   }
   private bar() {
       return "bar";
   }
}

class Foo {
    import FooTrait; //not insisting on exact keyword and syntax, just smth to start with

    // ...
}

class Bar {
    // rename methods:
    import FooTrait(foo => tfoo, bar => tbar);

    // and to include another trait, there is another import line:
    // import BarTrait;

    // ...
}

The code above could be compiled to JS below (can be optimized, just showing the main idea):

var FooTrait = (function () {
    function FooTrait() {
        throw "Cannot instantiate trait FooTrait";
    }
    FooTrait.prototype.foo = function () {
        return "foo";
    };
    FooTrait.prototype.bar = function () {
        return "bar";
    };
    return FooTrait;
})();

var Foo = (function (_super, FooTrait) {
    function Foo() { }
    Foo.prototype.foo = FooTrait.prototype.foo;
    Foo.prototype.bar = FooTrait.prototype.bar;
    return Foo;
})(undefined, FooTrait);

var Bar = (function (_super, FooTrait) {
    function Bar() { }
    Bar.prototype.tfoo = FooTrait.prototype.foo;
    Bar.prototype.tbar = FooTrait.prototype.bar;
    return Bar;
})(undefined, FooTrait);

Unresolved name conflicts should raise a compile time error.

I think it would be a great advance for the language. But the proposal has more than one year made (in codeplex) ​​and has not yet been implemented.

Activity

  1. ComFreek commented on Jul 30, 2014

    @ComFreek

    I think Mixins are relatively similar to traits, however, there are a few disadvantages (at least as it's described in the handbook):

    1. You have to redeclare class members and methods in the implementing class to satisfy the compiler.
    2. You have to "apply" the mixins (i.e. copy the class members and methods from the mixin class) using a custom function.

    Are the points 1 and 2 by design or are these feature yet to be implemented?

  2. yasselavila commented on Jul 30, 2014

    @yasselavila
    Author

    Mixins is an ugly way that feels like we are using ecmascript 3. Traits would allow us to use a more object oriented clean syntax.

  3. kamranayub commented on Sep 26, 2014

    @kamranayub

    This would certainly be awesome. I don't care how it's done, but this would be great--I use TS mainly in games and engines, and the ability to "mixin" methods from a trait would be great for actors/objects in games (for example, an object importing an EventDispatcher but inheriting from a base class).

    class Actor {
      x: number;
      y: number;
      // ... etc
    }
    
    class Player extends Actor {
      import Traits.Eventing.EventDispatcher;
    
      update(engine) {
        // using my event dispatcher trait
        this.publish(new Event(...));
      }
    }
    

    I don't know how this will work with TS to allow using traits like interfaces (maybe simply traits can implement interfaces).

    class SomeClass {
      public listen(emitter: EventDispatcher) {
        emitter.on("eventName", this.handleSomeEvent);
      }
      handleSomeEvent(args) {
      ...
      }
    }
    new SomeClass().listen(player);
    
  4. jbman commented on Oct 4, 2014

    @jbman

    Traits as part of Typescript and its type system would be really helpful to use the DCI programming paradigm with (http://en.wikipedia.org/wiki/Data,_context_and_interaction).

    I would prefer a trait solution, where the trait is an interface with a default implementation. Such an interface alone would not generate any JS code on compilation.
    Like in Scala, such a trait-interface can now be used in two ways:

    • The interface can be implemented by a class: The class will get the methods "mixed in" (like in Yassel Avila (@yasselavila) code in the first post) .
      The class may need to implement methods which don't have a default implementation
    • The interface can be added to an object when constructing the instance.

    Here is an example:

    // Trait-Interface with default implementation
    interface Customer {
        isGoldMember: false;
        public discountFactor(): number {
           return (this.isGoldMember ? 0.1 : 0); 
        }
    }
    
    // Trait-Interface is implemented
    class MyGoldCustomer implements Customer {
           public isGoldMember() { return true; }
    }
    
    // Trait-Interface is used on instantiation
    class Person {
          constructor(private name: string) {};
          public getName():String { return name; };
          // ...    
    }
    
    var p = new Person('John') with Customer; 
    // p is of type Person AND Customer. The new object gets the methods "mixed in".
    // The type of p is the same type as class X, if X is defined as "class X extends Person implements Customer"
    

    The "with" syntax is borrowed from Scala. Of course some other syntax could be used for mixing trait interfaces into a new instance, e.g.:
    new Person('John') & Customer;
    This syntax would fit to union typers (#805). With traits we have another sort of union type, but the instance is implementing both types, not one of them.

    I'm curious if this suggestion is compatible with the current Typescript type system. Any thoughts?

  5. anatoliyarkhipov commented on Nov 1, 2014

    @anatoliyarkhipov

    Also, without multiple inheritance or traits, not possible to use TypeScript with Dojo Toolkit. The whole library is built on mixins system. Of course, we can use default "dojo/_base/declare", but this way negates the benefits from classes and interfaces in TypeScript.

  6. csnover commented on Feb 26, 2015

    @csnover
    Contributor

    TypeScript team, are you guys open to receiving possible implementations of this feature?

  7. danquirk commented on Feb 27, 2015

    @danquirk
    Member

    We'd be open to implementations in the future but it's important that we first nail down a proposal we can all agree on. It'd be getting ahead of ourselves to start reviewing an implementation before we nail down what is and isn't the intended behavior here.

  8. csnover commented on Feb 27, 2015

    @csnover
    Contributor

    No problem. SitePen is currently working on defining some package specifications for Dojo and this is one of the areas that we are looking to improve, so once we get a bit further along and actually begin the work of defining technical proposals those will be brought here. A prototype implementation would be useful to inform that decision-making and may form a suitable foundation for a final implementation, but these are early days, we have no code yet. I just didn’t want to have anyone on this side start going down the road and then find out that there is no chance to accept this, or that after talking to Mohamed Hegazy (@mhegazy) about C3 linearization that you guys wanted to move in that direction instead.

  9. csnover commented on Feb 28, 2015

    @csnover
    Contributor

    Jon (@jbondc) Great! Looking over what you are proposing so far I would probably start by just focusing on the compiler side of things and not even thinking about any runtime enhancements in order to keep the initial feature very clear and focused (like the OP).

    I understand not wanting to introduce new reserved words that weren’t future reserved words in the EcmaScript spec but import class is very unfortunate. The strawman introduced a “trait” adjective for class (trait class), which is better.

    I/others will have additional feedback over the coming months on this.

  10. NoelAbrahams commented on Mar 1, 2015

    @NoelAbrahams

    Jon (@jbondc), there is a similar proposal on the old codeplex site. What do you think about it?

  11. csnover commented on Mar 13, 2015

    @csnover
    Contributor

    Just a brief update for everyone watching: I should have a complete(ish) proposal to share in the next couple of days at the latest. Sorry for the delay.

  12. csnover commented on Mar 16, 2015

    @csnover
    Contributor

    Our proposal is now available at https://docs.google.com/a/sitepen.com/document/d/112Xw-t8eh-eFIdKhn7LeDbGhO86XwlKv-Fwc6j9U-Oc/edit for discussion. (Please let me know if you prefer this to be in another format/location.) This proposal focuses purely on enhancing the compiler and doesn’t include any runtime enhancements.

    Looking forward to your feedback and working with the rest of the team and community on refining these proposals into something that will be accepted into TypeScript.

  13. yasselavila commented on Mar 16, 2015

    @yasselavila
    Author

    Colin Snover (@csnover) +1... but ... what about use only 'trait' instead of 'traitclass'?

  14. 9 remaining items

  15. anatoliyarkhipov commented on Apr 10, 2015

    @anatoliyarkhipov

    Jon (@jbondc)

    As long as the composing class implements all conflicting methods, there is no conflict reported by the compiler:

    To be able to do this, programmer must know about all methods in all imported mixins and which of them are in conflict. Why he should if it is possible resolve this automatically like dojo/declare does (like in dstore example by Colin Snover (@csnover))?

  16. added
    DeclinedThe issue was declined as something which matches the TypeScript vision
    and removed on Apr 27, 2015
  17. RyanCavanaugh commented on Apr 28, 2015

    @RyanCavanaugh
    Member

    There's a lot of overlap here with mix-ins; it would be bad (or at least treacherous) to do both. With mix-ins being a potential part of ES7/ES8+, we want to take the runtime side of this very conservatively and get TC39 to agree on a proposal in this area first.

    Features specific to the type system to support these patterns are still on the table, but we'll track those in other issues.

  18. zakhenry commented on Jan 22, 2016

    @zakhenry

    Is there an update on how/if mixins will become a first class capability of typescript rather than a bit of a hack with separated registration as it currently stands?

  19. kitsonk commented on Jan 22, 2016

    @kitsonk
    Contributor

    Is there an update on how/if mixins will become a first class capability of typescript rather than a bit of a hack with separated registration as it currently stands?

    I am sure I will be corrected if I am wrong, but the TypeScript team have made it clear that because this is a domain that TC39 has indicated they will address, the team feel that this is something it doesn't feel it can introduce as it would likely cause incompatibilities with ES in the future.

    The implications are, those of us who are passionate about it should be championing it within TC39. The concepts of mixins/traits were part of the originally Harmony proposals, but didn't make it into the spec. Also, as far as I am aware, there isn't any active advocacy on this specific area in TC39 at the moment. Once there is a proposal sufficiently through the process in TC39, then I believe the TypeScript team would be more than glad to implement it.

  20. zakhenry commented on Jan 22, 2016

    @zakhenry

    Kitson Kelly (@kitsonk) thanks for the update, I guess we just have to hope for TC39, I'll see if I can find any discussion on the matter. I feel at this point that traits are the only major capability missing - I'm currently managing a site with ~50k sloc TS, and the mixin registrations are somewhat unwieldy.

  21. sanex3339 commented on Jan 24, 2017

    @sanex3339

    +1 for this feature

  22. ackimwilliams commented on Feb 21, 2017

    @ackimwilliams

    What's the status on this feature?

  23. dylans commented on Feb 22, 2017

    @dylans

    What's the status on this feature?

    See https://blogs.msdn.microsoft.com/typescript/2017/02/02/announcing-typescript-2-2-rc/ ... basically they're not adding new language grammar, but by making some changes to the way Classes work, it's possible/easier to achieve now.

  24. alexeygolev commented on Mar 7, 2017

    @alexeygolev

    One powerful pattern that is possible with traits is this (similar to what Johannes Bergmann (@jbman) mentioned):

    trait AccountService {
      debit: (account: Account, amount: number) => Account;
      credit: (account: Account, amount: number) => Account;
      transfer(from: Account, to: Account, amount: number): [Account, Account, number] {
        return [this.debit(from, amount), this.credit(to, amount), amount]  
      }
    }

    This is not something that TC39 can help us with as in this particular form it's only relevant to statically typed languages. It can be done with abstract classes but then we lose the benefit of the mixin pattern as we move the problem into the inheritance area. Trait composition also makes pattern such as "cake" pattern possible.

  25. samfrances commented on Jul 28, 2017

    @samfrances

    +1

  26. michaelolof commented on Nov 4, 2017

    @michaelolof

    You can have a look at a simple library i wrote at TypeScript Mix I think it fits nicely into how you would want to use traits/mixins

  27. locked and limited conversation to collaborators on Jun 18, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    DeclinedThe issue was declined as something which matches the TypeScript visionSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions