Test If Generic Input Matches Record Type

Hey folks, this is maybe a convoluted question and I’m sure there are several ways to get it done but I wonder if there’s an elegant/idiomatic way to approach this in ReScript.

I’m writing a ReScript/GenType library that will be consumed by TS/JS users. I have a core type in my ReScript project that looks like:

  @genType
  type rec t = {
    kind: [
      | #Primitive(string)
      | #Composite(({"context": renderContext, "props": props, "children": array<t>}) => t)
    ],
    props: props,
    children: array<t>,
  }

Now, one layer above I have a TypeScript API that calls into my ReScript project to create and assemble these records of type t. As part of that API, I need to be able to receive a function argument of arbitrary type and determine whether or not this argument is of type t. Essentially I need:

type isT = ('a) => bool;

where isT takes some input and returns true if typeof('a) === "t" (pseudocode, of course).

In JavaScript I could always do something like

function isT(a) {
  return typeof a === 'object' &&
    t.hasOwnProperty('kind') &&
    typeof t.kind === "..."
    ...etc
}

What’s the best way to accomplish this? (Preferably in ReScript but if this makes more sense in the TS/JS layer I can do that too). Thanks!

I would question this assumption a little. Can the TS/JS users not call a constructor function that you provide, that constructs a valid instance of t? In fact doesn’t @genType create exactly such a constructor?

1 Like

It can definitely. My TypeScript API is more flexible than my ReScript API… essentially I’m allowing people to invoke one of two TS overloads:

function makeThing(...xs : Array<t>);
function makeThing(props: any, ...xs : Array<t>);

So to implement this overload I have to be able to check, at runtime, “what is this first argument?” Because the props argument there has such arbitrary shape, it makes more sense to me to check “is this props thing of type t?”

The best way IMO is to use atd. It’s designed for JSON parsing but should work fairly well for this use case if there are only JSON compatible values in the record.

It operates using a definition file and uses it to generate both a type definition and code to validate Js.Json.t types. It’s all done in OCaml but that still works just fine in ReScript.

The instructions for getting this running with ReScript are a bit spread out, I need to make time to do a writeup. The most recent I know of was back when the project was still BuckleScript. For now the best place to start is the runtime library readme.

There are other helper libraries for parsing JSON that might also work.

2 Likes

Thanks, I appreciate that! I’ve found a workaround for the short term which is just to annotate my type with a specific property that I can look for/inspect on any incoming object to identify if it is indeed the type I’m looking for. It’s not bullet proof, but it works:

  @genType
  let isT = (a): bool => {
    switch Js.Types.classify(a) {
      | JSObject(_) =>
        switch Js.Types.classify(a["symbol"]) {
          | JSSymbol(s) => s === symbol
          | _ => false
        }
      | _ => false
    }
  }
1 Like