<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Patrick Ecker</title>
    <description>The latest articles on DEV Community by Patrick Ecker (@ryyppy).</description>
    <link>https://dev.to/ryyppy</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F346153%2F756f26b6-7c5c-4f21-8e40-05fcd945c44a.jpeg</url>
      <title>DEV Community: Patrick Ecker</title>
      <link>https://dev.to/ryyppy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ryyppy"/>
    <language>en</language>
    <item>
      <title>ReScript records, NextJS, undefined and getStaticProps</title>
      <dc:creator>Patrick Ecker</dc:creator>
      <pubDate>Sat, 15 May 2021 12:46:07 +0000</pubDate>
      <link>https://dev.to/ryyppy/rescript-records-nextjs-undefined-and-getstaticprops-4890</link>
      <guid>https://dev.to/ryyppy/rescript-records-nextjs-undefined-and-getstaticprops-4890</guid>
      <description>&lt;p&gt;&lt;a href="https://nextjs.org/"&gt;NextJS&lt;/a&gt;, a pretty solid framework for building React based websites and web-applications, offers a nice feature for generating static pages from e.g. fetched endpoint data via the &lt;code&gt;getStaticProps&lt;/code&gt; API, which looks something like this in JavaScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;MyComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// props provided by getStaticProps&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getStaticProps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// do your async stuff here to fetch data&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="c1"&gt;// pass the data as props to your component&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is an important restriction though: The value defined as &lt;code&gt;props&lt;/code&gt; must be &lt;strong&gt;JSON serializable&lt;/strong&gt;. JavaScript objects usually resemble JSON data by default, so oftentimes this isn't really an issue. There are still some subtle cases where confusing errors pop up, so this article will describe a typical error scenario a ReScript developer will most likely face when working with NextJS.&lt;/p&gt;

&lt;h2&gt;
  
  
  The JSON Issue w/ undefined values
&lt;/h2&gt;

&lt;p&gt;Let's assume we want to use the &lt;code&gt;getStaticProps&lt;/code&gt; API and return some props based on a ReScript record:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c"&gt;// ReScript code&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;recipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;diet_kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;getStaticProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;_&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;recipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;diet_kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c"&gt;//last statement is the return value&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="s"&gt;"props"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;recipe&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As soon as you compile this code and boot up the Next development server, you will get an error similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Error serializing `.diet_kind` returned from `getStaticProps` in "/".

ReScript: `undefined` cannot be serialized as JSON. Please use `null` or omit this value all together.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's explain what's going on here.&lt;/p&gt;

&lt;p&gt;First of all, a ReScript record will compile to a JS object with the same object structure. The &lt;code&gt;diet_kind&lt;/code&gt; defined as &lt;code&gt;option&amp;lt;string&amp;gt;&lt;/code&gt; may be one of two different values, which compile to the following JS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Some("My Diet Name")&lt;/code&gt; will be compiled to &lt;code&gt;"My Diet Name"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;None&lt;/code&gt; will be compiled to &lt;code&gt;undefined&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As soon as I construct a recipe value &lt;code&gt;{ diet_kind: None }&lt;/code&gt;, it will be compiled to &lt;code&gt;{ diet_kind: undefined }&lt;/code&gt;, which is not a well defined JSON value.&lt;/p&gt;

&lt;p&gt;There are two solutions on how to tackle this (as already noted by the error message above), and I'd like to show how to do this specifically in ReScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use &lt;code&gt;null&lt;/code&gt; instead of &lt;code&gt;undefined&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Instead of using &lt;code&gt;option&lt;/code&gt;, we need to fall back to the JS specific interop type &lt;code&gt;Js.Nullable.t&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;recipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;diet_kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Js&lt;/span&gt;&lt;span class="py"&gt;.Nullable&lt;/span&gt;&lt;span class="nf"&gt;.t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;myRecipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;diet_kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Js&lt;/span&gt;&lt;span class="py"&gt;.Nullable.null&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will compile &lt;code&gt;myRecipe&lt;/code&gt; into &lt;code&gt;{ diet_kind: null }&lt;/code&gt; in JS, which now is valid JSON. This solution is functional but somewhat impractical. Pure ReScript code doesn't have any &lt;code&gt;null&lt;/code&gt; values and uses &lt;code&gt;options&lt;/code&gt; everywhere for expressing existing / non-existing values. So every time we want to use a Next based API, we'd need to map and convert those &lt;code&gt;options&lt;/code&gt; to nullable types back and forth.&lt;/p&gt;

