<?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: Victor Trofin</title>
    <description>The latest articles on DEV Community by Victor Trofin (@vtrofin).</description>
    <link>https://dev.to/vtrofin</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%2F777147%2F510978bd-1cfd-49f1-a8e7-b3d04a1eb967.jpeg</url>
      <title>DEV Community: Victor Trofin</title>
      <link>https://dev.to/vtrofin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vtrofin"/>
    <language>en</language>
    <item>
      <title>Return union types in ReScript</title>
      <dc:creator>Victor Trofin</dc:creator>
      <pubDate>Wed, 22 Dec 2021 14:25:58 +0000</pubDate>
      <link>https://dev.to/vtrofin/return-union-types-in-rescript-11d2</link>
      <guid>https://dev.to/vtrofin/return-union-types-in-rescript-11d2</guid>
      <description>&lt;p&gt;Inspired by &lt;a href="https://rescript-lang.org/blog/union-types-in-bucklescript"&gt;this article&lt;/a&gt; I've decided to share an approach on returning union types in ReScript. The case in point is exporting a &lt;a href="https://nextjs.org/docs/basic-features/data-fetching#getserversideprops-server-side-rendering"&gt;getServerSideProps&lt;/a&gt; function from a Next.js page, enabling us to fetch the props for pre-rendering the page server side. This example function returns only two kinds of objects, one for the props and another for the redirect (we will not handle the not found case). Note that in the ReScript implementation we will also be using an object for the return value mainly for convenience. Since it doesn't require a type declaration, the object type makes it easy to return the runtime representation of our desired object in JavaScript. &lt;/p&gt;

&lt;p&gt;Let's see the TypeScript implementation first to get a better idea of what we're trying to achieve. The type declaration of the getServerSideProps function reveals no surprise, its return type is a union type which can either return props, a redirect or notFound.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; // .ts
 export type GetServerSidePropsResult&amp;lt;P&amp;gt; =
   | { props: P }
   | { redirect: Redirect }
   | { notFound: true }

 export type GetServerSideProps&amp;lt;
   P extends { [key: string]: any } = { [key: string]: any },
   Q extends ParsedUrlQuery = ParsedUrlQuery
 &amp;gt; = (
   context: GetServerSidePropsContext&amp;lt;Q&amp;gt;
 ) =&amp;gt; Promise&amp;lt;GetServerSidePropsResult&amp;lt;P&amp;gt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is our complete TypeScript example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; // Page.ts
 import { GetServerSideProps, NextPage } from "next"

 type User = {
   name: string
   age: number
 }

 type Props = { user: User }

 export const getServerSideProps: GetServerSideProps&amp;lt;Props&amp;gt; = async (
   context,
 ) =&amp;gt; {
   const response = await fetch(`https://.../user`)
   const user: User = await response.json()

   if (!user) {
     return {
       redirect: {
         destination: "/",
         permanent: false,
       },
     }
   }

   return {
     props: { user },
   }
 }

 const Page: NextPage&amp;lt;Props&amp;gt; = ({ user: { name, age } }) =&amp;gt; {
   return (
     &amp;lt;div&amp;gt;
       &amp;lt;p&amp;gt;User name: {name}&amp;lt;/p&amp;gt;
       &amp;lt;p&amp;gt;Age: {age}&amp;lt;/p&amp;gt;
     &amp;lt;/div&amp;gt;
   )
 }

 export default Page
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Working with the union type looks effortless in TypeScript but is it the same for the ReScript counterpart? We'll start by namespacing the function's type definition into a separate module to keep things organized. Inside the module we also declare the type for the context argument this function takes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; module GetServerSideProps = {
   module Req = {
     type t
   }

   module Res = {
     type t
   }

   type context&amp;lt;'props, 'params, 'previewData&amp;gt; = {
     params: 'params,
     preview: option&amp;lt;bool&amp;gt;,
     previewData: Js.Nullable.t&amp;lt;'previewData&amp;gt;,
     query: Js.Dict.t&amp;lt;string&amp;gt;,
     req: Req.t,
     res: Res.t,
   }

   // The type of the `getServerSideProps` function
   type t&amp;lt;'props, 'params, 'previewData&amp;gt; = context&amp;lt;'props, 'params, 'previewData&amp;gt; =&amp;gt; Js.Promise.t&amp;lt;{
     "props": 'props,
   }&amp;gt;
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For convenience we choose to abstract &lt;code&gt;req&lt;/code&gt; and &lt;code&gt;res&lt;/code&gt;  properties on the context record. We declare them as opaque types because we’re not sure about their runtime representation and we actually don’t care about it. Currently our function returns a single object type with props. &lt;/p&gt;

