Repository navigation
Proposal for Abstract Classes and Methods #3578
Description
Proposal for Keyword abstract
This is a proposal for a new keyword, abstract, to allow for incomplete/partially implemented classes.
Introduction
This proposal expands upon #6, #2946, #2947.
Consider the following situation. A user would like to create a base class that shouldn't be instantiated as a template for derived classes. S/he might write something like
class Animal {
public age : number;
public yearsLeft() { return 20 - this.age; }
public makeSound() : string { return "???"; }
}
class Cow extends Animal {
makeSound() { return "Moo"; }
}
class Cat extends Animal {
makeSound() { return "Meow"; }
}Today, the writer is forced to make a choice: either (i) Animal can be declared an interface, but then yearsLeft cannot have an implementation, or (ii) write the program as above, but then Animal can be instantiated and makeSound has a bogus implementation! Neither of these options is particularly attractive.
We propose abstract as a class declaration modifier to allow the programmer control over whether a class can be instantiated, and as a member function modifier to control whether said member function offers an implementation (and whether the enclosing class is abstract).
Details
The modifier abstract can prepend a class declaration or a member function declaration. Constructors cannot be declared abstract.
- Objects whose type is
typeof CwhereCis anabstract-declared class (hereafter abstract class) cannot be instantiated via calls tonew. - A member function declared
abstractdoes not have an implementation. - Abstract methods cannot be called within a class member function body via
super.foo()wherefoois an abstract method belonging to the parent class. - Abstract methods may be called within a class member function body via
this.foo()wherefoois an abstract method belonging to that class. - If a class has any abstract methods, then the class itself must be declared abstract.
- Abstract methods are inherited.
- Abstract methods can be overridden in the derived class, either by an abstract method (effectively a re-declaration) or a function declaration that offers an implementation.
- A method cannot be simultaneously
abstractandstatic. - A method cannot be simultaneously
abstractandprivate. - If an abstract member declaration contains an explicit accessibility modifier, then the modifier must precede the
abstractkeyword. - Overloads on a member function must be either all abstract or all not abstract.
- Overloads of abstract member functions must be declared adjacent to eachother.
- Objects whose type is the type of an abstract class can only be assigned to other objects whose type is the type of an abstract class. For example,
The following code snippet summarizes valid and invalid usages of the abstract keyword
class A {
// ...
}
abstract class B {
foo(): number { return bar(); }
abstract bar() : number;
}
new B; // error
import myB = B;
new myB; // error
var BB: typeof B = B;
var AA: typeof A = BB; // error, AA is not of abstract type.
new AA;
function constructB(Factory : typeof B) {
new Factory; // error -- Factory is of type typeof C
}
var BB = B;
new BB; // error -- BB is of type typeof C
var x : any = C;
new x; // okay -- undefined behavior at runtime
class C extends B { } // error -- not declared abstract
abstract class D extends B { } // okay
class E extends B { // okay -- implements abstract method
bar() { return 1; }
abstract class F extends B {
abstract foo() : number;
bar() { return 2; }
}
abstract class F {
abstract qux(x : number) : string;
abstract qux() : number;
y : number;
abstract quz(x : number, y : string) : boolean; // error -- declarations must be adjacent
abstract nom() boolean;
nom(x : number) boolean: // error -- use of modifier abstract must match on all overloads.
}
class G { // error -- not declared abstract
abstract baz() : number;
}Pros
- Allows the programmer to write partial implementations of a class.
- Provides a mechanism for the type system to warn about classes that shouldn't be instantiated.
abstractannotations clarify the role of base classes in various patterns.
Cons
- Increases the complexity of the language.
To Be Discussed
- Should abstract be allowed within class expressions?
Proposed Changes to Grammar
A.6 Classes
ClassDeclaration:
abstractopt class Identifier TypeParametersopt ClassHeritage { ClassBody }
MemberFunctionDeclaration:
MemberFunctionOverloadsopt MemberFunctionImplementation
AbstractMemberFunctionOverloads
AbstractMemberFunctionOverloads:
AbstractMemberFunctionOverload
AbstractMemberFunctionOverloads AbstractMemberFunctionOverload
AbstractMemberFunctionOverload:
AccessibilityModifieropt abstract PropertyName CallSignature ;
A.10 Ambients
AmbientClassDeclaration:
abstractopt class Identifier TypeParametersopt ClassHeritage { AmbientClassBody }
AbstractOrStatic:
abstract
static
AmbientPropertyMemberDeclaration:
AccessibilityModifieropt AbstractOrStaticopt PropertyName CallSignature ;
EDIT: updated to clarify the assignability of objects of "abstract type".
Activity
CKGrafico commentedon Jun 20, 2015
Thanks for that!
mhegazy commentedon Jun 20, 2015
I do not understand what you mean by this.
jbondc commentedon Jun 22, 2015
Pretty much how I feel about it. I'd rather see a more lean functional-like TypeScript then adding astronaut like OO patterns like abstract or decorators.
This overlaps tons with mixins or traits too (combining things from other things). The benefit of traits or mixins is you can design 'abstract patterns' that work with modules too. This proposal is limited to a 'class' which lets face it is really a function under the hood.
aozgaa commentedon Jun 22, 2015
Mohamed Hegazy (@mhegazy) This definitely needs re-wording.
We want to prevent the constructor of an abstract class from being invoked. So we wanted to restrict the assignability of the constructor function. Consider the following example:
We don't want the second assignment to be legal. So we restrict the assignabilty of objects whose type is the type of an abstract class to other objects whose type is the type of an abstract class.
I haven't yet implemented this as part of the attached PR.
I've updated the spec to clarify this.
zpdDG4gta8XKpMCd commentedon Jun 25, 2015
Jon (@jbondc) so true, inheritance (for the sake of code "reuse" or whatever else) is a hands down rotten idea, composition does it all with a little bit more effort, screw this s*t
Gaelan commentedon Jun 25, 2015
👍
jbondc commentedon Jun 25, 2015
💩 I'd love to hear how this aligns with the design goals:
kitsonk commentedon Jun 25, 2015
I have long argued of the merits of embracing the protypical nature of ECMAScript instead of projecting a traditional OO paradigm. On the other hand, that hasn't stopped the wider community rejecting that and ECMAScript has taken a large bend towards traditional OO with ES6, though there seems much debate on this, as several of our ECMAScript luminaries seems to be conflicted on every aspect of this.
Jon (@jbondc) I think it is a fair challenge to question this proposal in line with design goals 5 and 6. It is very likely that some sort of abstract/traits/composition type features will end up in ES, though there seems to be no common consensus. Both abstract and traits were proposed during the class discussions in ES6/2015, but all got dropped because no one could agree. TypeScript already has interfaces which provide a sufficient level of abstraction ahead of any implementation of something along this line of ES.
The traits discussion (#311) was closed by Ryan Cavanaugh (@RyanCavanaugh) because of this conflicting with goal 6. I quote:
I honestly don't see what the difference is here.
While I am personally a fan of compostability, a strong emotive response without well thought arguments is unlikely to further your thoughts, especially when it appears you want to throw your arms up in the air and walk away. I don't think that is your intent, but that is the way it comes across.
zpdDG4gta8XKpMCd commentedon Jun 25, 2015
Here is the statement: inheritance is a dangerous tool that causes more problems than solves
All this contributes to frustration, missed deadlines, and myths that coding is hard. In my experience nothing is hard if designed properly. Well, inheritance encourages bad design by allowing developers to violate:
Besides that
Besides that, there is quite a number of successful examples of languages without inheritance at all:
DanielRosenwasser commentedon Jun 25, 2015
Alright, while we'd love to have a constructive conversation on the merits of different approaches in software design, I believe that classes are a useful abstraction and the
abstractmodifier would bring utility. For those who prefer using this paradigm, it would surely be helpful.Unless there are specific "gotchas" introduced in our language from
abstractthat are not found in other languages, I disagree that users would find this extremely complex. If one understands classes and one understands interfaces, then abstract classes do not require a massive cognitive leap.jbondc commentedon Jun 25, 2015
Objective thinking:
http://ocw.kfupm.edu.sa/ocw_courses/phase3/ICS201-Offering-052/Readings/p123-armstrong.pdf
What bothers me the most is the increasing and consistent sidestepping of the 'TS design rules' in favor of:
jbondc commentedon Jun 25, 2015
Kitson Kelly (@kitsonk) I must have missed it, when was abstract ever proposed?
http://wiki.ecmascript.org/doku.php?id=strawman:oldes6
26 remaining items
danquirk commentedon Jul 2, 2015
Aleksey-Bykov C# operates under a completely different set of constraints than TypeScript does. We could love pure functional programming more than anyone but it doesn't change the rules we actually need to operate with as we design for the future (ie conforming to future ECMAScript standards). If you want an arbitrary set of language features/semantics that compile to JS there are a lot of options, but TypeScript isn't one of them. There are also web centric languages that have opted to add desirable expression level features divorced from ECMAScript standards. The value/success of these two divergent strategies is left as an exercise for the reader ;)
Suffice to say, we love designing new language features and want to keep adding stuff that makes everyone's lives easier, but it's simply not the case that we get to just pick our favorite features from Haskell, Smalltalk, etc and put them into TypeScript with no regard for future ECMAScript incompatibility they may/will cause.
Yves57 commentedon Sep 3, 2015
Great job!
Just one question: it seems that properties cannot be abstract.
The code
public abstract get myProp(): string;is not accepted.Limitation or bug?
mhegazy commentedon Sep 3, 2015
Yves Marx (@Yves57) this is by design. there is no way to currently to distinguish between a property accessor and a property declaration, so a derived class can define a property declaration and would meet abstract constraints, but probably this is not what you intended by making the accessor abstract in the base.
Yves57 commentedon Sep 4, 2015
Mohamed Hegazy (@mhegazy) To be more precise, my case is the following
But I understand that this is a specific case!
denvned commentedon Sep 5, 2015
Yves Marx (@Yves57), I've faced exactly same problem. Looks like this case isn't so rare...
It would be better if abstract class was allowed to miss fields from it's interface (both value properties and methods), like in other OOP languages.
So, this should be allowed:
But currently it gives:
denvned commentedon Sep 5, 2015
Also I think that abstract value properties should be allowed in abstract classes (same way value properties are now allowed in interfaces):
mhegazy commentedon Sep 5, 2015
Denis Nedelyaev (@denvned) can you file separate issues to track the two requests, 1. abstract properties, and 2. missing property declaration in abstract class implementing interfaces.
electricessence commentedon Nov 3, 2015
I'm trying to use this in WebStorm 11 with TS1.6... Not working?
DanielRosenwasser commentedon Nov 3, 2015
electricessence WebStorm doesn't use our language service, so unless this is an issue you're seeing at compilations (i.e. not while editing) you should probably check in with them.
Though, it looks like they should support it: http://blog.jetbrains.com/webstorm/2015/09/webstorm-11-eap-142-4723/
AJamesPhillips commentedon Dec 8, 2015
Bryan Rayner (@bryanerayner) +1 for:
zpdDG4gta8XKpMCd commentedon Dec 8, 2015
Bryan Rayner (@bryanerayner), AJP / James Phillips (@AJamesPhillips)
mixins are evil, choose composition: https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750#.usm8t3fuz
AJamesPhillips commentedon Dec 8, 2015
Aleksey-Bykov agreed, I shouldn't have said mixins as practically they share major downsides with multiple inheritance. For the moment I'm:
thisto them then.Would be very interested to see composition / stamps / aspect / traits, in TypeScript. And thanks for the link to Dan Abramov's article.
electricessence commentedon Dec 12, 2015
It works in 11.
From: Daniel Rosenwasser [mailto:notifications@github.com]
Sent: Tuesday, November 03, 2015 3:44 PM
To: Microsoft/TypeScript
Cc: electricessence
Subject: Re: [TypeScript] Proposal for Abstract Classes and Methods (#3578)
electricessence https://github.com/electricessence WebStorm doesn't use our language service, so unless this is an issue you're seeing at compilations (i.e. not while editing) you should probably check in with them.
Though, it looks like they should support it: http://blog.jetbrains.com/webstorm/2015/09/webstorm-11-eap-142-4723/
—
Reply to this email directly or view it on GitHub #3578 (comment) .Image removed by sender.