// A convenient function that can be used for any abstract method
function abstractmethod() { throw new Error("abstract method"); }
// The AbstractSet class defines a single abstract method, contains().
function AbstractSet() { throw new Error("Can't instantiate abstract classes");}
AbstractSet.prototype.contains = abstractmethod;
From "Javascript: The Definitive Guide - 9.7.4 Class Hierarchies and Abstract Classes"
I understand the utility of abstract classes in JavaScript. What I don't understand is the necessity or use of setting abstract methods that only throw an error. You can't create an instance of that class, so only instances of the subclasses will exist. They'll each have their own definition for these methods, so why establish an inheritance to a method that just throws a generic error?
Thank you in advance for your guiding response.
I assume that the purpose of this is that it's not a generic error - it's an error that informs you where you went wrong (i.e. you failed to override the method in a subclass). If you didn't define that method, you would get an error saying "Undefined is not a function" (or something similar) and you'd have to spend time hunting around your code to understand why - this way it fails in a more verbose and useful manner.
The other reason, I'd assume, is to indicate the class interface to downstream developers implementing subclasses. Javascript doesn't have any kind of formal interface declaration, so it's helpful to be able to inspect the abstract class and see what methods I'm expected to implement.
Related
I'm looking for a way to implement an abstract static method in TypeScript.
Here an example:
abstract class A {
abstract static b (): any // error
}
class B extends A {
static b () {}
}
This is allowed with public, private and protected methods but not with a static method. TypeScript returns the following error:
'static' modifier cannot be used with 'abstract' modifier.ts(1243)
Is there any workaround?
I think the reasoning is generally as follows:
Abstract methods are placeholders that a concrete subclass is supposed to implement.
The abstract base class will have a concrete method which uses this abstract placeholder, and thus requires it to be implemented; without this, abstract methods make little sense at all. E.g.:
abstract class Foo {
bar() {
return this.baz() + 1;
}
abstract baz(): int;
}
This can only be called on a concrete instance, e.g.:
function (foo: Foo) {
let val = foo.bar();
...
}
The result will be different here depending on what concrete subclass of Foo you get, but it's all guaranteed to work.
Now, what would this look like with static methods?
abstract class Foo {
static bar() {
return this.baz() + 1;
}
abstract static baz(): int;
}
function (F: typeof Foo) {
let val = F.bar();
...
}
This looks somewhat logical and like it should work, shouldn't it? But:
If you write this, there's basically no difference to passing instances of classes, except for the awkward typeof typing. So, why?
If you never instantiate the class, what's the point of a class?
If your static method does instantiate an object, if it acts as an alternative constructor—which is entirely legitimate—then calling F.bar() to get a new instance, you'd expect to get an instance of F, but you might be getting an instance of some subclass of it. And that's arguably not desired.
To rephrase the chain of argument here:
If you're going to use static methods at all, they should act as alternative constructors and return an instance, otherwise they have little business being part of the class.
Alternative constructors should return an instance of the class that you called them on. Calling F.bar() and not getting an instance of exactly F is… weird?
If you're going to call static alternative constructors on classes, you're going to want to call them on specific classes and not variables as shown above, because otherwise you really won't know what you're getting (see point above).
Therefore, there's no real use case for an abstract static method, either as direct alternative constructor nor as helper function for one, since that would lead to a violation of one of the above points one way or another.
As far as I see, this is pretty much the classical thinking that lead to abstract static methods not being a thing. Certain technical limitations in Java may have contributed to that and/or lead to said technical limitations in the first place.
In Javascript one can argue that this makes less sense, since even classes are objects and it would be entirely feasible to use them as described above, and in certain situations it may even make sense. But here we are.
In node guide, it says that:
"The request object that's passed in to a handler implements the
ReadableStream interface."
However, afterwards it also says that
"Note: The request object is an instance of IncomingMessage.".
So how is request also instance of IncomingMessage and also it implements RedableStream interface?
How would you achieve that in JS?
Also, I thought in JS there were no interfaces?
If you look at the node.js source for an http incoming message, you see this:
/* Abstract base class for ServerRequest and ClientResponse. */
function IncomingMessage(socket) {
Stream.Readable.call(this);
....
}
and this:
util.inherits(IncomingMessage, Stream.Readable);
Which shows that an IncomingMessage constructor inherits from a Stream.Readable. It also overrides some of the readable methods to modify their behavior.
How would you achieve that in JS?
You would create your own object that inherits from another.
An object can both "implement" and "be an instance of" something, but the interface is just for documentation purposes.
In JS, to be an instance of something means that obj instanceof Something is true.
In dynamic languages, like JavaScript, to implement an interface means the object is expected to have certain methods. This is commonly known as duck-typing.
If an IncommingMessage behaves like a ReadableStream, and can be used as a ReadableStream, then it must be a ReadableStream.
JavaScript's class syntax, added in ES6, apparently makes it legal to extend null:
class foo extends null {}
Some Googling reveals that it was suggested on ES Discuss that such declarations be made an error; however, other commenters argued for them to be left legal on the basis that
someone might want to create a class that has a {__proto__: null} prototype
and that side of the argument ultimately prevailed.
I can't make much sense of this hypothetical use case. For one thing, while the declaration of such a class is legal, it seems that instantiating a class declared in this way isn't. Trying to instantiate the class foo from above in Node.js or Chrome gives me the wonderfully daft error
TypeError: function is not a function
while doing the same in Firefox gives me
TypeError: function () {
} is not a constructor
It doesn't help to define a constructor on the class, as shown in MDN's current example of the feature; if I try to instantiate this class:
class bar extends null {
constructor(){}
}
then Chrome/Node tell me:
ReferenceError: this is not defined
and Firefox tells me:
ReferenceError: |this| used uninitialized in bar class constructor
What is all this madness? Why are these null-extending classes not instantiable? And given that they're not instantiable, why was the possibility of creating them deliberately left in the spec, and why did some MDN author think that it was noteworthy enough to document? What possible use case is there for this feature?
EDIT (2021): TC39, which specifies JavaScript still hasn't resolved exactly how this is supposed to work. That needs to happen before browsers can consistently implement it. You can follow the latest efforts here.
Original answer:
Instantiating such classes is meant to work; Chrome and Firefox just have bugs. Here's Chrome's, here's Firefox's. It works fine in Safari (at least on master).
There used to be a bug in the spec which made them impossible to instantiate, but it's been fixed for a while. (There's still a related one, but that's not what you're seeing.)
The use case is roughly the same as that of Object.create(null). Sometimes you want something which doesn't inherit from Object.prototype.
To answer the second part:
I can't make much sense of this hypothetical use case.
That way, your object won't have Object.prototype in its prototype chain.
class Hash extends null {}
var o = {};
var hash = new Hash;
o["foo"] = 5;
hash["foo"] = 5;
// both are used as hash maps (or use `Map`).
hash["toString"]; // undefined
o["toString"]; // function
As we know, undefined in fact is not a function. In this case we can create objects without fearing a call on a field that shouldn't be there.
This is a common pattern through Object.create(null) and is common in a lot of code bases including big ones like NodeJS.
It is in fact possible to construct a class that extends null, as such
class bar extends null{
constructor(){
super() //required
}
}
//super is not a function
//change the super class (for static methods only)
Object.setPrototypeOf(bar, class{})
let instance = new bar()
//instances still have null prototype
console.log(instance.toString) //undefined
Given the class is extended from non-class (including, but not limited to, function),
function Fn() {}
class Class extends Fn {
constructor() {
super();
}
}
what are the the consequences? What do the specs say on that?
It looks like the current implementations of Babel, Google V8 and Mozilla Spidermonkey are ok with that, and TypeScript throws
Type '() => void' is not a constructor function type
If this is a valid ES2015 code, what's the proper way to handle it in TypeScript?
TypeScript Part
Up to now, the spec says a extends claus must be followed by a TypeReference. And a TypeReference must be in the form of A.B.C<TypeArgument>, like MyModule.MyContainer<MyItem>. So, syntactically your code is right. But it is not the typing case.
The spec says the BaseClass must be a valid typescript class. However, the spec is outdated, as said here. Now TypeScript allows expressions in extends clause, as long as expressions are computed to a constructor function. The definition is, well, implementation based. You can see it here. Simply put, a expression can be counted as constructor if it implements new() {} interface.
ES2015 Part
So, your problem is plain function is not recognized as constructor in TypeScript, which is arguable because ES2015 spec only requires the object has a [[construct]] internal method. While user-defined function object does have it.
ES2015 requires BaseClass is a constructor at runtime. An object isConstructor if it has [[construct]] internal methd. The spec says [[construct]] is an internal method for Function Object. User functions are instances of Function Objects, so naturally they are constructor. But builtin function and arrow function can have no [[construct]].
For example, the following code will throw runtime TypeError because parseInt is a builtin function and does not have [[construct]]
new parseInt()
// TypeError: parseInt is not a constructor
And from ECMAScript
Arrow functions are like built-in functions in that both lack .prototype and any [[Construct]] internal method. So new (() => {}) throws a TypeError but otherwise arrows are like functions:
As a rule of thumb, any function without prototype is not new-able.
Work Around
In short, not every function is constructor, TypeScript captures this by requiring new() {}. However, user-defined function is constructor.
To work around this, the easiest way is declare Fn as a variable, and cast it into constructor.
interface FnType {}
var Fn: {new(): FnType} = (function() {}) as any
class B extends Fn {}
Reasoning the incompatiblity
DISCALIMER: I'm not a TypeScript core contributor, but just a TS fan who has several side project related to TS. So this section is my personal guess.
TypeScript is a project originated in 2012, when ES2015 was still looming in dim dark. TypeScript didn't have a good reference for class semantics.
Back then, the main goal of TypeScript was to keep compatible with ES3/5. So, newing a function is legal in TypeScript, because it is also legal in ES3/5. At the same time, TypeScript also aims to capture programming errors. extends a function might be an error because the function might not be a sensible constructor (say, a function solely for side effect). extends did not even exist in ES3/5! So TypeScript could freely define its own usage of extends, making extends must pair with class variable. This made TypeScript more TypeSafe, while being compatible with JavaScript.
Now, ES2015 spec is finalized. JavaScript also has a extends keyword! Then incompatibility comes. There are efforts to resolve incompatibility. However, still problems exist. () => void or Function type should not be extendsable, as stated above due to builtin function. The following code will break
var a: (x: string) => void = eval
new a('booom')
On the other hand, if a ConstructorInterface was introduced into TypeScript and every function literal implemented it, then backward incompatibility would emerge. The following code compiles now but not when ConstructorInterface was introduced
var a = function (s) {}
a = parseInt // compile error because parseInt is not assignable to constructor
Of course, TS team can have a solution that balancing these two options. But this is not a high priority. Also, if salsa, the codename for TS powered JavaScript, is fully implemented. This problem will be solved naturally.
what are the the consequences? What do the specs say on that?
It is the same as extending a "EcmaScript 5" class. Your declare a constructor function and no prototype at all. You can extend it without any problem.
But for TypeScript, there is a big difference between function Fn() {} and class Fn {}. The both are not of the same type.
The first one is a just a function returning nothing (and TypeScript show it with the () => void). The second one is a constructor function. TypeScript refuse to do an extends on a non constructor function.
If one day javascript refuse to do that, it will break many javascript codes. Because at the moment, function Fn() {} is the most used way to declare a class in pure javascript. But from the TypeScript point of view, this is not "type safe".
I think the only way for TypeScript is to use a class :
class Fn {}
class Class extends Fn {
constructor() {
super();
}
}
I'm not sure I answer your question but I was very interested how to extend a JS function by a TypeScript class so I tried following:
fn.js (note the .js extension!)
function Fn() {
console.log("2");
}
c.ts
declare class Fn {}
class C extends Fn {
constructor() {
console.log("1");
super();
console.log("3");
}
}
let c = new C();
c.sayHello();
Then I ran:
$ tsc --target es5 c.ts | cat fn.js c.js | node # or es6
and the output is:
1
2
3
Hello!
Note, that this is not code for production use but rather a workaround for cases when you don't have time to convert some old JS file to TypeScript.
If I was in OP situation, I would try to convert Fn to a class because it makes code easier for others in a team.
This is indirectly related to TypeScript interface to describe class
Although the typeof a class is function, there is no Class (nor Interface) in Typescript which is the super class of all classes.
functions however are all interfaced by Function
I guess to be consistent with Javascript, Function should be probably be a class, all functions should be of that class and all classes should extend it...
I am new to js OO programming and I can't find the solution to this error.
I am declaring the following class hierarchy:
function FML_Field(id){
this.id= id;
this.optional= true;
this.node= null;
if(this.id === undefined){
throw ""; //should provide Id;
}
var this.node= document.getElementById(this.id);
if(this.node === null){
throw "";
}
this.setAsOptional= function(){
this.optional= true;
};
this.setAsRequired= function(){
this.optional= false;
};
this.isOptional= function(){
return this.optional;
};
}
and its son:
function FML_Text(id){
this.prototype= new FML_Field(id);
FML_Text.prototype.constructor= FML_Text;
this.maxLength= false;
this.minLength= false;
this.setMaxLength= function(maxLength){
this.maxLength= maxLength;
}
this.getMaxLength= function(){
return this.maxLength;
}
this.hasMaxLength= function(){
return this.maxLength !== false;
}
}
then I proceed with the following code:
var first_name = new FML_Text("first_name");
first_name.setAsRequired(); /*throws an error: setAsRequired is not defined*/
What's wrong? I've checked with the javascript console: first_name is defined but setAsRequired() isn't. The following function calls like first_name.setMaxLength() have no problems.
Thank you in advance.
Thank you in advance
This isn't how you set up inheritance:
function FML_Text(id){
this.prototype= new FML_Field(id);
// ...
}
All that does is create a property called prototype on the instance.
It's done like this:
function FML_Text(id){
// ...
}
FML_Text.prototype = new FML_Field();
...and you can't pass the id argument into it, because it happens before the child object constructor is called. Instead, the usual thing is to define an "initializer" function that each level in your hierarchy supports post-construction, and call that.
That's the basics of it, anyway, but really robust inheritance requires more work. For instance, actually making a call to the parent's version of a function that the child also has defined (e.g., a common initializer, or any time a child specializes a parent method) is actually a bit of a fiddle in JavaScript, and there are some other "gotchas" as well. But with a bit of plumbing, you can get very effective inheritance chains (including passing construction-time arguments to parent initializers).
Rather than flying solo, you might want to use one of the existing implementations of this stuff. I describe mine in this article from a while back, which features really efficient calls to parent methods ("supercalls"). The Prototype library also provides an effective "class" system although it was issues with the performance (and compatibility) of that system that lead me to do the article above. Dean Edwards has also written extensively on this subject, and John Resig has pitched in. I had issues with both of those when I looked at them a couple of years ago, but there may have been updates.
Things to look for (in my opinion):
Straightforward, declarative syntax.
Syntax that's very friendly to your class having private static members for implementation stuff that doesn't need to be public. (Private instance methods can be done in just about any system, but they're expensive; see Crockford's discussion of them, and my comparison of various ways to achieve them.)
The system should not rely on function decompilation (using the toString method on Function instances). Both Prototype's and Resig's do, I don't know about Edwards' as does Edwards'. Function decompilation has never been standardized and doesn't work on some mobile browsers. (In Resig's and Edwards' versions, the toString call is implicit, and so it's a bit hard to find, but it's there: They pass a function instance into a regex test, which will implicitly call the function's toString.)
The system should not create new function objects on-the-fly when calls to instance methods are made, only when classes are defined (if then). Prototype's does, every time you call an instance method that may need to call its parent's version (their magic $super argument). Their mechanism makes it dead easy to use the parent's version, but at the cost (again) of creating a new function on every call whether you actually call $super or not.