<?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: Muhamad Ilyas Mustafa</title>
    <description>The latest articles on DEV Community by Muhamad Ilyas Mustafa (@mustafamilyas).</description>
    <link>https://dev.to/mustafamilyas</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%2F677361%2F3292e966-26f0-4b63-af61-bb270c4bfa21.png</url>
      <title>DEV Community: Muhamad Ilyas Mustafa</title>
      <link>https://dev.to/mustafamilyas</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mustafamilyas"/>
    <language>en</language>
    <item>
      <title>Understanding the "Infer" Keyword in String Literal Types</title>
      <dc:creator>Muhamad Ilyas Mustafa</dc:creator>
      <pubDate>Sun, 07 Jan 2024 03:26:28 +0000</pubDate>
      <link>https://dev.to/mustafamilyas/understanding-the-infer-keyword-in-string-literal-types-gbh</link>
      <guid>https://dev.to/mustafamilyas/understanding-the-infer-keyword-in-string-literal-types-gbh</guid>
      <description>&lt;p&gt;When working with string literal types in TypeScript, the use of the &lt;code&gt;infer&lt;/code&gt; keyword can lead to some intriguing behaviors. Let's delve into an example to explore its nuances:&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="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Test&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&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="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;B&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;B&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&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;Test1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Test&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// never&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Test2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Test&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ab&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// a-b-&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Test3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Test&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;abcd&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// a-b-cd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Observations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The first result is &lt;code&gt;never&lt;/code&gt; because the string literal type is only one character long, and our type requires at least three characters.&lt;/li&gt;
&lt;li&gt;The second result, &lt;code&gt;a-b-&lt;/code&gt;, may seem unexpected. The &lt;code&gt;infer&lt;/code&gt; keyword is greedy, attempting to match as many characters as possible. In this case, it matches &lt;code&gt;a&lt;/code&gt; to &lt;code&gt;A&lt;/code&gt;, &lt;code&gt;b&lt;/code&gt; to &lt;code&gt;B&lt;/code&gt;, and the remaining empty string to &lt;code&gt;C&lt;/code&gt;. As we can only have &lt;strong&gt;one empty string&lt;/strong&gt; in the template literal type, the result is &lt;code&gt;a-b-&lt;/code&gt;. This behavior becomes clearer with a four-character example:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Test4Char&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&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="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;B&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;D&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;B&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;D&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&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;Test1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Test4Char&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// never (B, C, and D are empty strings, and only one is allowed)&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Test2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Test4Char&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ab&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// never (C &amp;amp; D are empty strings, and only one is allowed)&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Test3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Test4Char&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;abc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// a-b-c- (D is empty string)&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Test4&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Test4Char&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;abcd&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// a-b-c-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The third result, &lt;code&gt;a-b-cd&lt;/code&gt;, demonstrates that &lt;code&gt;C&lt;/code&gt; matches &lt;code&gt;cd&lt;/code&gt;, &lt;strong&gt;storing the remaining string&lt;/strong&gt; in &lt;code&gt;C&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this understanding, let's leverage this knowledge to create some intriguing types:&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;// Get the last character of a string&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;LastChar&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;Last&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;Last&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;LastChar&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Last&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&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;TestL1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;LastChar&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;abcd&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 'd'&lt;/span&gt;

&lt;span class="c1"&gt;// Reverse a string&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Reverse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;B&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;Reverse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;B&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&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;TestR1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Reverse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reverse&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// esrever&lt;/span&gt;

&lt;span class="c1"&gt;// Convert camel case to kebab case&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;CamelToKebab&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&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="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;B&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nb"&gt;Capitalize&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Lowercase&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;CamelToKebab&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;B&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&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;TestC1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;CamelToKebab&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fooBarBaz&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// foo-bar-baz&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;TestC2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;CamelToKebab&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fooBar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// foo-bar&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;TestC3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;CamelToKebab&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// foo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's all for now. Happy typing!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>typescript</category>
      <category>learning</category>
    </item>
    <item>
      <title>Brief Explanation of Javascript Module Bundler</title>
      <dc:creator>Muhamad Ilyas Mustafa</dc:creator>
      <pubDate>Tue, 25 Jul 2023 03:25:14 +0000</pubDate>
      <link>https://dev.to/mustafamilyas/brief-explanation-of-javascript-module-bundler-b1k</link>
      <guid>https://dev.to/mustafamilyas/brief-explanation-of-javascript-module-bundler-b1k</guid>
      <description>&lt;p&gt;The Javascript ecosystem is a wild-wild world. Every time humans breathe, a javascript library is released to the world. Of course, this is an exaggeration, but you can almost literally type random English words and add “JS” behind them, most likely You will find a javascript library with that term. That shows how popular Javascript is.&lt;/p&gt;