&lt;p&gt;For example, on the component side, we'd now need to handle the &lt;code&gt;recipe&lt;/code&gt; value like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;recipe&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c"&gt;// Interop React component without react.component ppx&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;props&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;React&lt;/span&gt;&lt;span class="py"&gt;.element&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="c"&gt;// Convert diet_kind back to option&lt;/span&gt;
   &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;diet_kind&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Js&lt;/span&gt;&lt;span class="py"&gt;.Nullable&lt;/span&gt;&lt;span class="nf"&gt;.toOption&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;props&lt;/span&gt;&lt;span class="py"&gt;.diet_kind&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

   &lt;span class="nf"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diet_kind&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Js&lt;/span&gt;&lt;span class="nf"&gt;.log2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"here is the kind: "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Js&lt;/span&gt;&lt;span class="nf"&gt;.log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"No kind found"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;};&lt;/span&gt;

   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This doesn't really feel like a very accessible approach, so I was curious if there are other ways to do it. The alternative is a little bit more hacky and unsafe though.&lt;/p&gt;

&lt;h2&gt;
  
  
  Omit all &lt;code&gt;undefined&lt;/code&gt; values all together
&lt;/h2&gt;

&lt;p&gt;The other approach would be to completely strip every object attribute that is set to &lt;code&gt;undefined&lt;/code&gt;. This is actually hard to do for pure ReScript code, since the JS object representation of a record will always maintain the full structure with all its attributes attached, even if they are undefined.&lt;/p&gt;

&lt;p&gt;That's why we'll need to go the JS interoperability route and use unsafe code to strip away those values. &lt;code&gt;getStaticProps&lt;/code&gt; is only used during build time, so I think it's okay to use the &lt;code&gt;JSON.stringify&lt;/code&gt; / &lt;code&gt;JSON.parse&lt;/code&gt; functions to do the dirty work for us. As a reference, that's what we want to do in JS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// json = { b: "test" }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, &lt;code&gt;JSON.stringify&lt;/code&gt; drops all the values that are not part of the JSON definition. It's also a quite generic approach, since it strips all non-JSON values recursively, and can be applied to any json data &lt;code&gt;object&lt;/code&gt; or &lt;code&gt;array&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before proceeding to the interesting part, here's a word of caution:&lt;/strong&gt; In ReScript it's often a trade-off between type-safety and convenience. The solutions proposed here are &lt;strong&gt;unsafe by design&lt;/strong&gt; for practical reasons. If you are striving for a 100% type-safe codebase, this approach is not for you.&lt;/p&gt;

&lt;p&gt;---------------------UNSAFE PART FROM HERE ---------------------&lt;/p&gt;

&lt;h3&gt;
  
  
  Somewhat unsafe &lt;code&gt;undefined&lt;/code&gt; stripping for ReScript records
&lt;/h3&gt;

&lt;p&gt;Let's get back to our recipe example with &lt;code&gt;option&lt;/code&gt; values. We will start out putting everything in a &lt;code&gt;Recipe&lt;/code&gt; module so we can easily add related functions for our recipe data type &lt;code&gt;t&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="n"&gt;Recipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;diet_kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's define an independent function &lt;code&gt;stripUndefined&lt;/code&gt; for the stringify / parse logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;stripUndefined&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Js&lt;/span&gt;&lt;span class="py"&gt;.Json.t&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;Js&lt;/span&gt;&lt;span class="py"&gt;.Json.t&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;open&lt;/span&gt; &lt;span class="n"&gt;Js&lt;/span&gt;&lt;span class="py"&gt;.Json&lt;/span&gt;
  &lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;parseExn&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since &lt;code&gt;stripUndefined&lt;/code&gt; defines &lt;code&gt;Js.Json.t&lt;/code&gt; values as its input and output, we need to add &lt;strong&gt;very unsafe external functions&lt;/strong&gt; to our &lt;code&gt;Recipe&lt;/code&gt; module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="n"&gt;Recipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;diet_kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="n"&gt;external&lt;/span&gt; &lt;span class="n"&gt;fromJson&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Js&lt;/span&gt;&lt;span class="py"&gt;.Json.t&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"%identity"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;external&lt;/span&gt; &lt;span class="n"&gt;toJson&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Js&lt;/span&gt;&lt;span class="py"&gt;.Json.t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"%identity"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;stripUndefined&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Js&lt;/span&gt;&lt;span class="py"&gt;.Json.t&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;Js&lt;/span&gt;&lt;span class="py"&gt;.Json.t&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Js&lt;/span&gt;&lt;span class="py"&gt;.Json&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;parseExn&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; As mentioned before, a ReScript record is represented as a JS object during runtime (so basically a JSON structure, if we only use JSON related values inside). With the &lt;code&gt;fromJson&lt;/code&gt; and &lt;code&gt;toJson&lt;/code&gt; identity externals, we are &lt;em&gt;lying&lt;/em&gt; to the compiler that our type &lt;code&gt;Recipe.t&lt;/code&gt; is equivalent to a &lt;code&gt;Js.Json.t&lt;/code&gt;, so this is completely unsafe and it should be made sure that your &lt;code&gt;type t&lt;/code&gt; really is handling values that are Json compliant (except undefined values of course). That means you should only use values such as &lt;code&gt;string&lt;/code&gt;, &lt;code&gt;float&lt;/code&gt;, &lt;code&gt;object&lt;/code&gt;, &lt;code&gt;array&lt;/code&gt;, and of course &lt;code&gt;option&lt;/code&gt; (but no other values such as functions, otherwise they will be stripped as well).&lt;/p&gt;

