How to extend JavaScript HTMLElement class in ReasonML for web component? - class

For the following JavaScript code, how can I write it in ReasonML?
class HelloWorld extends HTMLElement {
constructor() {
super();
// Attach a shadow root to the element.
let shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = `<p>hello world</p>`;
}
}
I could not find any documentation on writing classes in ReasonML? I cannot use plain objects/types as I need to extend from HTMLElement class which doesn't work with ES style classes.
I have looked into this existing question - How to extend JS class in ReasonML however, it is a different thing. To write web component, we need to extend HTMLElement and must call it with new keyword. ES5 style extension mechanism doesn't work.

You can't. Not directly at least, since BuckleScript (which Reason uses to compile to JavaScript) targets ES5 and therefore has no knowledge of ES6 classes.
Fortunately, ES6-classes require no special runtime support, but are implemented as just syntax sugar, which is why you can transpile ES6 to ES5 as shown in the question you link to. All you really have to do then, is to convert this transpiled output into ReasonML:
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var BaseElement = (function (_super) {
__extends(BaseElement, _super);
function BaseElement() {
_super.call(this);
}
return BaseElement;
}(HTMLElement));
And depending on what specific class-features you actually need, you can probably simplify it a bit.

Related

Is there a way around to create TypeScript classes in closures?

I'm using IntelliJ IDEA's File Watcher to automatically compile the TypeScript files, but for some reason it's not liking classes defined within blocks / function closures:
Is there a way around this without having to move everything to the top-level / global scope?
Using the following code in TypeScript results in practically the same JavaScript that you appear to be aiming for...
namespace MY_NAMESPACE {
export class AssetService {
}
}
Resulting code:
var MY_NAMESPACE;
(function (MY_NAMESPACE) {
var AssetService = (function () {
function AssetService() {
}
return AssetService;
}());
MY_NAMESPACE.AssetService = AssetService;
})(MY_NAMESPACE || (MY_NAMESPACE = {}));
If you want to really reduce the scope, switch to external modules (AKA "modules" these days).
If you don't export the class from the module/file, it won't be visible globally, i.e. there's no reason to enclose class definitions in function scopes.
More about modules in TS: https://www.typescriptlang.org/docs/handbook/modules.html

Class, Interface, Prototype, and else

I'm creating a JavaScript Framework for making applications in the classic Object-Oriented Programming (so with classes/interfaces instead of only prototypes). However I still have a problem with giving names to those.
For instance :
var Bidule = function() { /*...*/ } ;
Bidule.prototype = { /*...*/ } ;
Is Bidule a OOP Class as a class is a constructor ? Or is it only a constructor ?
As classes don't have prototypes, I don't think it could be called a true class.
So it means I should call them both 'Constructors'. However, what about an interface or an abstract class ? What is the correct word if they aren't constructors ? And is an interface a kind of class ?
I've designed constructor functions, classes and interfaces in my Framework. However I need a name to regroup all of theme so we may build one of those like this :
var Bidule = new _NAME_.Class(/*...*/) ; // Where _NAME_ is the name of the container of class/function/interface.
I was thinking of "Concept" but I'm not sure and would like to know your opinions.
I also named "visibility" the common word for describing public/private class,
"type" for static/abstract/interface/singleton/final class. Are those correct ?
And one last question : Is there a difference between the verb 'extend' and 'inherit' ?
Thanks in advance for your answers.
Javascript is not a traditional OO language, it is prototyped. Some concepts about OO cannot be applied to js for this reason.
Bidule is your type, so it is your "class". The constructor is the function you assined to the variable.
var Bidule = function() { /*...*/ } ;
Bidule.prototype = { /*...*/ } ;
var obj = new Bidule();
obj.foo = 'bar';
var obj2 = new Bidule();
obj2.foo = 'foo';
alert(obj instanceof Bidule); // true
alert(obj2 instanceof Bidule); // true
alert(obj.foo); // bar
alert(obj2.foo); // foo
Javascript does not support abstract classes or interfaces and no, interfaces are not some kind of classes. Interfaces defines contracts, or what your class does, but do not specify how.
Extend and inherit have the same meaning in this context.

Extending a class in another file

