I’m working with the react-table library that provides a formatting function on a column definition. I’m trying to pass in a text column and a date column.
The library wants column definitions like
const cols = [
{
accessor: 'name',
// Value is a string
Cell: ({ value }) => value,
},
{
accessor: 'date',
// Value is a JS Date
Cell: ({ value }) => value.toLocaleString(),
},
]
I can define a record with a generic cell renderer, but then I have different types that can’t exist in a single array.
type cellValue('v) = {value: 'v};
type genericRenderer('v) = cellValue('v) => string;
let textRenderer: genericRenderer(string) = n => n.value;
let dateRenderer: genericRenderer(Js.Date.t) = d => d.value->Js.Date.toString;
// Compilation Error!
let columns = [|
{
"accessor": "name",
"Cell": textRenderer,
},
{
"accessor": "date",
"Cell": dateRenderer,
},
|];
I can define a variant that encapsulates my record types to solve that problem.
type tableColumn =
| TextColumn(textColumn)
| DateColumn(dateColumn)
However, I can’t pass in an array tableColumns to react-table. I need to convert from the variant/record to an array of native JS objects that react-table expects.
Is there a solution other than encoding my records to JSON and converting them to plain JS objects?
Feel free to ask here, if you have some concrete questions.
In general the idea is to finally have a common type you could use for all your objects in your array. Problems arise because in js polymorphic arrays are allowed, but not in rescript (directly).
The @unboxed approach means: use a type of a single variant having a type paramtere for your array, but skip the variant in the runtime representation of your code.
Also known as"unboxed GADT".
See another blog post
The abstract type approach means: you create external functions which let you create different objects which all have the same type. The drawback of this approach is, it’s harder to access values of this abstract type later on - if needed.
I agree that GADT’s aren’t really something you want to throw at a newcomer - or in fact at anybody.
@Hongbo What would differentiate Belt.eraseType from identity external?
If opaque would be a predefined type, then I don’t really see a benefit compared to identity external.
I understand we want to move away from objects: Therefore I guess a solution involving records and identity external could be prefered?
I’d suggest a similar approach to the example of @jfrolich (I consider having 'a in an identity external to be dangerous):