&lt;p&gt;Now let's combine all these things together to see it in action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;myRecipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Recipe&lt;/span&gt;&lt;span class="py"&gt;.title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"My Recipe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;diet_kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;recipe&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;// No convertion from Js.Nullable needed anymore!&lt;/span&gt;
  &lt;span class="nf"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diet_kind&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Js&lt;/span&gt;&lt;span class="nf"&gt;.log2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"here is the kind: "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Js&lt;/span&gt;&lt;span class="nf"&gt;.log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"No kind found"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c"&gt;// Simulating the getStaticProps Api without any types here&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;getStaticProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;_&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;open&lt;/span&gt; &lt;span class="n"&gt;Recipe&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c"&gt;// Clear our recipe from any undefined values before returning&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myRecipe&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;stripUndefined&lt;/span&gt;&lt;span class="k"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;fromJson&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="s"&gt;"props"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;props&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it! After compiling and rerunning the code, the error is gone and our app works as expected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you are handling an &lt;code&gt;array(recipe)&lt;/code&gt;, you can apply the same technique to the whole array in an &lt;code&gt;Js.Json.t&lt;/code&gt; as well, since stringify / parse can act on json objects and json arrays.&lt;/p&gt;

&lt;p&gt;It's also noteworthy that the last &lt;code&gt;fromJson&lt;/code&gt; call is not needed when we are not enforcing any type shapes for the &lt;code&gt;props&lt;/code&gt; value. In my typical ReScript / Next projects (see my &lt;a href="https://github.com/ryyppy/rescript-nextjs-template"&gt;rescript-nextjs-template&lt;/a&gt; ReScript template) I enforce the props type across &lt;code&gt;getStaticProps&lt;/code&gt; and my React components.&lt;/p&gt;

