Referencing [@bs.deriving abstract] type definitions in another Module


#1

I just refactored some [@bs.deriving abstract] type definitions into another module. I referenced the type definition this way:

type name = Types.name;

Doing this threw reference errors. The reference error went away when I used open instead:

open Types;

I know using open should be avoided.

How would you reference [@bs deriving abstract] type definitions in another module?

Also can anyone help me understand why doing something like type name = Types.name for Js.t type definitions works but doesnā€™t with [@bs.deriving abstract] type definitions?

Thanks!


#2

I have such module:

[@bs.deriving jsConverter]
type tFilterOperation = [ | `OR | `IN | `NOTIN];

module Filters = {
  [@bs.deriving abstract]
  type oldFilter = {
    operation: tFilterOperation,
    path: tField,
    value: array(string),
  };
  external jsonToOldFilter : Js.Json.t => oldFilter = "%identity";
};

In other file I reference it like this and it compiles:

let requestOldFilters = (oldFilterJson: Js.Json.t) => {
  let a = QueryOldTypes.Filters.jsonToOldFilter(oldFilterJson);
  let newA =
    QueryOldTypes.Filters.oldFilter(
      ~operation=`IN,
      ~path=QueryOldTypes.tField(~name="name"),
      ~value=[|"a", "c"|],
    );
  QueryOldTypes.Filters.operation(newA);
};

Maybe you have an old BuckleScript installed? BS 3.1.4 was released couple days ago.


#3

hey @gladimdim ! Thanks for your reply!
My BuckleScript version was 2.2.2 :grimacing:
Iā€™ve updated it to 3.1.4. :smiley:

Ya, I am still not sure why I canā€™t reference [@bs.deriving abstract] type in another module. ĀÆ\_(惄)_/ĀÆ

Iā€™ll share my code to be more clear on the issue:

Types.re

module Project = {
  [@bs.deriving abstract]
  type furnishing = {
    [@bs.optional] id: int,
    [@bs.optional] name: string
  };
};

In another file:

type furnishing = Types.Project.furnishing;

...

<div>
  (ReasonReact.string(self.state.furnishing |. name |> Utils.resolveOption))
</div>

I am getting error: ā€œThe value name canā€™t be foundā€.

If I have the type definition in the same file, the code compiles, or if I use open Types.Project it also compiles fine.


#4

Maybe you have to name it with full path to module and submodule? Can you try this:

<div>
  (ReasonReact.string(self.state.furnishing |. Types.Project.name |> Utils.resolveOption))
</div>

name is a constructor function, injected into Types.Project module by compiler. It is my understanding, but I am not an expert :smiley:


#5

Hey! That worked! Fascinating.

I donā€™t understand why a property in a type definition gets compiled to a constructor function. I wonder if there is a more concise and cleaner pattern to follow. Prefixing the full path to every reference seems verbose.

Thanks again for your help @gladimdim!


#6

I guess one way to make it cleaner would be to alias the path to a shorter Module name.


#7

there could be dozens of ā€˜nameā€™ entities in your current module. Imagine, you have 5 different modules with 5 different types, all of them have ā€˜nameā€™ property. When you specify a full path to module, where type is defined, compiler knows exactly, which ā€˜nameā€™ constructor should be used.

I am glad, that I could help. I started doing this abstract stuff today :smiley:


#8

Thatā€™s a very good point. :thinking:

So letā€™s say you have a module that looks like this:

Types.re

[@bs.deriving abstract]
type company = {
  name: string,
  address: string
};

[@bs.deriving abstract]
type person = {
  name: string,
  age: int
};

Do you think you can have two type definition that both have property called ā€œnameā€ in the same module? Because I donā€™t think you can specify the path like Types.person and Types.company.

Or do you think the best way is to just have them in separate modules like this?

Types.re

module Company = {
  [@bs.deriving abstract]
  type company = {
    name: string,
    address: string
  };
};

module Person = {
  [@bs.deriving abstract]
  type person = {
    name: string,
    age: int
  };
};

And reference the type like so: Types.Person and Types.Company?


#9

Iā€™d suggest reading over these docs to understand whatā€™s going on.

The shadowing issue (two fields called name or even a type called name and a field called name) is the biggest issue with this right now. Weā€™re working on solutions but nesting the types inside modulesā€™ a good idea for now :slight_smile:


#10

Gotcha. Thanks!