Search code examples
javascriptffireasonbucklescriptreason-react

How to extend JS class in ReasonML


For example, I have an es6-like class:

class Foo {...}

And I want to extend it:

class Bar extends Foo {...}

In reason-react documentation I found examples, but I'm not sure that it's appropriate for me:

let component = ReasonReact.reducerComponent "TodoAppRe";
let make _children => {
  ...component,
  initialState: fun () => {count: 0},
  <...>

But when i try to write code in this style, i get an error:

let myclass unit => {
  ...mysuperclass,
    hello: fun () => {
      Js.log "FooBar";
    }
};

Error: Unbound record field update

(In this example mysuperclass is external from 3rd-party js-library).

Maybe I am doing something wrong?


Solution

  • let foo bar => { ...baz, quux: ... } is not a syntax for inheritance, it's a syntax for composition. Specifically, it's taking the baz record value (not object) and updating its quux member (not method).

    If you want to extend a JS class in Reason/BuckleScript, first keep in mind that BuckleScript generates code that's backwards-compatible with ES5, so that you don't need to use Babel. In fact BuckleScript doesn't directly support extending a class.

    But, you can include raw JavaScript in your Reason source code using BuckleScript's [%%bs.raw] attribute ( https://bucklescript.github.io/bucklescript/Manual.html#_embedding_raw_js_code_as_statements ):

    /* MyProject_Animal.re */
    
    [%%bs.raw {|
    
    // Dummy class for example:
    class Animal {
      speak() {}
    }
    
    // or:
    // import Animal from "somewhere";
    
    class Dog extends Animal {
      _name;
    
      constructor(name) {
        super();
        this._name = name;
      }
    
      speak() {
        console.log(`${this._name} says woof!!`);
        super.speak();
      }
    }
    |}];
    

    And then you can write a binding to the Dog class in the code below that:

    module Animal_Dog = {
      type t;
    
      [@bs.new] external make: string => t = "Dog";
      /* Need this to ensure correct usage in other modules */
      let make = make;
    
      [@bs.send] external speak: t => unit = "";
    };
    
    let spot = Animal_Dog.make("Spot");
    Animal_Dog.speak(spot);
    

    But, remember that since this ES2015, you will need to run it through Babel if you want it to be backwards-compatible with pre-ES2015 browsers.