&lt;p&gt;(As long as you are making sure that &lt;code&gt;Recipe.t&lt;/code&gt; contains JSON compliant values, it's also perfectly safe to continue using the modified record returned by &lt;code&gt;toJson(myRecipe)-&amp;gt;stripUndefined-&amp;gt;fromJson&lt;/code&gt; in ReScript code, since all runtime operations on an &lt;code&gt;option&lt;/code&gt; value will continue to work as intended.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We highlighted the problems with NextJS' &lt;code&gt;getStaticProps&lt;/code&gt; props value limitations and why this is relevant for writing idiomatic ReScript code that interops well within the framework.&lt;/p&gt;

&lt;p&gt;We showed how we can tackle the JSON serialization problem, either by converting &lt;code&gt;option&lt;/code&gt; to &lt;code&gt;Nullable.t&lt;/code&gt;, or do unsafe &lt;code&gt;undefined&lt;/code&gt; value stripping using &lt;code&gt;JSON.stringify&lt;/code&gt; / &lt;code&gt;JSON.parse&lt;/code&gt;. We also dabbled a little bit into the world of compile type / runtime value representation of ReScript records.&lt;/p&gt;

&lt;p&gt;If you are interested in more ReSCript related content, make sure to &lt;a href="https://twitter.com/ryyppy"&gt;follow me on Twitter&lt;/a&gt; and stay tuned for more practical insights!&lt;/p&gt;

</description>
      <category>rescript</category>
      <category>javascript</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Tagged Unions and ReScript Variants</title>
      <dc:creator>Patrick Ecker</dc:creator>
      <pubDate>Sat, 15 May 2021 10:57:42 +0000</pubDate>
      <link>https://dev.to/ryyppy/tagged-unions-and-rescript-variants-5e4d</link>
      <guid>https://dev.to/ryyppy/tagged-unions-and-rescript-variants-5e4d</guid>
      <description>&lt;p&gt;In JavaScript there are many situations where we want to express certain shapes of an object based on the conditions of its attributes, e.g.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Plain JS - Typical Redux Action types&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;addUser&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;createUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;removeUser&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;removeUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find this pattern in many other scenarios, such as representing the method of a request (&lt;code&gt;req.method === "POST"&lt;/code&gt; -&amp;gt; &lt;code&gt;req.body != null&lt;/code&gt;), representing UI state (&lt;code&gt;userReq.isLoading&lt;/code&gt; -&amp;gt; &lt;code&gt;userReq.name == undefined&lt;/code&gt;), or even error state (&lt;code&gt;result.err != null&lt;/code&gt; -&amp;gt; &lt;code&gt;result.msg != undefined&lt;/code&gt;). The shape of the object is different, depending on the state of attributes defined by a specific ruleset.&lt;/p&gt;

&lt;p&gt;In TypeScript, we'd use a so called Discriminated Union Type (Tagged Unions) to be able to encode the conditional object shape within the type itself. For our previous example, we would define a type for a user &lt;code&gt;action&lt;/code&gt; like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// TypeScript&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;AddUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;addUser&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;RemoveUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;removeUser&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;UserAction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;AddUser&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;RemoveUser&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a ReScript developer, you probably had troubles writing FFI (interop) code to represent such Tagged Unions. How are we able to handle these data structures without changing the JS representation?&lt;/p&gt;

&lt;p&gt;Usually we'd define a variant for representing different shapes of data, but unfortunately variants do not compile to the same shape of user defined Tagged Unions.&lt;/p&gt;

&lt;p&gt;This article demonstrates in a practical example how we'd map data structures for RichText data (designed as a Tagged Union) to ReScript variants.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; We'll only discuss mapping ReScript variants to immutable JS values, since mutations to the original values will eventually not be reflected in the variants at runtime. Handling mutable data requires a different strategy which is not covered in this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background on the Use-Case
&lt;/h2&gt;

&lt;p&gt;This post is based on a a real-world use-case where I needed to represent &lt;a href="https://www.storyblok.com/"&gt;Storyblok CMS'&lt;/a&gt; RichText data structures within ReScript but couldn't find any proper documentation on how to do this.&lt;/p&gt;

&lt;p&gt;I tried to keep the data model simple to only capture the basic concepts. For a more thorough side-by-side implementation of a TS / ReScript Storyblok RichText model, including rendering logic, you can check &lt;a href="https://github.com/ryyppy/richtext-blog-example"&gt;this repository&lt;/a&gt; later on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design RichText Data with TypeScript
&lt;/h2&gt;

&lt;p&gt;To kick things off, we'll define some basic RichText elements we want to be able to represent: &lt;code&gt;Text&lt;/code&gt;, &lt;code&gt;Paragraph&lt;/code&gt; and &lt;code&gt;Doc&lt;/code&gt;. These will be defined as a Tagged Union called &lt;code&gt;RichText&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Text&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Paragraph&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;paragraph&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RichText&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Doc&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;doc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RichText&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;RichText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;Doc&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;Text&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;Paragraph&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each case of the &lt;code&gt;RichText&lt;/code&gt; type listed above has one common attribute &lt;code&gt;type&lt;/code&gt;, which helps the type system to differentiate the shape of a given value by checking &lt;code&gt;value.type&lt;/code&gt;, e.g. via an &lt;code&gt;if&lt;/code&gt; or &lt;code&gt;switch&lt;/code&gt; statement. Let's see that in action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Recursively iterate through the RichText tree and print all Text.text contents&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;printTexts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RichText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;doc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;paragraph&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;printTexts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RichText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;   &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;doc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;paragraph&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text 1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;paragraph&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text 2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;printTexts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;TypeScript will be able to infer the relevant data for each case correctly &lt;em&gt;most of the time&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;There's a few things I personally dislike in TS when handling Tagged Unions (especially via &lt;code&gt;switch&lt;/code&gt; statements):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;switch&lt;/code&gt; statements are not expressions (can't return a value without wrapping a function around it)&lt;/li&gt;
&lt;li&gt;cases need extra braces to prevent variable hoisting and need a break / return statement to prevent case fall-through&lt;/li&gt;
&lt;li&gt;Without any return statements or other trickery, TS apparently does not do any exhaustive checks within switches&lt;/li&gt;
&lt;li&gt;Discriminated union types are really noisy in type space code and I often had a hard time navigating / writing types, even in smaller codebases&lt;/li&gt;
&lt;li&gt;switch statements can only match one value at once. More complex discriminants / multiple discriminants are impractical&lt;/li&gt;
&lt;li&gt;object types are structurally typed and TS will not always automatically infer the type correctly without type annotation (as seen in the &lt;code&gt;const input&lt;/code&gt; declaration above). Error messages are generally harder to read because of that.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;... but these are all just opinions.&lt;/p&gt;

&lt;p&gt;In the next step, let's discover how we'd represent that data model in ReScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  Representing Tagged Unions in ReScript
&lt;/h2&gt;

&lt;p&gt;We now have an existing RichText representation, and we want to write ReScript FFI (interop) code to represent the same data without changing the JS parts.&lt;/p&gt;

&lt;p&gt;ReScript's type system can't express Tagged Unions in the same  way as TypeScript does, so let's take a step back:&lt;/p&gt;