&lt;p&gt;On the one hand, this means Javascript has a vast ecosystem. Every problem that you face, you can easily find the answer, or at least you can ask easily. But on the other hand, it is so huge, that no mortal can understand all of them. This is becoming a huge headache especially when you are comparing similar libraries.&lt;/p&gt;

&lt;p&gt;This also applies to Javascript Module Bundler, as so many bundlers out there, with their own configuration, with their own term. Not many of us touch bundler configuration regularly (including me), and it can be quite intimidating to set up your own bundle configuration from scratch. Here is my brief understanding of the Javascript Module Bundler.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Module Bundler
&lt;/h2&gt;

&lt;p&gt;The art of &lt;a href="https://en.wikipedia.org/wiki/Modular_programming" rel="noopener noreferrer"&gt;modular programming&lt;/a&gt; is splitting your application code into small independent interchangeable units called modules. It is extremely useful, by following this paradigm, as the codebase grows, we can just create separate functions, and/or files rather than adding more lines of code into an already crowded file.&lt;/p&gt;

&lt;p&gt;Take a look at the code below&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.0/jquery.min.js"&lt;/span&gt; &lt;span class="na"&gt;integrity=&lt;/span&gt;&lt;span class="s"&gt;"sha512-3gJwYpMe3QewGELv8k/BX9vcqhryRdzRMxVfq6ngyWXwo03GFEzjsUm8Q7RZcHPHksttq7/GFoxjCVUjkjvPdw=="&lt;/span&gt; &lt;span class="na"&gt;crossorigin=&lt;/span&gt;&lt;span class="s"&gt;"anonymous"&lt;/span&gt; &lt;span class="na"&gt;referrerpolicy=&lt;/span&gt;&lt;span class="s"&gt;"no-referrer"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"./components/a.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"./components/b.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"./main.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;In the past, it is really painful to manage projects. As you can see above, when we want to add a new file, we also need to update the HTML script. This also applies when we want to update the dependency, remove files, etc. We also have to be mindful about many things (i.e. the order of the script, naming collision), and as the code grows bigger, it becomes tedious to manage all of this. This is one of the cases in which a module bundler might be helpful.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Module bundler&lt;/strong&gt; is a development tool that processes your code into one bundle of code. It traverses your code from the provided &lt;strong&gt;entry point&lt;/strong&gt;(s), builds a &lt;strong&gt;dependency graph,&lt;/strong&gt; and produces the &lt;strong&gt;output&lt;/strong&gt; files based on the configuration.  So essentially just transform your application code into another form.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg9pw5nqdu2ajqi3gfpf9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg9pw5nqdu2ajqi3gfpf9.png" alt="module bundler flow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are at least two processes involved which are dependency resolution and packing. &lt;strong&gt;Dependency resolution&lt;/strong&gt; is a process to create a relationship graph between each module/file called a &lt;strong&gt;dependency graph&lt;/strong&gt;. The dependency graph is useful to identify the module retrieval order and to eliminate unreachable modules/files. &lt;strong&gt;Packing&lt;/strong&gt; is a process of bundling everything based on the created dependency graph and the specified configuration into something that is executable by the browser. In the above case, out of all files in the above codebase, the module bundler produces only one file. And then later we can just insert &lt;code&gt;main.js&lt;/code&gt; into the HTML.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"./main.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;One of the questions that might come to mind after seeing the above codes is “How do we debug this code in the browser if all the codes merged into one?”.  The answer is a &lt;strong&gt;source map&lt;/strong&gt;. A source map is a type of file that can help tools (i.e. browser) to reconstruct a minified code. Along with the executable code, a module bundler can also produce a source map. With a source map, we can debug the code in the browser like the code's original structure. Of course, we can disable it, if we don’t want our user to be able to read our code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Things to pay attention
&lt;/h2&gt;

&lt;p&gt;As we discussed earlier, Javascript has a lot of module bundlers. Each module bundler has its own unique configuration and terms. But they have a pattern and here are several configurations that you need to pay attention to.&lt;/p&gt;

