Description
If I create a class that depends on the behavior of a mixin, the code works fine when compiled into es6
but breaks when compiled into es5
.
I had created a mixin that allows classes to automatically unsubscribe in ngOnDestroy()
and it was working fine until I discovered that I needed to support IE 11, so I changed my target to es5
and noticed that the prototype chain was broken.
This problem does not happen when the class using the mixin doesn't need to know about the mixin, but in my case it did so the mixin function calls happened in the extends
clause, using Object
as its base, which isn't supported.
Script Version:
Nightly
Search Terms:
mixins es5
Code
I based this off https://mariusschulz.com/blog/mixin-classes-in-typescript but I added the code that makes it fail (in es5), that is, the User
class wants to use the methods getTimestamp()
and getCoolName()
from two mixins. For that reason, I couldn't just use
const TimestampedUserWithCoolName = Timestamped(HasCoolName(User))
;
type Constructor<T = {}> = new (...args: any[]) => T;
function Timestamped<TBase extends Constructor>(Base: TBase) {
return class extends Base {
getTimeStamp() { return Date.now(); }
};
}
function HasCoolName<TBase extends Constructor>(Base: TBase) {
return class extends Base {
getCoolName() { return "HotDiggityDog"; }
};
}
class User extends Timestamped(HasCoolName(Object)){
getUserName() {
return this.getCoolName() + this.getTimeStamp();
}
}
const user = new User();
// Errors in es5 but not in es6
user.getUserName();
// Also errors, it lost the whole prototype chain
user.getTimeStamp();
Expected behavior:
User
instances should have access to all three methods: getTimeStamp,getCoolName,getUserName
Actual behavior:
It works when compiled to es6
but throws a runtime error when compiled into es5
Workaround
Instead of using Object
as the base of the mixin chain in an extends clause, just use class {}
. See https://github.com/Microsoft/TypeScript/wiki/FAQ#why-doesnt-extending-built-ins-like-error-array-and-map-work
I realize this is very much related to the FAQ answer above, but it was not immediately obvious to me when I first read it. Thank God for the similar issues listed below when creating the questions. I decided to still create the issue in case someone else has the same problem since this workaround is a better one if people experience the problem I had where the base object didn't have to be Object
or Error
or Array
, it was just to be able to call mixins in the extend clause so the class can use the mixin methods.
Related Issues:
#33023 seems related since changing the mixin chain to Timestamped(HasCoolName(class{}))
worked. It convinced me that extending native objects is the root of my "problem".