&lt;p&gt;The core idea of Tagged Unions is to express a "A &lt;strong&gt;or&lt;/strong&gt; B &lt;strong&gt;or&lt;/strong&gt; C"  relation and to access different data, depending on what branch we are currently handling. This is exactly what ReScript &lt;a href="https://rescript-lang.org/docs/manual/latest/variant"&gt;Variants&lt;/a&gt; are made for.&lt;/p&gt;

&lt;p&gt;So let's design the previous example with the help of variants. We will start defining our type model within our &lt;code&gt;RichText.res&lt;/code&gt; module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c"&gt;// RichText.res&lt;/span&gt;

&lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;case&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;Doc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="py"&gt;.t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;Paragraph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;Unknown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, there's no much going on here. Let's go through it really quick:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We defined a submodule &lt;code&gt;Text&lt;/code&gt;, with a &lt;code&gt;type t&lt;/code&gt; representing a Text RichText element. We refer to this type via &lt;code&gt;Text.t&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;type t;&lt;/code&gt; is representing our actual Tagged Union &lt;code&gt;RichText&lt;/code&gt; element. It doesn't have any concrete shape, which makes it an "abstract type". We'll also call this type &lt;code&gt;RichText.t&lt;/code&gt; later on.&lt;/li&gt;
&lt;li&gt;Lastly we defined our &lt;code&gt;case&lt;/code&gt; variant, describing all the different cases as defined by the Tagged Union in TS. Note how we also added an &lt;code&gt;Unknown(t)&lt;/code&gt; case, to be able to represent malformed / unknown RichText elements as well&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these types we can fully represent our data model, but we still need to classify incoming JS data to our specific cases. Just for a quick reminder: The &lt;code&gt;RichText.t&lt;/code&gt; type internally represents a JS object with following shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;...,&lt;/span&gt; &lt;span class="c1"&gt;// exists if type = "doc" | "paragraph"&lt;/span&gt;
   &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;...,&lt;/span&gt;    &lt;span class="c1"&gt;// exists if type = "text"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's add some more functionality to reflect on that logic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Classifying RichText.t data
&lt;/h3&gt;

&lt;p&gt;We will extend our &lt;code&gt;RichText.res&lt;/code&gt; module with the following functions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c"&gt;// RichText.res&lt;/span&gt;

&lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;case&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;Doc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="py"&gt;.t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;Paragraph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;Unknown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;getType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nf"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;
    &lt;span class="nf"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;typeof&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s"&gt;"object"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="py"&gt;.type&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="py"&gt;.type&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"unknown"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;getContent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nf"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;
    &lt;span class="nf"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;typeof&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s"&gt;"object"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="py"&gt;.content&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="py"&gt;.content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;classify&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;case&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="n"&gt;switch&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="k"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;getType&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"doc"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Doc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="k"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;getContent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="k"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Obj&lt;/span&gt;&lt;span class="py"&gt;.magic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"paragraph"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Paragraph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="k"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;getContent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"unknown"&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Unknown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code above shows everything we need to handle incoming &lt;code&gt;RichText.t&lt;/code&gt; values.&lt;/p&gt;

&lt;p&gt;Since we are internally handling a JS object and needed access to the &lt;code&gt;type&lt;/code&gt; and &lt;code&gt;content&lt;/code&gt; attributes, we defined two unsafe raw functions &lt;code&gt;getType&lt;/code&gt; and &lt;code&gt;getContent&lt;/code&gt;. Both functions receive a &lt;code&gt;RichText.t&lt;/code&gt; value to extract the appropriate attribute (while making sure our data is correctly shaped, otherwise we will end up with an &lt;code&gt;Unknown&lt;/code&gt; value).&lt;/p&gt;

&lt;p&gt;Now with those two functions in place, we are able to define the &lt;code&gt;classify&lt;/code&gt; function to refine our &lt;code&gt;RichText.t&lt;/code&gt; into &lt;code&gt;case&lt;/code&gt; values. It first retrieves the &lt;code&gt;type&lt;/code&gt; of the input &lt;code&gt;v&lt;/code&gt; and returns the appropriate variant constructor (with the correct payload). Since this code uses &lt;code&gt;raw&lt;/code&gt; functions and relies on &lt;code&gt;Obj.magic&lt;/code&gt;, it is considered to be unsafe code. For this particular scenario, the unsafe code is at least isolated in the &lt;code&gt;RichText&lt;/code&gt; module (make sure to write tests!).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You might have noticed that we store the &lt;code&gt;content&lt;/code&gt; part of a &lt;code&gt;"doc"&lt;/code&gt; object directly in the &lt;code&gt;Doc(array&amp;lt;t&amp;gt;)&lt;/code&gt; variant constructor. Since we know that our Doc model does not contain any other information, we went ahead and made our model simpler instead.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using the RichText module
&lt;/h3&gt;