&lt;h3&gt;
  
  
  Entry Point(s)
&lt;/h3&gt;

&lt;p&gt;The entry point(s) indicates where the bundler should start traversing your code to create a dependency graph. You can specify one or multiple entry points. This is extremely useful when you are building a library, for example, a component library, as it doesn’t have a specific entry point.&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="c1"&gt;// webpack.config.js&lt;/span&gt;&lt;br&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
  &lt;span class="na"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./path/to/my/entry/file.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
&lt;span class="p"&gt;};&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="c1"&gt;// rollup.config.js&lt;/span&gt;&lt;br&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
  &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src/main-a.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;b/index&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="s1"&gt;src/main-b.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;br&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;br&gt;
&lt;span class="p"&gt;};&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h3&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Output&lt;br&gt;
&lt;/h3&gt;

&lt;p&gt;Output specifies where and how the bundled code should be placed after being processed. Naming, file splitting, and format are some examples that can be determined here.&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="c1"&gt;// webpack.config.js&lt;/span&gt;&lt;br&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;br&gt;
  &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dist&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-first-webpack.bundle.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;&lt;br&gt;
&lt;span class="p"&gt;};&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="c1"&gt;// rollup.config.js&lt;/span&gt;&lt;br&gt;
&lt;span class="p"&gt;...&lt;/span&gt;&lt;br&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
    &lt;span class="p"&gt;...,&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;br&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
            &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bundle.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
            &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;es&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;br&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;&lt;br&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
            &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bundle.min.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
            &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;es&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
            &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;terser&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;&lt;br&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;&lt;br&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;&lt;br&gt;
&lt;span class="p"&gt;};&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h3&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Plugin System&lt;br&gt;
&lt;/h3&gt;

&lt;p&gt;The great thing about module bundlers is a plugin system. A module bundler's main function is just well bundling. If we want to have some advanced functionality like a dev server, transform SCSS into CSS, polyfill new javascript functionality, &lt;em&gt;uglifying&lt;/em&gt; code, etc., we need to add that functionality by inserting the appropriate plugin into the module bundler’s plugin system.&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="c1"&gt;// webpack.config.js&lt;/span&gt;&lt;br&gt;
&lt;span class="p"&gt;...&lt;/span&gt;&lt;br&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
  &lt;span class="p"&gt;...,&lt;/span&gt;&lt;br&gt;
  &lt;span class="na"&gt;module&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;br&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
        &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;.(&lt;/span&gt;&lt;span class="sr"&gt;js|jsx&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
        &lt;span class="na"&gt;use&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;babel-loader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;&lt;br&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;&lt;br&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;&lt;br&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;br&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;webpack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ProgressPlugin&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;br&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HtmlWebpackPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/index.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;&lt;br&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;&lt;br&gt;
&lt;span class="p"&gt;};&lt;/span&gt;&lt;br&gt;
&lt;span class="c1"&gt;// rollup.config.js&lt;/span&gt;&lt;br&gt;
&lt;span class="p"&gt;...&lt;/span&gt;&lt;br&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
  &lt;span class="p"&gt;...,&lt;/span&gt;&lt;br&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;graphql&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;&lt;br&gt;
&lt;span class="p"&gt;};&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h3&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Quirk&lt;br&gt;
&lt;/h3&gt;

&lt;p&gt;Anything else is bundle-specific and advances features. Some bundlers are very opinionated (i.e. parcel, vite), and the others are like a blank canvas, you need to configure it yourselves (i.e. webpack). The best way to learn this is to read the official documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;Nowadays, module bundler is very essential for application development. Understanding basic behavior is encouraged, but no need to understand everything. Of course, it is good to understand deeply, but for starters, just start coding and ask along the way.&lt;/p&gt;

&lt;p&gt;If you have any comments or feedback, feel free to leave a comment below. Will be happy to discuss this with you all. Cheers!&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Modular_programming" rel="noopener noreferrer"&gt;Modular Programming - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://snipcart.com/blog/javascript-module-bundler" rel="noopener noreferrer"&gt;The Complete JavaScript Module Bundlers Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>programming</category>
      <category>learning</category>
    </item>
    <item>
      <title>Creating Undo-Redo System Using Command Pattern in React</title>
      <dc:creator>Muhamad Ilyas Mustafa</dc:creator>
      <pubDate>Tue, 18 Jul 2023 04:45:27 +0000</pubDate>
      <link>https://dev.to/mustafamilyas/creating-undo-redo-system-using-command-pattern-in-react-mmg</link>
      <guid>https://dev.to/mustafamilyas/creating-undo-redo-system-using-command-pattern-in-react-mmg</guid>
      <description>&lt;p&gt;Supposed you want to learn React by following React's official documentation. And then you come across the &lt;a href="https://react.dev/learn/tutorial-tic-tac-toe"&gt;Tic Tac Toe tutorial&lt;/a&gt;, you find it interesting and then you want to add some functionality to put it into your portfolio. (btw, If you haven't done it, please do it first, it is a good exercise for learning state management)&lt;/p&gt;

