Giter Club home page Giter Club logo

Comments (50)

am11 avatar am11 commented on March 29, 2024

👍

The example would be more awesome if you incorporate virtual as well. 😄

abstract class Base {
    abstract getThing(): string;
    getOtherThing() { return 'hello'; }
    virtual getVirtualThing() { return 'hello virtual'; }
}
...

from typescript.

DanielRosenwasser avatar DanielRosenwasser commented on March 29, 2024

@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?

from typescript.

am11 avatar am11 commented on March 29, 2024

@DanielRosenwasser, that is so true. Thanks!

Actually, I was thinking about the conventional OO keywords virtual and overrirde, 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 new keyword there, CS compiler gives warning).

from typescript.

RyanCavanaugh avatar RyanCavanaugh commented on March 29, 2024

new is infeasible given JavaScript's runtime semantics; given that we've already shipped the language this way, virtual would 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 abstract modifier implicit in a class that doesn't implement all abstract methods?

from typescript.

am11 avatar am11 commented on March 29, 2024

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 abstract methods?

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 :`)

from typescript.

RamIdeas avatar RamIdeas commented on March 29, 2024

Even before abstract is 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.

from typescript.

RamIdeas avatar RamIdeas commented on March 29, 2024

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)
abstract MustInherit MustOverride
virtual Overridable Overridable
sealed NotInheritable NotOverridable

From these more descriptive keywords, it can be seen immediately that the overridable keyword 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 @am11. I believe the same gain in clarity is achieved with the other keywords listed.

from typescript.

RyanCavanaugh avatar RyanCavanaugh commented on March 29, 2024

We're trying to clear out our design backlog and I think this should come up soon once ES6 features are wrapped up. We discussed abstract in a design meeting a few months ago and there didn't seem to be any big open questions.

from typescript.

metaweta avatar metaweta commented on March 29, 2024

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.

from typescript.

RyanCavanaugh avatar RyanCavanaugh commented on March 29, 2024

Discussed 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 new on a symbol, we see if the symbol is a class with abstract, 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.

from typescript.

Vadorequest avatar Vadorequest commented on March 29, 2024

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 was abstract or not to do so. What if having a abstract class would generate somehow hidden field ___abstract___ = true. If the class isn't abstract like with class 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.

from typescript.

mihhail-lapushkin avatar mihhail-lapushkin commented on March 29, 2024

I think, you don't have to go crazy with this.
I believe, the main thing that people want from this is to be able to get a compile-time error on non-implemented abstract methods and also to omit implementation of interface methods in abstract classes.
The rest is great, but not that crucial. Start small and extend if needed.

from typescript.

mwisnicki avatar mwisnicki commented on March 29, 2024

Abstract type shouldn't have visible constructor. i.e. new t() in above example shall fail.

If user wants constructible type he should explicitly allow it which is possible thanks to type unions:

var t: typeof Abs | new() => Abs;

from typescript.

iislucas avatar iislucas commented on March 29, 2024

I would have thought that a type being abstract would be a bit in the type system. That's how people code it up in other functional languages. Then the compiler maintains knowledge of which types are abstract and which are concrete. Make sense?

from typescript.

pspi avatar pspi commented on March 29, 2024

I wouldn't mind if the typeof corner-case doesn't catch abstract.

I feel the common use case is to simply define abstract methods that are left for child classes to implement.

from typescript.

Griffork avatar Griffork commented on March 29, 2024

I like two proposed solutions personally,

  1. Abstract classes are type only, they have no code and what would be their code is added to child classes code.
  2. don't handle the typeof case.
    Here's something else too: abstract classes could have an init method instead of a constructor, and their constructor could error. The init method must be called by child classes (like super ()) and must be bound to a child instance. Init methods on abstract class must call their parent's constructor or init.
    This leaves the constructor free to contain a throw new error and still have child classes be able to work correctly.
    The method doesn't need to be called init, and can be marked with an underscore or something if deemed necessary.

from typescript.

RamIdeas avatar RamIdeas commented on March 29, 2024

@Griffork, your first proposition wouldn't allow for someChild instanceof MyAbstractClass at runtime

@RyanCavanaugh, instances like your typeof example is when I really hate static typing. With static typing this shouldn't be allowed because there is a chance that variable holds an abstract class. In an ideal world, the compiler would "run" the code and only error in the case I've assigned non-abstract classes to the variable (not the case in your example).

Also, are you definitely going with the abstract keyword?

from typescript.

RyanCavanaugh avatar RyanCavanaugh commented on March 29, 2024

I don't think any alternatives have even been mentioned.

from typescript.

RamIdeas avatar RamIdeas commented on March 29, 2024

I think mustoverride would be a more descriptive keyword for abstract methods

For alternatives in other scenarios that you may use abstract, please see my comment above


To spell it out, I think mustoverride, mustinherit, notoverridable and notinheritable are more descriptive keywords

from typescript.

Griffork avatar Griffork commented on March 29, 2024

@RamIdeas Good point, didn't think of that.

Alternatives to mustoverride and mustinherit that are more descriptive of the current behaviour than the intended use case would include notcallable and notnewable.

After all, you can't garentee how something is going to be used, but you can garentee it's state, and that's what the abstract and virtual keywords were designed to do.

from typescript.

enoshixi avatar enoshixi commented on March 29, 2024

I would say prevent directly instantiating abstract classes at compile time. Otherwise let it be a run time error. It works the same way in other statically typed languages.

// e.g. C#
object value = new Stream(); // compile time error

Type type = typeof(Stream);
value = Activator.CreateInstance(type); // this compiles fine but will fail at runtime

What about something like this to prevent runtime instantiation?

// TypeScript
abstract class GreeterBase {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    abstract greet(): void;
}

class Greeter extends GreeterBase {
    constructor() {
        super("World");
    }

    greet() {
        alert("Hello, " + this.greeting);
    }
}
// JavaScript
var GreeterBase = (function () {
    function GreeterBase(message) {
        throw new Error("Cannot instantiate abstract class");
    }
    GreeterBase.prototype.__ctor = function (message) {
        this.greeting = message;
    }
    return GreeterBase;
})();
var Greeter = (function (_super) {
    __extends(Greeter, _super);
    function Greeter() {
        _super.prototype.__ctor.call(this, "World");
    }
    Greeter.prototype.greet = function () {
        alert("Hello, " + this.greeting);
    };
    return Greeter;
})(GreeterBase);

from typescript.

nathggns avatar nathggns commented on March 29, 2024

That sounds like the best solution.

Sent from my iPhone

On 19 Dec 2014, at 19:20, enoshixi [email protected] wrote:

I would say prevent directly instantiating abstract classes at compile time. Otherwise let it be a run time error. It works the same way in other statically typed languages.

// e.g. C#
object value = new Stream(); // compile time error

Type type = typeof(Stream);
value = Activator.CreateInstance(type); // this compiles fine but will fail at runtime
What about something like this to prevent runtime instantiation?

// TypeScript
abstract class GreeterBase {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
abstract greet();
}

class Greeter extends GreeterBase {
constructor() {
super("World");
}

greet() {
    "Hello, " + this.greeting;
}

}
// JavaScript
var GreeterBase = (function () {
function GreeterBase(message) {
throw new Error("Cannot instantiate abstract class");
}
GreeterBase.prototype.__ctor = function (message) {
this.greeting = message;
}
return GreeterBase;
})();
var Greeter = (function (_super) {
__extends(Greeter, _super);
function Greeter() {
_super.prototype.__ctor.call(this, "World");
}
Greeter.prototype.greet = function () {
"Hello, " + this.greeting;
};
return Greeter;
})(GreeterBase);
—
Reply to this email directly or view it on GitHub.

from typescript.

Griffork avatar Griffork commented on March 29, 2024

@enoshixi
Exactly what I was trying to suggest, but much better presented!

from typescript.

panost avatar panost commented on March 29, 2024

Just an idea of a more JScript oriented implementation of abstract methods, without abstract classes

class GreetBase {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet:()=>void;
}

class Greeter extends GreeterBase {
    greet() {
        console.log("Hello, " + this.greeting);
    }
}

and used as

function logGreet() {
    console.log( "Log: " + this.greeting );
}

var gr =  new Greeter("World");
gr.greet = logGreet; // supply implementation on the fly
gr.greet(); // "Log: World"
delete gr.greet; // back to prototyped
gr.greet(); // "Hello, World"

var gBase = new GreetBase("Abstract");
gBase.greet = logGreet; // supply implementation on the fly
gBase.greet(); // "Log: Abstract"
delete gBase.greet; // back to default
gBase.greet(); // error

If you wanted to prevent public construction of GreetBase then perhaps the constructor could be decorated with "protected"

from typescript.

NoelAbrahams avatar NoelAbrahams commented on March 29, 2024

Regarding throwing an error from the constructor of the abstract class, I would rather abstract be a design-time check only. We have cases where we want to write test cases for abstract classes, so the following should be permitted:

  var sut: MyAbtractClass = new (<any>MyAbtractClass)();

  assert.areEqual('foo', sut.foo);

I'm aware there are workarounds (e.g. create a mock derived class); not very eager to do that. A runtime error also doesn't really fit in with design goal number 1:

Statically identify constructs that are likely to be errors.

@RyanCavanaugh, the compiler should issue an error for the factory method case. It's always possible to workaround that quite easily, similar to the test case above:

var j: Abs = new (<any>t)();

Not clear about this:

attempting to prevent it would require a lot of extra type system mechanics.

Is that because of the aliasing?

from typescript.

Griffork avatar Griffork commented on March 29, 2024

@NoelAbrahams
How many other languages that have abstract classes allow you to override their abstract nature and make them anyway?
I for one would prefer having to derive from classes in my tests of that meant that catching runtime errors would be easier (particularly for refactoring).
We have a system in place that may actually allow abstract classes to be initialised when they shouldn't which would bypass all of the static type checking, so run- time errors in abstract classes would be a huge time saver.

from typescript.

enoshixi avatar enoshixi commented on March 29, 2024

I like the run time error. If you end up accidentally instantiating an abstract class you are likely to receive a run time error anyway (and not getting an error would almost be worse). I'd rather have it fail quickly and obviously.

Maybe some extra syntax could be used to opt-in or out of the error?

abstract class GreeterBase with Error("Cannot instantiate abstract class") { }

from typescript.

Griffork avatar Griffork commented on March 29, 2024

Or a flag -abstractdontthrowerror?

That way you could turn off the error on client code and leave it in for development (or vice versa)

from typescript.

RyanCavanaugh avatar RyanCavanaugh commented on March 29, 2024

I don't think there's sufficient value in automatically generating a runtime check. People who really want that behavior would always be free to add that to their classes. Adding runtime checks for type system constraints is not something TypeScript does in general.

The code posted by @enoshixi is not workable in practice because you don't know whether any given declare abstract class was generated by TypeScript (in which case the compiler needs to emit __ctor.call) or is a declaration for a "plain" JavaScript class (in which case the compiler needs to emit the normal super call). We'd really rather not complicate the ambient declaration system to the point where people need to know special syntax for one kind of abstract class versus another.

from typescript.

Griffork avatar Griffork commented on March 29, 2024

Huh, hadn't considered the declare problem before, good catch @RyanCavanaugh.
Without the ability to have a run-time check, abstract classes are actually not giving Typescript developers anything new; you can already define class methods that will either error or not be assigned to within the typescript language; the largest bonus here is the ability to essentially "override" the constructor in a similar manner to the way the error-throwing methods can be overridden.

In JavaScript I had a number of "hacks" for implementing runtime-error abstract classes (e.g. THREE.js' prototype copying). I am sad that not only is this not available in Typescript, with the current implementation of classes it is pretty much not possible in Typescript (you can't opt-in to it at all).

Excepting of course, completely forgoing Typescript's classes and mimicking them with JavaScript code.

[EDIT] P.S. You can probably realise at this point that at work I have no problem with people purposefully instantiating "abstract" classes (commenting can prevent someone from doing this) but I have a problem with a more automatic means of creating objects sometimes creating objects that are supposed to be "abstract".
Pain Point.

from typescript.

mwisnicki avatar mwisnicki commented on March 29, 2024

@Griffork how do you define class methods that are not assigned ?

from typescript.

mwisnicki avatar mwisnicki commented on March 29, 2024

@Griffork if you want runtime checks perhaps you would be interested in this.

from typescript.

Griffork avatar Griffork commented on March 29, 2024

Is that (Edit: Safe & Efficient Gradual Typing for TypeScript) actually available?
Also, it seems a little overkill. As I said in another thread, I deal with software that needs to be performant. I'm happy with the idea of developers getting to choose whether or not the constructor throws an error (possibly even with a keyword), but Typescript has made it very difficult to do that at all.

In Javascript you could do something like this:

//Extends
var __extends = this.__extends || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};
//Parent (modified from the Typescript example)
var AbstractGreeter = (function () {
    function AbstractGreeter(message) {
        throw new Error("Can not instantiate parent directly");
    }
    AbstractGreeter.prototype.init = function (message) {
        this.greeting = message;
    };
    AbstractGreeter.prototype.greet = function () {
        return "Hello, " + this.greeting;
    };
    return AbstractGreeter;
})();
//Child, doesn't call super(), therefore doesn't trigger the run-time error.
var Greeter = (function (_super) {
    __extends(Greeter, _super);
    function Greeter(message) {
        _super.prototype.init.call(this, message);
    }
    return Greeter;
})(AbstractGreeter);

In Typescript, you have to forgo all class support to achieve the same effect (or live with little red squiggles everywhere).

Typescript Attempt:

//Code copied from typescript output.
var __extends = this.__extends || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};

class AbstractGreeter {
    greeting: string;
    constructor(message: string) {

    }
    init(message: string) {
        this.greeting = message;
                //If we need to (e.g. if we're extending from another class) we can call
               //super.init.apply(this, arguments) or super.apply(this, arguments) here.
    }
    greet() {
        return "Hello, " + this.greeting;
    }
}

//Code copied from a typescript generated class, and then modified.
var Greeter = (function (_super) {
    __extends(Greeter, _super);
    function Greeter() {
        var _this = this;
        _super.prototype.init.apply(this, arguments);
        this.greet = function () {
            return "Hello, " + _this.greeting;
        };
    }
    return Greeter;
})(AbstractGreeter);

var greeter = new Greeter("world"); //Error, Supplied parameters do not match any signature of call target. Only a void function can be called with the 'new' keyword.

Errors in Typescript but the compiled code runs anyway. It's also a pain to maintain (about as much of a pain as JavaScript is, which is why we switched to Typescript).

I'm ok with using another keyword to mark-up which abstract classes have run-time errors in their constructors and which don't, if that turns out to be the 'best' solution, but run-time errors for these things would be invaluable.


@mwisnicki To declare methods and not have them assigned you can use the following code:

class AbstractGreeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet: ()=>void;
}

class Greeter extends AbstractGreeter {
    greet = () => {
        return "Hello, " + this.greeting;
    }
}

It may not have the syntax that you prefer, but it is actually doable in Typescript's current class structure. The primary problem here is that there is no way of declaring functions as needing to be overridden (you will get no red squiggles or compile-time errors).


Conclusion:
By default abstract methods will error at run time because they're not defined (this already happens). So the abstract keyword needs only add compile-time checks.
Abstract classes have no run-time or compile-time errors, it would be best if Typescript could supply both; in a System where classes can be instantiated by ID instead of hard-coded, you can't ensure that some classes won't be instantiated at some point. And often those errors can take hours, days or weeks to track down.

from typescript.

RyanCavanaugh avatar RyanCavanaugh commented on March 29, 2024

Just posting this here in case anyone wants to have a "throws at runtime" abstract constructor that still works for derived cases:

function abstract<T>(ths: { constructor: T }, cls: { prototype: T } ) {
    if(Object.getPrototypeOf && (Object.getPrototypeOf(ths) === cls.prototype)) throw new Error('This class is abstract')
}

class Abs {
    constructor() {
        abstract(this, Abs);

        // Other ctor logic here
    }
}

class Concrete extends Abs {
} 

var x = new Abs(); // Throws
var y = new Concrete(); // OK

from typescript.

mwisnicki avatar mwisnicki commented on March 29, 2024

@Griffork I don't think it's available but I hope their work will some day find its way into TypeScript compiler, perhaps as an option. Full type safety with runtime checks may not be appropriate for high performance libraries but for usual application code it would help a lot.
But in the end there is no single solution that will satisfy everyone. Ideally I'd like tsc to provide some api/hooks that let people customize code generation so you can output whatever checks you need and target whatever JS class library/pattern you want.


@Griffork As for your undefined abstract method example, I don't see the point of doing it the way you did - besides ugly syntax you get no compile time check (obviously) and worst of all you waste memory on per instance property instead of using prototype like normal method would. With current syntax limitations it's better to just define normal method that throws exception.

from typescript.

Griffork avatar Griffork commented on March 29, 2024

@RyanCavanaugh thanks for that, I totally didn't think of that!

from typescript.

bwindels avatar bwindels commented on March 29, 2024

This would also be really useful for me.

One solution to avoid having to implement a runtime check could be to just not implement the abstract method in the abstract class in javascript, and let it throw a TypeError if you somehow manage to instantiate the abstract class and try to call an abstract method. It's not perfect, but it respects typescript philosophy of not doing anything typescript specific in the generated javascript and already helps a lot.

It could look like this:

class AbstractGreeter {
    constructor(private name: string) {}

    greet() {
        return this.salutation() + ", " + this.name;
    }

    abstract salutation(): string;
}

class HiGreeter extends AbstractGreeter {
    salutation(): string {
        return "hi";
    }
}

var g = new AbstractGreeter("Typescript");  //compile error
var ctor = AbstractGreeter;

function makeMaybe(ctor): AbstractGreeter {
    if(Math.random() > 0.5) {
        return new ctor("Typescript");
    }
    else {
        return null;
    }
}

var ag1 = new AbstractGreeter("Typescript"); //compile error

var hg = makeMaybe(HiGreeter); //compiles
if(hg) {
    console.log(hg.greet()); //logs "hi, Typescript"
}

var ag = makeMaybe(AbstractGreeter); //compiles 
if(ag) {
    console.log(ag.greet()); //throws "TypeError: undefined is not a function"
}

With the generated javascript looking like this, not defining salutation() in AbstractGreeter:

var __extends = this.__extends || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};
var AbstractGreeter = (function () {
    function AbstractGreeter(name) {
        this.name = name;
    }
    AbstractGreeter.prototype.greet = function () {
        return this.salutation() + ", " + this.name;
    };
    return AbstractGreeter;
})();
var HiGreeter = (function (_super) {
    __extends(HiGreeter, _super);
    function HiGreeter() {
        _super.apply(this, arguments);
    }
    HiGreeter.prototype.salutation = function () {
        return "hi";
    };
    return HiGreeter;
})(AbstractGreeter);
var g = new AbstractGreeter("Typescript"); //compile error
var ctor = AbstractGreeter;
function makeMaybe(ctor) {
    if (Math.random() > 0.5) {
        return new ctor("Typescript");
    }
    else {
        return null;
    }
}
var ag1 = new AbstractGreeter("Typescript"); //compile error
var hg = makeMaybe(HiGreeter); //compiles
if (hg) {
    console.log(hg.greet()); //logs "hi, Typescript"
}
var ag = makeMaybe(AbstractGreeter); //compiles 
if (ag) {
    console.log(ag.greet()); //throws "TypeError: undefined is not a function"
}

from typescript.

bwindels avatar bwindels commented on March 29, 2024

Thinking about the above example. I don't know how elaborate the type system is inside the compiler, but wouldn't the compiler be able to determine in the example above that the makeMaybe() function receives constructor of an abstract class and in some cases that constructor function is used with the new operator inside the function?

from typescript.

Griffork avatar Griffork commented on March 29, 2024

My concern is a long story that involves dynamic require statements (requires that are given a variable instead of a hard coded string).
Those required files export one class, and that class is created at run time.
Some bugs have caused abstract classes to be instantiated in this manner, which isn't ideal, this the request for the ability for the constructor to be able to error if not called by a derived class. Note: some of these abstract classes don't contain any virtual functions (all functions have implementation) but they are not 'complete' enough to be used for anything in their own.

I was hoping for native Typescript support for that, but since I'll be able to write the check myself that works too. As long as I can avoid the hours it took to find out I was instantiating an abstract class that it did the first time ;-).

from typescript.

bryanrideshark avatar bryanrideshark commented on March 29, 2024

+1 to include the feature as a compile-only error (without throwing errors in the constructor). All I want is to know that I've forgotten to override a method. It would be awesome if Intellisense in Visual Studio could generate stubs for you when you begin defining a class which extends the abstract class. IMO, the benefit of Typescript revolves around composing code, not changing the execution behavior. I've been waiting for abstract classes for a while, for that purpose : to help me write code, and catch errors at compile time.

from typescript.

RyanCavanaugh avatar RyanCavanaugh commented on March 29, 2024

Going to delete a bunch of 👍 comments to make this thread shorter (upvote feature please GitHub!).

List of fine people who have 👍'd:
basarat, ddotlic, nathggns, ca0v, oswdr, Mogglewump, jeffmay, Lenne231, Griffork, filipstachura, Vadorequest, mihhail-lapushkin, cy, koloboid, Elephant-Vessel, pspi, am11, FedericoZanin, Kagiyama, vnenkpet, rolyp, bvaughn, bryceg, Nimbus89, gotenxds

from typescript.

RyanCavanaugh avatar RyanCavanaugh commented on March 29, 2024

Accepting PRs.

Design notes for potential implementors:

  • No runtime checking, per our usual design goals
  • It is an error to invoke new on an identifier that binds to an abstract class declaration. 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 abstract
  • No abstract methods/properties/fields at this time

from typescript.

DickvdBrink avatar DickvdBrink commented on March 29, 2024

For 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.

from typescript.

mhegazy avatar mhegazy commented on March 29, 2024

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.

from typescript.

jeffreymorlan avatar jeffreymorlan commented on March 29, 2024

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 abstract

I 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 instantiable

But t could be declared as new() => Abs instead, which better describes what is expected of a valid t value anyway. (There is no backward compatibility to worry about, since there are no abstract classes in existing TypeScript code.)

In fact, using typeof Abs here may already be an error even without abstract! Suppose that Abs has an extra constructor parameter, which B and C supply in their super(...) 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 parameters

Because Abs has the wrong constructor signature, we are already required to use new() => Abs instead of typeof Abs in this case. It doesn't seem an undue burden to require the same if Abs's "holes" take the form of abstract methods rather than constructor parameters.

from typescript.

Griffork avatar Griffork commented on March 29, 2024

@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.

from typescript.

RyanCavanaugh avatar RyanCavanaugh commented on March 29, 2024

Merged into master and will be available in our 1.6 release. Thanks @aozgaa !

from typescript.

 avatar commented on March 29, 2024

In case you are wondering, it all happened with #3579 (with no back reference to this original issue ðŸ˜Ē)

from typescript.

ddotlic avatar ddotlic commented on March 29, 2024

Yeah, thanks @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 proper abstract support.

Beta/Alpha 1.6 cannot come soon enough 😉

from typescript.

aozgaa avatar aozgaa commented on March 29, 2024

My apologies about not offering the back-reference. Thanks @jasonwilliams200OK for linking above!

from typescript.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google âĪïļ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.