&lt;p&gt;Now with the implementation in place, let's showcase how we'd iterate over &lt;code&gt;RichText&lt;/code&gt; data and print every &lt;code&gt;Text&lt;/code&gt; content within all paragraphs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c"&gt;// MyApp.res&lt;/span&gt;

&lt;span class="c"&gt;// We simulate some JS object coming into our system&lt;/span&gt;
&lt;span class="c"&gt;// ready to be parsed&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;RichText&lt;/span&gt;&lt;span class="py"&gt;.t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nf"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"doc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"paragraph"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"text 1"&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"paragraph"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"text 2"&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// keyword rec means that this function is recursive&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;rec&lt;/span&gt; &lt;span class="n"&gt;printTexts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;RichText&lt;/span&gt;&lt;span class="py"&gt;.t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RichText&lt;/span&gt;&lt;span class="nf"&gt;.classify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;Doc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;Paragraph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Belt&lt;/span&gt;&lt;span class="py"&gt;.Array&lt;/span&gt;&lt;span class="nf"&gt;.forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;printTexts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Js&lt;/span&gt;&lt;span class="nf"&gt;.log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;Unknown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Js&lt;/span&gt;&lt;span class="nf"&gt;.log2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unknown value found: "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nf"&gt;printTexts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see in the &lt;code&gt;printTexts&lt;/code&gt; function above, we call the function &lt;code&gt;RichText.classify&lt;/code&gt; on the input parameter, for the &lt;code&gt;Doc | Paragraph&lt;/code&gt; branch we can safely unify the &lt;code&gt;content&lt;/code&gt; payload (which both are of type &lt;code&gt;array&amp;lt;RichText.t&amp;gt;&lt;/code&gt;) and recursively call the &lt;code&gt;printTexts&lt;/code&gt; function again. In case of a &lt;code&gt;Text&lt;/code&gt; element, we can deeply access the record attribute &lt;code&gt;RichText.Text.text&lt;/code&gt;, and for every other &lt;code&gt;Unknown&lt;/code&gt; case, we directly log the &lt;code&gt;value&lt;/code&gt; of type &lt;code&gt;RichText.t&lt;/code&gt;, which is the original JS object (&lt;code&gt;Js.log&lt;/code&gt; is able to log any value, no matter which type).&lt;/p&gt;

&lt;p&gt;In contrast to the TS &lt;code&gt;switch&lt;/code&gt; statement, let's talk about the control flow structures here (namely the ReScript &lt;code&gt;switch&lt;/code&gt; statement):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;switch&lt;/code&gt; is an expression. The last statement of each branch is the return value. You can even assign it to a binding (&lt;code&gt;let myValue = switch("test") {...}&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Each branch must return the same type (forces simpler designs)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The most important part is, that we have the full power of &lt;a href="https://rescript-lang.org/docs/manual/latest/pattern-matching-destructuring"&gt;Pattern Matching&lt;/a&gt;, which can be performed on any ReScript data structure (numbers, records, variants, tuples,...). Here is just one small example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nf"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RichText&lt;/span&gt;&lt;span class="nf"&gt;.classify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;Doc&lt;/span&gt;&lt;span class="p"&gt;([])&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Js&lt;/span&gt;&lt;span class="nf"&gt;.log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"This document is empty"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;Doc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Belt&lt;/span&gt;&lt;span class="py"&gt;.Array&lt;/span&gt;&lt;span class="nf"&gt;.forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;printTexts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"text 1"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Js&lt;/span&gt;&lt;span class="nf"&gt;.log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"We ignore 'text 1'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Js&lt;/span&gt;&lt;span class="nf"&gt;.log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Text we accept: "&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="cm"&gt;/* "Do nothing" */&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Doc([])&lt;/code&gt;: "Match on all Doc elements with 0 elements in its content&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Doc(content)&lt;/code&gt;: "For every other content (&amp;gt; 0) do the following..."&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Text({text: "text 1"})&lt;/code&gt;: "Match on all Text elements where element.text = 'text 1'"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Text({text})&lt;/code&gt;:  "For every other Text element with a different text do the following ..."&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;_ =&amp;gt; ()&lt;/code&gt;: "For everything else &lt;code&gt;_&lt;/code&gt; do nothing &lt;code&gt;()&lt;/code&gt;"&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Extending the RichText data model
&lt;/h3&gt;

&lt;p&gt;Whenever we want to extend our data model, we just add a new variant constructor to our &lt;code&gt;case&lt;/code&gt; variant, and add a new pattern match within our &lt;code&gt;classify&lt;/code&gt; function. E.g.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;case&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;Doc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="py"&gt;.t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;Paragraph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;BulletList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// &amp;lt;-- add a constructor here!&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;Unknown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;classify&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;case&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nf"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="k"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;getType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"doc"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Doc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="k"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;getContent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="k"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Obj&lt;/span&gt;&lt;span class="py"&gt;.magic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"paragraph"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Paragraph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="k"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;getContent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"bullet_list"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;BulletList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="k"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;getContent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// &amp;lt;-- add a case here!&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="s"&gt;"unknown"&lt;/span&gt;
  &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Unknown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's that easy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Note on Runtime Overhead
