Repository navigation
Suggestion: abstract classes #6
Description
Activity
👍
The example would be more awesome if you incorporate
virtualas well. 😄abstract class Base { abstract getThing(): string; getOtherThing() { return 'hello'; } virtual getVirtualThing() { return 'hello virtual'; } } ...
DanielRosenwasser commented
on Jul 27, 2014 MemberMore actionsAdeel Mujahid (@am11) given that methods in JS get called by traversing the prototype chain, all methods are already "virtual" in the traditional sense that C++, C#, Java, and similar languages define it.
For instance:
class E { f () { alert("E"); } } class F extends E { f () { alert("F"); } } var x: E = new F(); x.f();
will alert "F".
Did you have something else in mind?
Daniel Rosenwasser (@DanielRosenwasser), that is so true. Thanks!
Actually, I was thinking about the conventional OO keywords
virtualandoverrirde, so its easy to clearly differentiate between various inheritance behaviors. The TS compiler might just throw warning if those keywords are missing in applicable scenarios and fallback to standard JS convention.I believe this way we can bring C#'s new modifier as well:
abstract class A { public void foo() { } protected virtual void bar() { } } class A1 : A { protected override void bar() { } }
there may exist A2, such that:
class A2 : A { public new void foo() { } }
to explicitly hide super's implementation (without
newkeyword there, CS compiler gives warning).RyanCavanaugh commented
on Jul 28, 2014 MemberAuthorMore actionsnewis infeasible given JavaScript's runtime semantics; given that we've already shipped the language this way,virtualwould be the default in the absence of any other modifier. Let's take discussion of those modifiers to a different suggestion if needed.Seems like the example above has enough information to discuss. Some open questions:
- Can I have an abstract class with zero abstract methods?
- Is the
abstractmodifier implicit in a class that doesn't implement allabstractmethods?
Can I have an abstract class with zero abstract methods?
Since abstract class -- in principle -- serves a purpose of grouping commonalities together such that its objects alone don't make sense; I think we should rule no_abstract_method as a legit case and let the TypeScript linter take care of recommending best practices, code-styling et el.
Is the abstract modifier implicit in a class that doesn't implement all
abstractmethods?Given an abstract class would be able to inherit another abstract class, implicit inference like this would be confusing? I guess (at least C#/Java) developers would expect compiler to throw error and editor to help them correct it, if the first non-abstract / concrete implementer is missing any expected
override. Well as a Ruby guy, I'd probably appreciate this kind of crypto-magic :`)Even before
abstractis implemented, I'm still hoping you'd allow intellisense kick in (in Visual Studio) when overriding base class functions:class BaseClass { myFunction() { /* TO BE OVERRIDDEN */ } } class MyClass extends BaseClass { myF // intellisense should have kicked in -- but it doesn't }
My only option is to open BaseClass.ts to check I'm spelling it correctly.
I'd still prefer to see more descriptive keywords, as I mentioned on https://typescript.codeplex.com/discussions/449920
C# VB (on classes) VB (on methods) abstractMustInheritMustOverridevirtualOverridableOverridablesealedNotInheritableNotOverridableFrom these more descriptive keywords, it can be seen immediately that the
overridablekeyword is unnecessary as methods in JavaScript are overridable by default as opposed to in C# and is less likely to lead to confusion demonstrated by Adeel Mujahid (@am11). I believe the same gain in clarity is achieved with the other keywords listed.RyanCavanaugh commented
on Dec 3, 2014 MemberAuthorMore actionsWe're trying to clear out our design backlog and I think this should come up soon once ES6 features are wrapped up. We discussed
abstractin a design meeting a few months ago and there didn't seem to be any big open questions.The standard design pattern in JS is to throw an exception in the abstract class:
function Base() { throw new Error("Not implemented."); } Base.prototype.f = function () { throw new Error("Not implemented."); }; Base.prototype.g = function () { /* default impl */ }; function Derived() {} Derived.prototype.f = function () { /* impl */ }; Derived.prototype.g = function () { /* override impl */ };The benefit TypeScript could provide is reducing boilerplate and a compile-time error.
RyanCavanaugh commented
on Dec 12, 2014 MemberAuthorMore actionsDiscussed today in the design meeting.
The open question here is how we prevent you from trying to instantiate an abstract class. Consider some code:
abstract class Abs { constructor() { /* some general init here */ } } var x = new Abs(); // Desired: error
This case is easy -- when you invoke
newon a symbol, we see if the symbol is a class withabstract, and error if so.Next:
var a = Abs; var x = new a(); // Not a good thing to do
This is less obvious. You probably want an error? But then consider this case:
class B extends Abs { /* ... */ } class C extends Abs { /* ... */ } var t: typeof Abs; if(/*... */) { t = B; } else { t = C; } var j = new t(); // This is fine
There's nothing distinguishing this case from the previous one (in terms of the type system), but the latter is perfectly valid code to write (and would probably even be common code to write in a factory method). We will probably have to not error on the
new a();case, both because it mimics a valid pattern and because attempting to prevent it would require a lot of extra type system mechanics.What about an hidden field?
Let's say we have a class
abstract class Abs { constructor() { /* some general init here */ } }Because the class is
abstract, we cannot instanciate it, you said you wanted to check if the class wasabstractor not to do so. What if having aabstractclass would generate somehow hidden field___abstract___ = true. If the class isn't abstract like withclass B extends Abs { /* ... */ }then we would have the___abstract___ = false, so to check if a class is abstract or not we could just check that hidden field.I don't know if it's a proper solution, or even if it's possible, but the point is to check if the closest class is abstract or not, whatever if it extends and abstract class or not.
35 remaining items
DickvdBrink commented
on Apr 28, 2015 ContributorMore actionsFor the record, I'm currently (trying) to implement this.
edit What is the best way for this? First some basic stuff and create a PR for review? Or implement it with full test-suite and stuff? Because when I'm moving in total wrong direction I might waste a lot of my time and maybe yours too.
We are open to looking at incremental changes. Just make sure the first iteration is substantial enough to warrant feedback.
I would recommend sharing your approach and design on this issue before jumping into implementation. this way we can help save you time early on.
Indirect invocations, e.g.
var x = MyAbstractClass; var y = new x();are allowed, and the static side of abstract classes have the same construct signatures they would have if they weren't abstractI think this should be reconsidered. Special-casing only a direct "new MyAbstractClass" (rather than removing abstract classes'
new()signatures) is hacky, and makes it easy to accidentally pass an abstract class to a function that instantiates it (function constructAndDoOtherStuff(clazz: new() => Foo) { ... })Earlier, this code was given as an example in favor of abstract classes having a
new()signature:class B extends Abs { /* ... */ } class C extends Abs { /* ... */ } var t: typeof Abs; if(/*... */) { t = B; } else { t = C; } var j = new t(); // error here if `typeof Abs` is not instantiableBut
tcould be declared asnew() => Absinstead, which better describes what is expected of a validtvalue anyway. (There is no backward compatibility to worry about, since there are noabstractclasses in existing TypeScript code.)In fact, using
typeof Abshere may already be an error even withoutabstract! Suppose thatAbshas an extra constructor parameter, whichBandCsupply in theirsuper(...)calls (This is a pattern I use a lot - currently, it's the only way for a class to have an abstract-like "hole" that must be supplied by subclasses):class Abs { constructor(public typeName: string) {} } class B extends Abs { constructor() { super("Class B"); } } class C extends Abs { constructor() { super("Class C"); } } var t: typeof Abs; t = B; new t(); // Error here - wrong number of parametersBecause
Abshas the wrong constructor signature, we are already required to usenew() => Absinstead oftypeof Absin this case. It doesn't seem an undue burden to require the same ifAbs's "holes" take the form ofabstractmethods rather than constructor parameters.jeffreymorlan there was a suggestion that users can program their abstract classes to error in the constructor if they wanted it to (it's above) like this:
class classa { constructor () { if(Object.getPrototypeOf && (Object.getPrototypeOf(this) === classa.prototype) { throw new error("classa is abstract and should not be directly instantiated"); } } }
This specifically checks of classa is 'bottom' of the prototype chain.
- addedCommittedThe team has roadmapped this issueThe team has roadmapped this issueFixedA PR has been merged for this issueA PR has been merged for this issueand removedHelp WantedYou can do thisYou can do this
on Jul 1, 2015 RyanCavanaugh commented
on Jul 1, 2015 MemberAuthorMore actionsMerged into
masterand will be available in our 1.6 release. Thanks Arthur Ozga (@aozgaa) !Yeah, thanks Arthur Ozga (@aozgaa)! Lately, I've been adding many, many
throw new Error('this should not have been called, it's abstract')across my codebase with the intention of replacing it all with properabstractsupport.Beta/Alpha 1.6 cannot come soon enough 😉
My apologies about not offering the back-reference. Thanks @jasonwilliams200OK for linking above!
- locked and limited conversation to collaborators
on Jun 18, 2018
Support an
abstractkeyword for classes and their methodsExamples: