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?
Inheriting from two classes can be done by creating a parent object as a combination of two parent prototypes. The syntax for subclassing makes it possible to do that in the declaration, since the right-hand side of the extends clause can be any expression.
The ES6 JavaScript supports Object-Oriented programming components such as Object, Class and Methods. Further in Classes we can implement inheritance to make child inherits all methods of Parent Class. This can be done using the extends and super keywords. We use the extends keyword to implement the inheritance in ES6.
TypeScript supports single inheritance and multilevel inheritance. We can not implement hybrid and multiple inheritances using TypeScript. The inheritance uses class-based inheritance and it can be implemented using extends keywords in typescript.
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.
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.
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 {}
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, new
ing 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();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With