&lt;p&gt;We'll group the union type definition into another nested module named Return and we'll provide a module signature to expose two methods which are responsible for returning the appropriate object kind with either props or redirection.  Everything else is left as an implementation detail. Here is how the Return module looks like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; module Return: {
   type rec t&amp;lt;'props&amp;gt;

   let inject: (~props: 'props) =&amp;gt; t&amp;lt;'props&amp;gt;

   let redirect: (~destination: string, ~permanent: bool, unit) =&amp;gt; t&amp;lt;'props&amp;gt;
 } = {
   @unboxed type rec t&amp;lt;'props&amp;gt; = Any('value): t&amp;lt;'props&amp;gt;

   let inject = (~props) =&amp;gt; Any({ "props": props })

   let redirect = (~destination, ~permanent, ()) =&amp;gt; Any({
     "redirect": { 
       "destination": destination, 
       "permanent": permanent
     },
   })
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Checking the module signature we notice again an opaque type &lt;code&gt;t&amp;lt;'props&amp;gt;&lt;/code&gt; to hide the underlying type of the Return module as an implementation detail. This type references itself so we need to define it as a recursive type &lt;code&gt;rec&lt;/code&gt; otherwise we would get a compiler error. &lt;/p&gt;

&lt;p&gt;Inside the Return module, &lt;code&gt;t&lt;/code&gt; is defined as a variant &lt;code&gt;Any&lt;/code&gt; with some payload. We are also making use of the &lt;code&gt;@unboxed&lt;/code&gt; attribute which strips out the variant constructor and makes its runtime representation equal to the underlying value. These two combined enable returning at runtime the two objects expected from geServerSideProps.&lt;br&gt;
Now we can update the function's type definition to return a value of type &lt;code&gt;Return.t&amp;lt;'props&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type t&amp;lt;'props, 'params, 'previewData&amp;gt; = context&amp;lt;'props, 'params, 'previewData&amp;gt; =&amp;gt; Js.Promise.t&amp;lt;
   Return.t&amp;lt;'props&amp;gt;,
 &amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are now ready to implement the getServerSideProps function inside the Page file. We destructure inject and redirect functions from the Return module and call them to return the desired object - inject to "inject" the props into the page and redirect to redirect to the main page when failing to load the props.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; // Page.res
 module GetServerSideProps = {
   module Req = {
     type t
   }

   module Res = {
     type t
   }

   type context&amp;lt;'props, 'params, 'previewData&amp;gt; = {
     params: 'params,
     preview: option&amp;lt;bool&amp;gt;,
     previewData: Js.Nullable.t&amp;lt;'previewData&amp;gt;,
     query: Js.Dict.t&amp;lt;string&amp;gt;,
     req: Req.t,
     res: Res.t,
   }

   module Return: {
     type rec t&amp;lt;'props&amp;gt;

     let inject: (~props: 'props) =&amp;gt; t&amp;lt;'props&amp;gt;

     let redirect: (~destination: string, ~permanent: bool, unit) =&amp;gt; t&amp;lt;'props&amp;gt;
   } = {
     @unboxed type rec t&amp;lt;'props&amp;gt; = Any('value): t&amp;lt;'props&amp;gt;

     let inject = (~props) =&amp;gt; Any({"props": props})

     let redirect = (~destination, ~permanent, ()) =&amp;gt; Any({
       "redirect": {"destination": destination, "permanent": permanent},
     })
   }

   type t&amp;lt;'props, 'params, 'previewData&amp;gt; = context&amp;lt;'props, 'params, 'previewData&amp;gt; =&amp;gt; Js.Promise.t&amp;lt;
     Return.t&amp;lt;'props&amp;gt;,
   &amp;gt;
 }

 type user = {
   name: string,
   age: int,
 }

 type props = {user: user}

 let getServerSideProps: GetServerSideProps.t&amp;lt;_, _, _&amp;gt; = _context =&amp;gt; {
   let {inject, redirect} = module(GetServerSideProps.Return)

   let getData = () =&amp;gt; {
     // fetch the data from somewhere
     Js.Promise.resolve({name: "John", age: 30})
   }

   getData()-&amp;gt;Js.Promise.then_(user =&amp;gt; {
     inject(~props={user: user})-&amp;gt;Js.Promise.resolve
   }, _)-&amp;gt;Js.Promise.catch(_error =&amp;gt; {
     redirect(~destination="/", ~permanent=true, ())-&amp;gt;Js.Promise.resolve
   }, _)
 }

 @react.component
 let default = (~user: user) =&amp;gt; {
   let {name, age} = user

   &amp;lt;div&amp;gt;
     &amp;lt;p&amp;gt; {`User name: ${name}`-&amp;gt;React.string} &amp;lt;/p&amp;gt;
     &amp;lt;p&amp;gt; {`Age: ${age-&amp;gt;Js.Int.toString}`-&amp;gt;React.string} &amp;lt;/p&amp;gt;
   &amp;lt;/div&amp;gt;
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hopefully this example helps you get a grasp on union types in ReScript. It may require a bit more effort to master the topic as a newcomer to the language but I believe this is by no means impossible to achieve. It's definitely worth it.&lt;/p&gt;

</description>
      <category>rescript</category>
      <category>javascript</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Covering the edge case on the ReScript record type</title>
      <dc:creator>Victor Trofin</dc:creator>
      <pubDate>Mon, 20 Dec 2021 02:01:42 +0000</pubDate>
      <link>https://dev.to/vtrofin/covering-the-edge-case-on-the-rescript-record-type-4j83</link>
      <guid>https://dev.to/vtrofin/covering-the-edge-case-on-the-rescript-record-type-4j83</guid>
      <description>&lt;p&gt;In terms of preference, Typescript dominates the category for statically typed languages that compile to plain JavaScript. However,  ReScript - a fairly new contender in this category - aims to improve the JavaScript experience and address some of the pitfalls. In this article I'm planning on looking at the edge case in one of the most heavily used data types for each of the two languages. How different is working with records in ReScript than working with objects in TypeScript?&lt;/p&gt;

&lt;p&gt;As opposed to TypeScript objects, records in ReScript are immutable by default. Another big difference is that &lt;strong&gt;records use nominal typing&lt;/strong&gt; while objects use structural typing. What this means is that two records sharing the same properties (field names) will not have the same type. &lt;/p&gt;

&lt;p&gt;For example, in TypeScript this code compiles. I'm able to provide an argument of type SubscriptionUser to a function that accepts an argument of type QueryUser simply because they have the same properties.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// .ts
type QueryUser = {
  age: number
  name: string
}

type SubscriptionUser = {
  age: number
  name: string
}

const logMyUser = ({ age, name }: QueryUser) =&amp;gt; {
  console.log(`Hi! I am ${name} of ${age} years old.`)
}

const subscriptionUser: SubscriptionUser = {
  name: "John",
  age: 30,
}
logMyUser(subscriptionUser)

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

&lt;/div&gt;



&lt;p&gt;This however will not work for records.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// .res
type queryUser = {
  name: string,
  age: int,
}

type subscriptionUser = {
  name: string,
  age: int,
}

let logMyUser = ({name, age}: queryUser) =&amp;gt; {
  Js.log(`Hi! I am ${name} of ${age-&amp;gt;Js.Int.toString} years old.`)
}

let subscriptionUser: subscriptionUser = {name: "John", age: 30}
logMyUser(subscriptionUser)

// ERROR:
// [E] Line 17, column 10:
// This has type: subscriptionUser
// Somewhere wanted: queryUser

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

&lt;/div&gt;



&lt;p&gt;This means that different record types with the same properties cannot be passed to the same function. The main benefit for this is that type error messages are really good and point you to the specific line of code where you need to address the issue. Anyone who has used the structurally typed &lt;a href="https://rescript-lang.org/docs/manual/latest/polymorphic-variant"&gt;polymorphic variants&lt;/a&gt; in ReScript may know that sometimes it is difficult to pinpoint the exact location in your codebase where you need to address the type error. You may have to do a little bit of digging to figure out where that &lt;code&gt;somewhere&lt;/code&gt; might be.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;This has type `x`
Somewhere wanted type `y`
Types for method `z` are incompatible
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, why does this matter? Well, because updating deeply nested records is a little more tedious. For example, consider the case of having a front-end react app with a graphQL query to fetch users, with reactivity provided via graphQL subscriptions. Every time an user gets updated we need to map over all of our users stored in state and replace the old values with the updated ones. &lt;/p&gt;

&lt;p&gt;In TypeScript you would just assign the updated nested object and be done with it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Page.ts
import React, { useState, useEffect } from "react"

type QueryUser = {
  name: string
  age: number
}

type SubscriptionUser = {
  name: string
  age: number
}

type QueryResult = {
  id: string
  userData: QueryUser
}

type SubscriptionResult = {
  id: string
  userData: SubscriptionUser
}

// assume we have an array of users fetched from a GraphQL api
const someMagicWayToGetData = (): QueryResult[] =&amp;gt; {
  const users = [
    { id: "1", userData: { name: "John", age: 35 } },
    { id: "2", userData: { name: "Mary", age: 20 } },
    { id: "3", userData: { name: "Kate", age: 50 } },
  ]

  return users
}

// and a graphQL subscription to push updates to our page
const someMagicWayToGetUpdates = (): SubscriptionResult =&amp;gt; {
  const updatedUser = {
    id: "2",
    userData: { name: "Mary Jane", age: 21 },
  }

  return updatedUser
}

const Page = () =&amp;gt; {
  const [
    users, 
    setUsers
  ] = useState&amp;lt;QueryResult[]&amp;gt;(someMagicWayToGetData())
  const updatedUser = someMagicWayToGetUpdates()

  useEffect(() =&amp;gt; {
    const newUsers = users.map((user) =&amp;gt; {
      if (user.id === updatedUser.id) {
        return {
          ...user,
          userData: updatedUser.userData,
        }
      }

      return user
    })

    setUsers(newUsers)
  }, [updatedUser])

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;ul&amp;gt;
        {users.map(({ id, userData: { name, age } }) =&amp;gt; (
          &amp;lt;li key={id}&amp;gt;
            User: {name}; Age: {age}
          &amp;lt;/li&amp;gt;
        ))}
      &amp;lt;/ul&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}

export default Page

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

&lt;/div&gt;



&lt;p&gt;In ReScript you need to manually assign each updated property within the nested record. This will not compile.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// .res
let newUsers = users-&amp;gt;Js.Array2.map(user =&amp;gt; {
  if user.id == updatedUser.id {
    {...user, userData: updatedUser.userData}
  } else {
    user
  }
})

// Error:
// [E] Line 43, column 20:
// This has type: subscriptionUser
// Somewhere wanted: queryUser

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

&lt;/div&gt;



&lt;p&gt;This compiles&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Page.res
type queryUser = {
  name: string,
  age: int,
}

type subscriptionUser = {
  name: string,
  age: int,
}

type queryResult = {id: string, userData: queryUser}
type subscriptionResult = {id: string, userData: subscriptionUser}

let someMagicWayToGetData: unit =&amp;gt; array&amp;lt;queryResult&amp;gt; = () =&amp;gt; {
  let users: array&amp;lt;queryResult&amp;gt; = [
    {id: "1", userData: {name: "John", age: 35}},
    {id: "2", userData: {name: "Mary", age: 20}},
    {id: "3", userData: {name: "Kate", age: 50}},
  ]

  users
}

let someMagicWayToGetUpdates: unit =&amp;gt; subscriptionResult = () =&amp;gt; {
  let updatedUser = {
    id: "2",
    userData: {name: "Mary Jane", age: 21},
  }

  updatedUser
}

@react.component
let default = () =&amp;gt; {
  let (users, setUsers) = React.useState(_ =&amp;gt; someMagicWayToGetData())
  let updatedUser = someMagicWayToGetUpdates()

  React.useEffect1(() =&amp;gt; {
    let newUsers = users-&amp;gt;Js.Array2.map(user =&amp;gt; {
      if user.id == updatedUser.id {
        {
          ...user,
          userData: {
            name: updatedUser.userData.name,
            age: updatedUser.userData.age,
          },
        }
      } else {
        user
      }
    })
    setUsers(_ =&amp;gt; newUsers)

    None
  }, [updatedUser])

  &amp;lt;div&amp;gt;
    &amp;lt;ul&amp;gt;
      {users
      -&amp;gt;Js.Array2.map(({id, userData: {name, age}}) =&amp;gt;
        &amp;lt;li key=id&amp;gt; {`User: ${name}; Age: ${age-&amp;gt;Js.Int.toString}`-&amp;gt;React.string} &amp;lt;/li&amp;gt;
      )
      -&amp;gt;React.array}
    &amp;lt;/ul&amp;gt;
  &amp;lt;/div&amp;gt;
}

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

&lt;/div&gt;



&lt;p&gt;It might not look like much of a hassle in this example but in the real world, a graphQL query result with lots of nested records may become a little bit annoying to deal with. &lt;/p&gt;

&lt;p&gt;However, the ReScript docs do provide a good alternative for this situation, and a better way to handle this case is to represent the userData as a combination of a variant and record instead. This could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// .res
type user = {
  name: string,
  age: int,
}

type userData = {
  id: string,
  userData: user,
}

type result =
  | QueryResult(userData)
  | SubscriptionResult(userData)

let users: array&amp;lt;result&amp;gt; = [
  QueryResult({
    id: "1",
    userData: {name: "John", age: 35},
  }),
  QueryResult({
    id: "2",
    userData: {name: "Mary", age: 20},
  }),
  QueryResult({
    id: "3",
    userData: {name: "Kate", age: 50},
  }),
]

let updatedUser: result = SubscriptionResult({
  id: "2",
  userData: {name: "Mary Jane", age: 21},
})

let newUsers = users-&amp;gt;Js.Array2.reduce((allUsers, user) =&amp;gt; {
  switch (user, updatedUser) {
  | (
      QueryResult({id}), 
      SubscriptionResult({id: subscriptionId, userData: subscriptionUserData})
    ) if id == subscriptionId =&amp;gt;
    allUsers-&amp;gt;Js.Array2.concat([
      QueryResult({
        id: id,
        userData: subscriptionUserData,
      }),
    ])
  | (QueryResult(user), _) =&amp;gt; allUsers-&amp;gt;Js.Array2.concat([QueryResult(user)])
  | (_, _) =&amp;gt; allUsers
  }
}, [])

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

&lt;/div&gt;



&lt;p&gt;I chose the reduce method in the example above to have less cases to handle in the switch pattern but definitely an array map would also work. Only thing is that we'll have to handle a few more cases and it looks a little too verbose to me, even though that is the recommended way to go.&lt;/p&gt;

&lt;p&gt;Wonderful, isn't it? This ended up looking a lot nicer than I was expecting. I can definitely see the benefits of using the nominally typed records instead of objects even for deeply nested data structures. By the way, if you actually prefer it, the structurally typed object exists in ReScript as well but it is more suitable to be used for binding to JavaScript objects. &lt;/p&gt;

</description>
      <category>rescript</category>
      <category>typescript</category>
      <category>record</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