&lt;/h3&gt;

&lt;p&gt;It's worth noting that our &lt;code&gt;RichText&lt;/code&gt; module approach introduces following overhead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Variants with payloads are represented as arrays, so every classify will create a new array with the variant content inside (also the extra &lt;code&gt;classify&lt;/code&gt; call.&lt;/li&gt;
&lt;li&gt;Our &lt;code&gt;getContent&lt;/code&gt; and &lt;code&gt;getType&lt;/code&gt; function does extra checks on the structure of each input value.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please note that the ReScript Compiler team is currently investigating in a better runtime representation for variants to be able to map more seamlessly to JS and improve performance in the future.&lt;/p&gt;

&lt;h3&gt;
  
  
  Note on Recursion
&lt;/h3&gt;

&lt;p&gt;I am aware that the examples used in this article are not stack-safe. This means that you can practically blow your call stack when there are enough deep recursive calls. There's ways to optimize the examples to be stack-safe, just be aware that I tried to keep it simple.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We started out by defining a very simple version of (Storyblok based) RichText data structures in TypeScript and highlighted some aspects of Discriminated Unions / Tagged Unions. &lt;/p&gt;

&lt;p&gt;Later on we created FFI code wrapping variants around the same RichText data structures. We created a &lt;code&gt;RichText.res&lt;/code&gt; module, defined a data model with a &lt;code&gt;cases&lt;/code&gt; variant and a &lt;code&gt;classify&lt;/code&gt; function to be able to parse incoming data. We used pattern matching to access the data in a very ergonomic way.&lt;/p&gt;

&lt;p&gt;We only scratched the surface here. I hope this article gave you an idea on how to design your own ReScript modules to tackle similar problems!&lt;/p&gt;

&lt;p&gt;In case you are interested in more ReScript related topics, make sure to &lt;a href="https://twitter.com/ryyppy"&gt;follow me on twitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Special thanks to hesxenon and cristianoc for the extensive technical reviews and discussions!&lt;/p&gt;

&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html#discriminating-unions"&gt;TS documentation: Discriminated Union Types&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/ksaldana1/domain-modeling-with-tagged-unions-in-graphql-reasonml-and-typescript-2gnn"&gt;Domain Modeling with Tagged Unions in GraphQL, ReasonML and TypeScript&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rescript</category>
      <category>typescript</category>
      <category>javascript</category>
    </item>
    <item>
      <title>A Quick Look on my ReasonML Workflow with VSCode</title>
      <dc:creator>Patrick Ecker</dc:creator>
      <pubDate>Thu, 05 Mar 2020 08:10:23 +0000</pubDate>
      <link>https://dev.to/ryyppy/a-quick-look-on-my-reasonml-workflow-with-vscode-1pmm</link>
      <guid>https://dev.to/ryyppy/a-quick-look-on-my-reasonml-workflow-with-vscode-1pmm</guid>
      <description>&lt;p&gt;&lt;strong&gt;Update, 10.9.2018:&lt;/strong&gt; This article has migrated to the newly recommended &lt;a href="https://marketplace.visualstudio.com/items?itemName=jaredly.reason-vscode"&gt;VSCode plugin&lt;/a&gt; by Jared Forsyth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The version of my toolchain on this date:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Reason 3.3.3 @ e10fa53&lt;/li&gt;
&lt;li&gt;  BuckleScript 4.0.5&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This article is aimed for beginners without any knowledge about Reason.&lt;/p&gt;

&lt;p&gt;In April 2018, Nik Graf and I were giving workshops at &lt;a href="https://www.react-finland.fi"&gt;React-Finland&lt;/a&gt; and got some really good impressions on how newcomers learn to code in Reason for their very first time.&lt;/p&gt;

&lt;p&gt;The tools in the Reason ecosystem are quite useful to learn the language itself, so I try to show you how I work on Reason code inside VSCode and the terminal, to make it easier for you to get started.&lt;/p&gt;

&lt;p&gt;Luckily since August 2018, the installation steps got way easier with the new VSCode plugin done by Jared Forsyth.&lt;/p&gt;

&lt;p&gt;What’s the major difference to the existing &lt;a href="https://github.com/freebroccolo/ocaml-language-server/"&gt;ocaml / reason plugin&lt;/a&gt;? You can read the motivation behind this &lt;a href="https://github.com/jaredly/reason-language-server#what-about-ocaml-language-server"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is also the only plugin which works on Windows without any hacks, therefore makes it the official recommended way of developing Reason.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1— Installing the VSCode Reason Plugin
&lt;/h2&gt;