&lt;p&gt;One of your ideas may be to add an undo-redo system. The first intuition is just to save all of the states as snapshots and add a pointer so that you can easily go back and forth between snapshots.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/nice-goodall-sdph7g"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;As you can see above, we have a &lt;code&gt;history&lt;/code&gt; variable that contains the history of the application's states and we also have &lt;code&gt;currentMove&lt;/code&gt; to track which application state we are currently in.&lt;/p&gt;

&lt;p&gt;This is an okayish approach in this case because of the small scope. Unfortunately, this approach doesn't scale well. Imagine you are building a Photoshop, it is not feasible to save all of the states' snapshots as it might cause memory overhead. This is one of the cases that the Command Pattern comes in handy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Command Pattern
&lt;/h3&gt;

&lt;p&gt;The command pattern is a design pattern in object-oriented programming that enables an application to perform an action by &lt;strong&gt;encapsulating the necessary information&lt;/strong&gt; to do the action in the form of an object. Encapsulating the necessary information enables an application to trigger an action whenever they want. &lt;/p&gt;

&lt;p&gt;The Command Pattern consists of three main components: the invoker, the receiver, and the command. The invoker is responsible for invoking the command and is often triggered by a user action. The receiver is the controller of the action that will ultimately perform the action specified by the command. The command itself encapsulates the action to be taken, along with any necessary information.&lt;/p&gt;

&lt;p&gt;To learn more about this pattern you can read &lt;a href="https://en.wikipedia.org/wiki/Command_pattern"&gt;here&lt;/a&gt; and &lt;a href="https://refactoring.guru/design-patterns/command"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exploring Command Pattern by Creating Simple Undo-Redo Functionality
&lt;/h3&gt;

&lt;p&gt;In this section, we will focus on building a simple text formatter as you can see below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZIENVT8c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4tgdb424h1hbucmbr3pb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZIENVT8c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4tgdb424h1hbucmbr3pb.gif" alt="preview of the application that we want to build" width="448" height="560"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The styling of the text in the middle can be changed by clicking the action buttons below. The 3 main style modifiers are &lt;code&gt;bold&lt;/code&gt;, &lt;code&gt;italic&lt;/code&gt;, and &lt;code&gt;underline&lt;/code&gt;. The other two buttons are for undo-redo the style changes.&lt;/p&gt;

&lt;p&gt;To implement this using the command pattern, we need to create a base class for the command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export abstract class Command&amp;lt;T&amp;gt; {
  utils: T;
  constructor(utils: T) {
    this.utils = utils;
  }
  abstract execute(): void;
  abstract undo(): void;
  abstract getInfo(): string;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This base command list all of the method that &lt;strong&gt;must&lt;/strong&gt; be implemented by each command. If we wanted the command to be applied, then we need to trigger &lt;code&gt;execute&lt;/code&gt;. We can also revert the action by calling &lt;code&gt;undo&lt;/code&gt;. And to make it more intuitive, we can also show the command information by calling &lt;code&gt;getInfo&lt;/code&gt;. As for &lt;code&gt;utils&lt;/code&gt;, it is used to interact with the &lt;code&gt;outer&lt;/code&gt; world.&lt;/p&gt;

&lt;p&gt;Let's try implementing &lt;code&gt;bold&lt;/code&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export interface CommandUtils {
  styles: CSSProperties;
  setStyles: Dispatch&amp;lt;React.SetStateAction&amp;lt;CSSProperties&amp;gt;&amp;gt;;
}

export class BoldCommand&amp;lt;T extends CommandUtils&amp;gt; extends Command&amp;lt;T&amp;gt; {
  prevFontWeight?: CSSProperties["fontWeight"];
  constructor(utils: T) {
    super(utils);
    this.prevFontWeight = utils.styles.fontWeight;
  }