I have some TypeScript code that is being generated by a tool. I'd like to extend this class in another file. As of 0.9.1.1, what's the best way to go about this?
I thought maybe I could staple my additional functions onto the prototype, but this is giving various errors (which change depending what mood the compiler is in).
For example:
Foo.ts (generated by a tool)
module MyModule {
export class Dog { }
}
Bar.ts
module MyModule {
function bark(): string {return 'woof';}
Dog.prototype.bark = bark;
}
You cannot split a class definition between multiple files in TypeScript. However typescript understands how JavaScript works and will let you write idomatic JavaScript classes just fine:
module MyModule {
export function Dog(){};
}
module MyModule {
function bark(): string {return 'woof';}
Dog.prototype.bark = bark;
}
Try it online
One way around this is to use inheritance:
class BigDog extends Dog{
bark(){}
}
I have encountered your problem as well before, but I had some deeper problems. You can see from basarat's example, that simple functions can be added as an extension to the prototype, but when it comes to static functions, or other static values you might want to extend your (presumably third party) class, then the TSC will warn you, that there is no such method defined on the class statically.
My workaround was the following little hack:
module MyModule {
export function Dog(){};
}
// in the other file
if (typeof MyModule !== 'undefined'){
Cast<any>(MyModule.Dog).Create = ()=>{return new Dog();};
}
// where Cast is a hack, for TS to forcefully cast types :)
Cast<T>(element:any):T{ return element; }
This should cast MyModule.Dog, to an any object, therefore allowing attachment of any kinds of properties, functions.

Implementing TypeScript interface with bare function signature plus other fields

How do I write a class that implements this TypeScript interface (and keeps the TypeScript compiler happy):
interface MyInterface {
(): string;
text2(content: string);
}
I saw this related answer:
How to make a class implement a call signature in Typescript?
But that only works if the interface only has the bare function signature. It doesn't work if you have additional members (such as function text2) to be implemented.
A class cannot implement everything that is available in a typescript interface. Two prime examples are callable signatures and index operations e.g. : Implement an indexible interface
The reason is that an interface is primarily designed to describe anything that JavaScript objects can do. Therefore it needs to be really robust. A TypeScript class however is designed to represent specifically the prototype inheritance in a more OO conventional / easy to understand / easy to type way.
You can still create an object that follows that interface:
interface MyInterface {
(): string;
text2(content: string);
}
var MyType = ((): MyInterface=>{
var x:any = function():string { // Notice the any
return "Some string"; // Dummy implementation
}
x.text2 = function(content:string){
console.log(content); // Dummy implementation
}
return x;
}
);
There's an easy and type-safe way to do this with ES6's Object.assign:
const foo: MyInterface = Object.assign(
// Callable signature implementation
() => 'hi',
{
// Additional properties
text2(content) { /* ... */ }
}
)
Intersection types, which I don't think were available in TypeScript when this question was originally asked and answered, are the secret sauce to getting the typing right.
Here's an elaboration on the accepted answer.
As far as I know, the only way to implement a call-signature is to use a function/method. To implement the remaining members, just define them on this function. This might seem strange to developers coming from C# or Java, but I think it's normal in JavaScript.
In JavaScript, this would be simple because you can just define the function and then add the members. However, TypeScript's type system doesn't allow this because, in this example, Function doesn't define a text2 member.
So to achieve the result you want, you need to bypass the type system while you define the members on the function, and then you can cast the result to the interface type:
//A closure is used here to encapsulate the temporary untyped variable, "result".
var implementation = (() => {
//"any" type specified to bypass type system for next statement.
//Defines the implementation of the call signature.
var result: any = () => "Hello";
//Defines the implementation of the other member.
result.text2 = (content: string) => { };
//Converts the temporary variable to the interface type.
return <MyInterface>result;
})(); //Invokes the closure to produce the implementation
Note that you don't need to use a closure. You could just declare your temporary variable in the same scope as the resulting interface implementation. Another option is to name the closure function to improve readability.
Here's what I think is a more realistic example:
interface TextRetriever {
(): string;
Replace(text: string);
}
function makeInMemoryTextRetriever(initialText: string) {
var currentText = initialText;
var instance: any = () => currentText;
instance.Replace = (newText: string) => currentText = newText;
return <TextRetriever>instance;
}
var inMemoryTextRetriever = makeInMemoryTextRetriever("Hello");

Mootools "Extends" plus "Implements"