&lt;p&gt;Go to the extension tab and search the marketplace for a plugin called &lt;code&gt;reason-vscode&lt;/code&gt;by Jared Forsyth, press the install button and reload your VSCode instance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--g4x-wr_X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/ryyppy/oneblog/blob/master/img/704/1%2AiIQP3illUXFAtkOcFJ71yg.png%3Fraw%3Dtrue" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--g4x-wr_X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/ryyppy/oneblog/blob/master/img/704/1%2AiIQP3illUXFAtkOcFJ71yg.png%3Fraw%3Dtrue" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now your editor should be able to interact with Reason code. After an initial build with BuckleScript, you should be able to see type information on cursor hover, inferred types and also basic type errors. Also you will notice that &lt;code&gt;*.re&lt;/code&gt; will have correct syntax highlighting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2— Let’s try out the setup
&lt;/h2&gt;

&lt;p&gt;For the next steps, let’s check out a Reason / BuckleScript based project. For convenience, we will use the same repository I use for my workshops:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ryyppy/reason-workshop"&gt;https://github.com/ryyppy/reason-workshop&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clone the repository and run &lt;code&gt;npm install&lt;/code&gt; to install all necessary dependencies. Since &lt;code&gt;bs-platform&lt;/code&gt; is defined as a devDependency, all the tools you need to compile Reason to JavaScript will also be installed inside your &lt;code&gt;node_modules&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;We already defined some convenience scripts inside &lt;code&gt;package.json&lt;/code&gt; to easily interact with the BuckleScript compiler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"scripts": {
  "bs:clean": "bsb -clean-world",
  "bs:build": "bsb -make-world",
  "bs:watch": "bsb -make-world -w"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So by running &lt;code&gt;npm run bs:watch&lt;/code&gt; you will start the BuckleScript compiler in watch mode, actively recompiling Reason files (&lt;code&gt;*.re&lt;/code&gt;) on each change. Since BuckleScript also offers better error messages than Merlin, let’s use the watch command inside the VSCode terminal itself:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1Ic3Nc3G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/ryyppy/oneblog/blob/master/img/704/1%2AqbkboeSbZ-tfyrlwKEmtFA.png%3Fraw%3Dtrue" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1Ic3Nc3G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/ryyppy/oneblog/blob/master/img/704/1%2AqbkboeSbZ-tfyrlwKEmtFA.png%3Fraw%3Dtrue" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the screenshot above, you’ll see BuckleScript up and running in the bottom terminal pane. Whenever there is a type error in our code (after saving a file), the compiler will show nice error messages for troubleshooting:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vbk0UWCT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/ryyppy/oneblog/blob/master/img/704/1%2AQ2YV96-XNDV2iaHzOq3kFA.png%3Fraw%3Dtrue" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vbk0UWCT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/ryyppy/oneblog/blob/master/img/704/1%2AQ2YV96-XNDV2iaHzOq3kFA.png%3Fraw%3Dtrue" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For me, I find it much easier to have BuckleScript running inside my VSCode, since I will never be required to switch from my IDE to my terminal. As soon as I adapted this workflow, I realized that my feedback loop was much quicker and I got a much more immersive coding experience.&lt;/p&gt;

&lt;p&gt;Of course, there are many other tools which need to be run in the background, like &lt;code&gt;webpack&lt;/code&gt; or a development server. I usually keep them in my separate terminal, because in most cases, errors only happen whenever BuckleScript doesn’t fully compile.&lt;/p&gt;

&lt;h2&gt;
  
  
  Last Quick Tip: Reformatting
&lt;/h2&gt;

&lt;p&gt;Whenever you are editing a &lt;code&gt;*.re&lt;/code&gt; file, you can run &lt;code&gt;Shift-Option-F&lt;/code&gt; to reformat the editor buffer.&lt;/p&gt;

&lt;p&gt;To try this, copy this code snippet in one of the reason files inside the&lt;code&gt;src&lt;/code&gt; directory and hit the &lt;code&gt;Shift-Option-F&lt;/code&gt; hotkey:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let addAndStringify = (
  a,
  b) =&amp;gt; {
let c = a + b;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;string_of_int(c);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code should now be correctly formatted.&lt;/p&gt;




&lt;p&gt;That’s it! After following these 2steps, you are now able to work on Reason code in VSCode.&lt;/p&gt;

&lt;p&gt;If you have any problems with your setup, make sure to ask questions on our &lt;a href="https://discord.gg/reasonml"&gt;Reason Discord&lt;/a&gt; #editorsupport channel.&lt;/p&gt;

&lt;p&gt;For more blog articles &amp;amp; news about Reason and other web related programming languages, &lt;a href="https://twitter.com/ryyppy"&gt;follow me on Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