  getNextStyle() {
    if (
      this.prevFontWeight === "bold" ||
      (typeof this.prevFontWeight === "number" &amp;amp;&amp;amp; this.prevFontWeight &amp;gt;= 700)
    ) {
      return "normal";
    }
    return "bold";
  }

  execute() {
    const nextFontStyle = this.getNextStyle();
    this.utils.setStyles((prevStyles) =&amp;gt; ({
      ...prevStyles,
      fontWeight: nextFontStyle,
    }));
  }

  undo() {
    this.utils.setStyles((prevStyles) =&amp;gt; ({
      ...prevStyles,
      fontWeight: this.prevFontWeight,
    }));
  }

  getInfo() {
    return "Bold Command : Change into " + this.getNextStyle();
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, it is pretty straight forward. When we instantiate the &lt;code&gt;BoldCommand&lt;/code&gt;, we store the previous state. And if we decided to execute the command, we call &lt;code&gt;getNextStyle&lt;/code&gt; to get the next style and apply it to the "outer" world by calling &lt;code&gt;setStyles&lt;/code&gt; from &lt;code&gt;utils&lt;/code&gt;. To revert the changes, we just need to call &lt;code&gt;undo&lt;/code&gt; to apply previous stored state into the current styling.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ItalicCommand&lt;/code&gt; and &lt;code&gt;UnderlineCommand&lt;/code&gt; are quite similar with the code above. You can see it &lt;a href="https://github.com/mustafamilyas/command-pattern-undo-redo/blob/main/src/command.ts"&gt;here&lt;/a&gt; or you can do it yourselves as an exercise.&lt;/p&gt;

&lt;p&gt;The next that we need is the "receiver", the one that manage the action. In React, we can implement this in many way, but in this article, we will create a custom hook as you can see below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function useHistoryManager&amp;lt;T&amp;gt;() {
  const [forwardHistory, setForwardHistory] = useState&amp;lt;Command&amp;lt;T&amp;gt;[]&amp;gt;([]);
  const [backHistory, setBackHistory] = useState&amp;lt;Command&amp;lt;T&amp;gt;[]&amp;gt;([]);

  const executeCommand = async (command: Command&amp;lt;T&amp;gt;) =&amp;gt; {
    // clear forward history
    setForwardHistory([]);
    await command.execute();
    setBackHistory((prev) =&amp;gt; [...prev, command]);
  };
  const redo = async () =&amp;gt; {
    if (!forwardHistory.length) return;
    const topRedoCommand = forwardHistory[forwardHistory.length - 1];
    await topRedoCommand.execute();
    setForwardHistory((prev) =&amp;gt; prev.slice(0, -1));
    setBackHistory((prev) =&amp;gt; [...prev, topRedoCommand]);
  };
  const undo = async () =&amp;gt; {
    if (!backHistory.length) return;
    const topUndoCommand = backHistory[backHistory.length - 1];
    await topUndoCommand.undo();
    setBackHistory((prev) =&amp;gt; prev.slice(0, -1));
    setForwardHistory((prev) =&amp;gt; [...prev, topUndoCommand]);
  };

  const histories = useMemo(() =&amp;gt; {
    const formattedBackHistory = backHistory.map((command, index) =&amp;gt; ({
      type: "undo",
      command,
      message: command.getInfo(),
    }));
    const formattedForwardHistory = [...forwardHistory]
      .reverse()
      .map((command, index) =&amp;gt; ({
        type: "redo",
        command,
        message: command.getInfo(),
      }));
    return [...formattedBackHistory, ...formattedForwardHistory];
  }, [backHistory.length, forwardHistory.length]);

  return {
    executeCommand,
    redo,
    undo,
    histories,
  };
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;useHistoryManager&lt;/code&gt; manages actions by storing action commands into two places, &lt;code&gt;forwardHistory&lt;/code&gt; and &lt;code&gt;backHistory&lt;/code&gt;. &lt;code&gt;backHistory&lt;/code&gt; stores all of the actions that have already been done. &lt;code&gt;forwardHistory&lt;/code&gt; stores all of the actions that could be done. If we want to introduce new action, we need to call it using &lt;code&gt;executeCommand&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The last thing that we need to set up is the "invoker". To do this, we can just call our custom context and execute the command based on the API that we already made.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default function App() {
  const [styles, setStyles] = useState&amp;lt;CSSProperties&amp;gt;({});
  const utils = { styles, setStyles };
  const { executeCommand, redo, undo, histories } =
    useHistoryManager&amp;lt;CommandUtils&amp;gt;();

  const setTextToItalic = async () =&amp;gt; {
    const italicCommand = new ItalicCommand(utils);
    await executeCommand(italicCommand);
  };

  const setTextToBold = async () =&amp;gt; {
    const boldCommand = new BoldCommand(utils);
    await executeCommand(boldCommand);
  };

  const setTextToUnderline = async () =&amp;gt; {
    const underlineCommand = new UnderlineCommand(utils);
    await executeCommand(underlineCommand);
  };

  return (
    &amp;lt;div className={appStyles.container}&amp;gt;
      &amp;lt;div className={appStyles.editor}&amp;gt;
        &amp;lt;p className={appStyles.mainText} style={styles}&amp;gt;
          Hello from react!
        &amp;lt;/p&amp;gt;
        &amp;lt;div className={appStyles.actions}&amp;gt;
          &amp;lt;button
            className={appStyles.button}
            onClick={setTextToItalic}
            title="italic"
          &amp;gt;
            &amp;lt;img src="/assets/format_italic.svg" alt="italic" /&amp;gt;
          &amp;lt;/button&amp;gt;
          &amp;lt;button
            className={appStyles.button}
            onClick={setTextToBold}
            title="bold"
          &amp;gt;
            &amp;lt;img src="/assets/format_bold.svg" alt="bold" /&amp;gt;
          &amp;lt;/button&amp;gt;
          &amp;lt;button
            className={appStyles.button}
            onClick={setTextToUnderline}
            title="underline"
          &amp;gt;
            &amp;lt;img src="/assets/format_underline.svg" alt="underline" /&amp;gt;
          &amp;lt;/button&amp;gt;
          &amp;lt;button className={appStyles.button} onClick={undo} title="undo"&amp;gt;
            &amp;lt;img src="/assets/undo.svg" alt="undo" /&amp;gt;
          &amp;lt;/button&amp;gt;
          &amp;lt;button className={appStyles.button} onClick={redo} title="redo"&amp;gt;
            &amp;lt;img src="/assets/redo.svg" alt="redo" /&amp;gt;
          &amp;lt;/button&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;div className={appStyles.history}&amp;gt;
        &amp;lt;h2 className={appStyles.historyHeader}&amp;gt;History&amp;lt;/h2&amp;gt;
        &amp;lt;ol className={appStyles.historyContent}&amp;gt;
          &amp;lt;li className={`${appStyles.historyItem}`}&amp;gt;
            &amp;lt;button&amp;gt;Initial&amp;lt;/button&amp;gt;
          &amp;lt;/li&amp;gt;
          {histories.map((history, index) =&amp;gt; (
            &amp;lt;li
              className={`${appStyles.historyItem} ${
                history.type === "undo" ? appStyles.undo : appStyles.redo
              }`}
              key={index + history.type + history.message}
            &amp;gt;
              &amp;lt;button&amp;gt;{history.message}&amp;lt;/button&amp;gt;
            &amp;lt;/li&amp;gt;
          ))}
        &amp;lt;/ol&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hooray!, we can create undo-redo using the command pattern. If you want to see this live in action, click &lt;a href="https://command-pattern-undo-redo.vercel.app/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZIENVT8c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4tgdb424h1hbucmbr3pb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZIENVT8c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4tgdb424h1hbucmbr3pb.gif" alt="preview of the application that we want to build" width="448" height="560"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;With the command pattern, we can simplify action information into a series of objects that can be triggered at a convenient time. But the usage of command pattern is very limited, as this pattern requires a lot of boilerplates that in many cases we don't necessarily need them.&lt;/p&gt;

&lt;p&gt;All of the codes above can be found on &lt;a href="https://github.com/mustafamilyas/command-pattern-undo-redo"&gt;this repository&lt;/a&gt;. If you have any comments or feedback, feel free to leave a comment below. Will be happy to discuss this with you all. Cheers!&lt;/p&gt;

&lt;h3&gt;
  
  
  Further Reading
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://react.dev/learn/tutorial-tic-tac-toe"&gt;React Tic Tac Toe&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Command_pattern"&gt;Command Pattern - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://refactoring.guru/design-patterns/command"&gt;Command Pattern - Refactoring.guru&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>react</category>
      <category>webdev</category>
      <category>designpatterns</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