I like to write my code slim and sexy (on the performance and memory side), I am using Mootools and was wondering if I was using it the correct way, also you can help me by telling me how to test my code to find the answers I am looking for my self.
//First we create a class like so:
var FirstClass = new Class {(
'someFunc': function() { /* do stuff */ }
})
//Now this class uses first class with "Implements"
var SecondClass = new Class ({
'Implements': [FirstClass, SomeOtherClass, SomeOtherOtherClass],
'iAlsoDoStuff': function() {/*do stuff */}
})
// finally the class that Extends second class
var ThirdClass = new Class ({
'Extends': SecondClass,
'takeOverTheWorld': function() {/*code to win lottery */}
})
How can I tell if every time secondclass is extended it doesnt make a new copy of the Implemented classes?
The reason I am doing what I am doing above is to Extend SecondClass for every class that needs it - doing so statically, while the second class cannot extend more then one class thus I am using Implements.
The main difference between Extends and Implements is that Implement changes the class's prototype, while Extend creates a copy. This means that if you implement a change into a class all instances of that class will inherit that change instantly, while if you use Extend then all existing instances will remain the same.
this is a quote from the mootorial, check it out. http://mootorial.com/wiki/mootorial/02-class/#implement-vs.-extend
as for the testing - I would very much recommend you build some sample cases with ninja classes and putting them on to http://www.jsfiddle.net - then ask for some analytical advice or the mootools mail list on google or on IRC (irc.freenode.net#mootools), SO does not seem to get many hits from the mootools core team. Ideally, you want to talk to somebody like aaron newton, arian, cpojer or rpflo :)
update: I even blogged about this but I was wrong. There simply is a difference in the order in which mutators like Extends and Implements are brought in. You can implement and extend but you need to declare Extends first for it to work.
Read more here: http://fragged.org/mootools-pattern-fun-class-implements-extends-at-the-same-time_1359.html
update Turns out, there are some cases where this is useful. Here is the problem:
var ninja = new Class({
kill: function() {
alert("kill!");
}
});
var human = new Class({
initialize: function(){
alert("i r human!");
}
});
var badass = new Class({
Implements: [ninja],
Extends: human,
initialize: function() {
alert("i r badass and.. ");
this.parent();
this.kill();
}
});
new badass(); // i r badass, i r human, this.kill is not a function exception.
... simply does not work. You need class human to implement ninja instead and class badass to simply extend human. Aside from the side-effect of humans getting a new kill method (which they may or may not know about), it will mean that badass will be able to use .kill as well as call upon his direct parent human.
Why not rewrite things the way you want them and w/o complications? Because you may be extending a native class like Request.JSONP and then decide to mixin a new storage class into your extended one. True story... Either way, you may not have the luxury of refactoring certain classes available to you.
An interesting pattern to overcome this (consider the human class your request.jsonp, defined elsewhere) - if you just want to add more methods and properties to the class you are extending but don't plan to reuse the mixin class (ninja):
human.implement(new new Class({
kill: function() {
alert("kill!");
}
}));
var badass = new Class({
Extends: human,
initialize: function() {
alert("i r badass and.. ");
this.parent();
this.kill();
}
});
new badass(); // // i r badass, i r human, kill!
Arguably, you could just do human.implement({ method: function }); but a class can be so much more.
If you want to have a saved reference to your ninja class for other uses, the above would would be the same as this (if you plan to reuse your mixin):
var ninja new Class({
kill: function() {
alert("kill!");
}
});
human.implement(new ninja);
// this is what differs from say - instantiation + shared inherited properties.
// also, a constructor will work.
// the alternative would just do:
// human.prototype.kill = function() { alert("kill"); }
var badass = new Class({
Extends: human,
initialize: function() {
alert("i r badass and.. ");
this.parent();
this.kill();
}
});
new badass(); // // i r badass, i r human, kill!
Hope this helps somebody. Here's a practical example where I am extending Request.JSONP with an additional storage class as a mixin: http://jsfiddle.net/dimitar/YacZe/
I finally got my answer on the Mootools google group, thought I would update it here in case some one finds interest in it.
http://groups.google.com/group/mootools-users/browse_thread/thread/5aec78813fa51cc1
Enjoy!
Roman
Extends and Implements are very well tested by Mootools developers themselves. Infact the whole test suite they use is available on anutron/mootools-unittester. You don't need to be testing core functionality of the framework, its done for you (and done very well too).
I'd suggest having a good read up on what Extend and Implement do on the mootools docs, clientcide website, mootorial, etc.
How many objects are you creating by the way? If its not a huge number then memory etc. should not be a major issue even if it was creating the objects in a memory heavy manner. Could this be premature optimisation?