<?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>Forem: Kelvyn Thai</title>
    <description>The latest articles on Forem by Kelvyn Thai (@kelvynthai).</description>
    <link>https://forem.com/kelvynthai</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%2F977712%2F90d9d534-352b-440d-ac8c-5a22e064f8fd.jpeg</url>
      <title>Forem: Kelvyn Thai</title>
      <link>https://forem.com/kelvynthai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/kelvynthai"/>
    <language>en</language>
    <item>
      <title>Stop blindly replacing `enum` with `as const`</title>
      <dc:creator>Kelvyn Thai</dc:creator>
      <pubDate>Fri, 13 Mar 2026 07:18:46 +0000</pubDate>
      <link>https://forem.com/kelvynthai/stop-blindly-replacing-enum-with-as-const-56o8</link>
      <guid>https://forem.com/kelvynthai/stop-blindly-replacing-enum-with-as-const-56o8</guid>
      <description>&lt;p&gt;You’ve probably seen many posts suggesting that we should replace TypeScript &lt;code&gt;enum&lt;/code&gt; with &lt;code&gt;as const&lt;/code&gt; objects because of benefits like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;better tree-shaking&lt;/li&gt;
&lt;li&gt;smaller bundle size&lt;/li&gt;
&lt;li&gt;less runtime code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That advice is not entirely wrong, but it is often oversimplified.&lt;/p&gt;

&lt;p&gt;The real question is not &lt;strong&gt;“Should we stop using enums?”&lt;/strong&gt;&lt;br&gt;
The better question is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do we actually understand what TypeScript generates behind the scenes, and what trade-offs we are making?&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The key idea: reverse mapping
&lt;/h2&gt;

&lt;p&gt;For &lt;strong&gt;numeric enums&lt;/strong&gt;, TypeScript generates extra runtime code.&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;enum&lt;/span&gt; &lt;span class="nx"&gt;Direction&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Up&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Down&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Right&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 is transpiled to JavaScript roughly like this:&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;var&lt;/span&gt; &lt;span class="nx"&gt;Direction&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Direction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Direction&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Direction&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Up&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Up&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;Direction&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Direction&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Down&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Down&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;Direction&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Direction&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Left&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Left&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;Direction&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Direction&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Right&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Right&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;Direction&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Direction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first glance, that looks weird.&lt;br&gt;
But here is what is happening:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;TypeScript creates an &lt;strong&gt;IIFE&lt;/strong&gt; (Immediately Invoked Function Expression).&lt;/li&gt;
&lt;li&gt;It assigns &lt;code&gt;Direction["Up"] = 0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;That assignment returns &lt;code&gt;0&lt;/code&gt;, so TypeScript also sets &lt;code&gt;Direction[0] = "Up"&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So at runtime, we get an object like this:&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="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;Up&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Down&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Up&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Down&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Left&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Right&lt;/span&gt;&lt;span class="dl"&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 is called &lt;strong&gt;reverse mapping&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That means:&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="nx"&gt;Direction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Up&lt;/span&gt; &lt;span class="c1"&gt;// 0&lt;/span&gt;
&lt;span class="nx"&gt;Direction&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;// "Up"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why some developers prefer &lt;code&gt;as const&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Now compare that with:&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;const&lt;/span&gt; &lt;span class="nx"&gt;DirectionConst&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;Up&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Up&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;Down&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Down&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;Left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Left&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;Right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Right&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="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is just a plain JavaScript object.&lt;br&gt;
No IIFE.&lt;br&gt;
No reverse mapping.&lt;br&gt;
No extra enum runtime emit.&lt;/p&gt;

&lt;p&gt;That is why many developers say &lt;code&gt;as const&lt;/code&gt; can be better for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;frontend apps&lt;/li&gt;
&lt;li&gt;config objects&lt;/li&gt;
&lt;li&gt;route names&lt;/li&gt;
&lt;li&gt;statuses&lt;/li&gt;
&lt;li&gt;action types&lt;/li&gt;
&lt;li&gt;other JS-first patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  But does that mean &lt;code&gt;enum&lt;/code&gt; is bad?
&lt;/h2&gt;

&lt;p&gt;Not at all.&lt;/p&gt;

&lt;p&gt;The real issue is that many people &lt;strong&gt;blindly replace enums without understanding the use case&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Because not all enums are the same:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;numeric enums&lt;/strong&gt; generate reverse mapping&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;string enums&lt;/strong&gt; do not generate reverse mapping&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;const enum&lt;/code&gt;&lt;/strong&gt; has a different trade-off again&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;as const&lt;/code&gt;&lt;/strong&gt; is not a 1:1 replacement for every enum use case&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The important takeaway
&lt;/h2&gt;

&lt;p&gt;We should not blindly replace &lt;code&gt;enum&lt;/code&gt; with &lt;code&gt;as const&lt;/code&gt; just because it is trending.&lt;/p&gt;

&lt;p&gt;Instead, we should ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do we need reverse mapping?&lt;/li&gt;
&lt;li&gt;Do we need a runtime enum object?&lt;/li&gt;
&lt;li&gt;Are we using numeric enums or string enums?&lt;/li&gt;
&lt;li&gt;Is this app code, library code, or protocol/compiler-style code?&lt;/li&gt;
&lt;li&gt;Are we optimizing for explicitness, bundle size, or JS simplicity?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final thought
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;as const&lt;/code&gt; is a great pattern.&lt;br&gt;
But &lt;code&gt;enum&lt;/code&gt; is not automatically wrong.&lt;/p&gt;

&lt;p&gt;The real senior move is not to follow slogans like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Stop using enums.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The real senior move is to understand &lt;strong&gt;how TypeScript works behind the scenes&lt;/strong&gt; and choose the right tool for the right use case.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>javascript</category>
    </item>
    <item>
      <title>My Friend Failed an Interview at a Top Company Because of One JavaScript Question: this</title>
      <dc:creator>Kelvyn Thai</dc:creator>
      <pubDate>Wed, 04 Mar 2026 09:59:41 +0000</pubDate>
      <link>https://forem.com/kelvynthai/my-friend-failed-an-interview-at-a-top-company-because-of-one-javascript-question-this-9k8</link>
      <guid>https://forem.com/kelvynthai/my-friend-failed-an-interview-at-a-top-company-because-of-one-javascript-question-this-9k8</guid>
      <description>&lt;p&gt;Recently my friend failed an interview at a top tech company.&lt;/p&gt;

&lt;p&gt;The interviewer didn't ask about algorithms.&lt;br&gt;
Not about system design.&lt;/p&gt;

&lt;p&gt;Just one topic:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The &lt;code&gt;this&lt;/code&gt; keyword in JavaScript.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At first glance the questions looked simple.&lt;br&gt;&lt;br&gt;
But they were designed to reveal whether the candidate truly understands &lt;strong&gt;runtime binding&lt;/strong&gt; in JavaScript.&lt;/p&gt;

&lt;p&gt;Let's walk through the same interview questions.&lt;/p&gt;


&lt;h1&gt;
  
  
  The Golden Rule of &lt;code&gt;this&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;Before looking at the questions, remember this rule:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;this&lt;/code&gt; is determined by how a function is called, not where it is defined.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This single rule explains most &lt;code&gt;this&lt;/code&gt; bugs in JavaScript.&lt;/p&gt;

&lt;p&gt;Another important rule:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Arrow functions do not have their own &lt;code&gt;this&lt;/code&gt;. They capture it from the surrounding scope.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Understanding these two rules is key to solving the following interview questions.&lt;/p&gt;


&lt;h1&gt;
  
  
  Interview Question 1 — Object Method vs Unbound Function
&lt;/h1&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;module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nf"&gt;getX&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;getXLambda&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;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getX&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getXLambda&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;unboundGetX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getX&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;unboundGetXLambda&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getXLambda&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;unboundGetX&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;unboundGetXLambda&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  What many developers answer
&lt;/h3&gt;


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

&lt;/div&gt;

&lt;h3&gt;
  
  
  Actual result (typical browser environment)
&lt;/h3&gt;


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

&lt;/div&gt;

&lt;h3&gt;
  
  
  Why?
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Case 1
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getX&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This is a &lt;strong&gt;method call&lt;/strong&gt;, so:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Result:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;h4&gt;
  
  
  Case 2
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getXLambda&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Arrow functions &lt;strong&gt;do not bind &lt;code&gt;this&lt;/code&gt; to the object&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
They capture &lt;code&gt;this&lt;/code&gt; from the surrounding scope (usually the global scope).&lt;/p&gt;

&lt;p&gt;So:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;this !== module
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;h4&gt;
  
  
  Case 3
&lt;/h4&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;unboundGetX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getX&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;unboundGetX&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the function is extracted from the object and called as a &lt;strong&gt;plain function&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;this === window (browser) or undefined (strict mode)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;h4&gt;
  
  
  Case 4
&lt;/h4&gt;

&lt;p&gt;Arrow functions still reference the outer scope &lt;code&gt;this&lt;/code&gt;, so extracting them does not change behavior.&lt;/p&gt;

&lt;p&gt;Result:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Fix
&lt;/h3&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;boundGetX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;module&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;boundGetX&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// 42&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Interview Question 2 — Callable Function Object
&lt;/h1&gt;

&lt;p&gt;Now the interviewer moved to a slightly trickier example.&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;MyNum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;defaultValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kr"&gt;number&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;MyNum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultValue&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;MyNum&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;MyNum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;MyNum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MyNum&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What developers expect
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Actual result
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why?
&lt;/h3&gt;

&lt;p&gt;Calling:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;is a &lt;strong&gt;plain function call&lt;/strong&gt;, so &lt;code&gt;this&lt;/code&gt; becomes the global object (or &lt;code&gt;undefined&lt;/code&gt; in strict mode).&lt;/p&gt;

&lt;p&gt;The function effectively evaluates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;window.defaultValue * window.scale
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both are &lt;code&gt;undefined&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;undefined * undefined → NaN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Arrow Function Version
&lt;/h3&gt;

&lt;p&gt;Some developers try to fix it using an arrow function.&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;MyNum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&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="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultValue&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scale&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 &lt;strong&gt;still does not work&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Arrow functions capture &lt;code&gt;this&lt;/code&gt; from the surrounding scope, not from the function object.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fix
&lt;/h3&gt;

&lt;p&gt;Bind the function to itself:&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;boundMyNum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;MyNum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;MyNum&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;boundMyNum&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// 5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Interview Question 3 — Old React Class Component Bug
&lt;/h1&gt;

&lt;p&gt;This problem appeared frequently in older React class components.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&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="k"&gt;super&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;handleClick&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;handleClickLambda&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;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;render&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleClick&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          Normal Function
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleClickLambda&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          Arrow Function
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What happens?
&lt;/h3&gt;

&lt;p&gt;For the normal function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;this.handleClick
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;React extracts the function and executes it like:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;So:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cannot read property 'setState' of undefined
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Fix
&lt;/h3&gt;

&lt;p&gt;Bind it in the constructor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleClick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleClick&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why Arrow Functions Work
&lt;/h3&gt;

&lt;p&gt;Arrow functions capture &lt;code&gt;this&lt;/code&gt; from the class instance, so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;this === Counter instance
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is why arrow functions became popular in React class components.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why These Questions Matter
&lt;/h1&gt;

&lt;p&gt;These questions are not about syntax.&lt;/p&gt;

&lt;p&gt;They test whether you understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;runtime binding&lt;/li&gt;
&lt;li&gt;callback execution&lt;/li&gt;
&lt;li&gt;arrow function lexical &lt;code&gt;this&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;how JavaScript functions behave when extracted from objects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Frameworks like React, Node.js, and Express rely heavily on callbacks.&lt;br&gt;&lt;br&gt;
If you misunderstand &lt;code&gt;this&lt;/code&gt;, subtle bugs appear everywhere.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final Takeaway
&lt;/h1&gt;

&lt;p&gt;The most important rule to remember:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;this&lt;/code&gt; depends on how a function is called, not where it is written.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Arrow functions capture &lt;code&gt;this&lt;/code&gt; from their surrounding scope.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you remember these two rules, debugging most &lt;code&gt;this&lt;/code&gt; issues becomes much easier.&lt;/p&gt;

&lt;p&gt;And in my friend's case, understanding that rule would have made the difference between &lt;strong&gt;passing and failing the interview.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
    </item>
    <item>
      <title>What is the diff between `Date` vs `new Date()` or `String` vs `new String()`</title>
      <dc:creator>Kelvyn Thai</dc:creator>
      <pubDate>Sat, 07 Feb 2026 14:00:05 +0000</pubDate>
      <link>https://forem.com/kelvynthai/what-is-the-diff-between-date-vs-new-date-1ep0</link>
      <guid>https://forem.com/kelvynthai/what-is-the-diff-between-date-vs-new-date-1ep0</guid>
      <description>&lt;p&gt;Hi everyone, I'm Kelvyn. Yesterday I did a deep dive regarding the&lt;br&gt;
&lt;strong&gt;constructor signature&lt;/strong&gt; side. Today we will go through this topic to&lt;br&gt;
clarify some questions related to:&lt;/p&gt;

&lt;p&gt;1/ What is the diff between new (constructor signature) vs executing a function (call signature)?&lt;/p&gt;

&lt;p&gt;2/ Even if we know these definitions, how can we declare their typings in TypeScript?&lt;/p&gt;

&lt;p&gt;3/ Even if we understand the diff and know how to declare typings following the docs, why do we have the new keyword? What is an instance? Why do primitives and built-in objects / object prototypes exist?&lt;/p&gt;

&lt;p&gt;Great! Let's start.&lt;/p&gt;


&lt;h2&gt;
  
  
  Prerequisite
&lt;/h2&gt;

&lt;p&gt;If you still haven't read about call constructors, you can quickly&lt;br&gt;
review it here:\&lt;br&gt;
👉 &lt;a href="https://dev.to/kelvynthai/build-a-simple-mini-dayjs-clone-logger-middleware-by-call-signatures-in-typescript-8pj"&gt;https://dev.to/kelvynthai/build-a-simple-mini-dayjs-clone-logger-middleware-by-call-signatures-in-typescript-8pj&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;Well, sometimes you might see code like this in your codebase:&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can call these &lt;strong&gt;built-in objects&lt;/strong&gt; in JavaScript, and they are&lt;br&gt;
supported by the runtime environment. But what is really happening&lt;br&gt;
behind the scenes? How do we have them? How do we declare their typings?&lt;/p&gt;


&lt;h1&gt;
  
  
  1/ What is the diff between &lt;code&gt;new&lt;/code&gt; (constructor signature) vs executing a function (call signature)?
&lt;/h1&gt;

&lt;p&gt;It's quite simple --- with a constructor signature, we must use the&lt;br&gt;
&lt;code&gt;new&lt;/code&gt; keyword. Too easy!&lt;/p&gt;

&lt;p&gt;Such as:&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How about the call signature (you can review the call constructor link)?&lt;br&gt;
Examples include &lt;code&gt;dayjs()&lt;/code&gt; or &lt;code&gt;logger()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One is executed as a function, the other is used with &lt;code&gt;new&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Sounds good?&lt;/p&gt;


&lt;h1&gt;
  
  
  2/ How can we declare their typings in TypeScript?
&lt;/h1&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;ConstructDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;new &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;dateStr&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="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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;constructDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ConstructDate&lt;/span&gt;&lt;span class="p"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myConstructDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;constructDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sat 7 Feb 2026&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Following this way, we can easily declare typings for a constructor&lt;br&gt;
signature. Too easy, right?&lt;/p&gt;

&lt;p&gt;Yeah --- are we done? Can we close the laptop and go to sleep?&lt;/p&gt;

&lt;p&gt;But if you're like me and don't understand how to mix call and&lt;br&gt;
constructor signatures, calm down and continue scrolling 🙂&lt;/p&gt;



&lt;p&gt;I spent a lot of time reading this in TypeScript, and to be honest, it&lt;br&gt;
was quite confusing. From the call signature, we have clear examples of&lt;br&gt;
how to define it. But with constructors, the docs usually only simplify&lt;br&gt;
the declaration of typings and don't give us a method to implement them.&lt;/p&gt;

&lt;p&gt;So let me guide you.&lt;/p&gt;



&lt;p&gt;Let's try printing 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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// function&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// object&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// object&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the root cause is that using the &lt;code&gt;new&lt;/code&gt; keyword returns&lt;br&gt;
an &lt;strong&gt;object&lt;/strong&gt;, not a primitive value. But why is that?&lt;/p&gt;

&lt;p&gt;*** Why does new return an object instead of a primitive? ***&lt;/p&gt;

&lt;p&gt;Because the purpose of new is to create an instance.&lt;/p&gt;

&lt;p&gt;And an instance must be an object so it can:&lt;br&gt;
    • hold properties&lt;br&gt;
    • inherit methods&lt;br&gt;
    • link to a prototype&lt;br&gt;
    • support polymorphism&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The instanceof operator tests to see if the prototype property of a constructor appears anywhere in the prototype chain of an object. The return value is a boolean value. Its behavior can be customized with Symbol.hasInstance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;A primitive cannot do these things.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So this is not accidental — it is by language design.&lt;/p&gt;

&lt;p&gt;Learn more at: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Object prototypes: &lt;a href="https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Object_prototypes" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Object_prototypes&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Instance &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;



&lt;blockquote&gt;
&lt;p&gt;Prototypes are the mechanism by which JavaScript objects inherit features from one another. In this article, we explain what a prototype is, how prototype chains work, and how a prototype for an object can be set.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In JavaScript, we have 3 ways to implement prototype inheritance:&lt;br&gt;
&lt;strong&gt;object, constructor, and class&lt;/strong&gt;. Among these, classes are very&lt;br&gt;
straightforward.&lt;/p&gt;

&lt;p&gt;For example:&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;class&lt;/span&gt; &lt;span class="nc"&gt;Animal&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;eat&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nhoammmm&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Human&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Animal&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;work&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Working and earn money!&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;kelvynThai&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Human&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;Human&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;Human&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// function, object&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;kelvynThai&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;Human&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;kelvynThai&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPrototypeOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;kelvynThai&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;Human&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPrototypeOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Human&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;kelvynThai&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// object&lt;/span&gt;
&lt;span class="nx"&gt;kelvynThai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;work&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Working and earn money!&lt;/span&gt;
&lt;span class="nx"&gt;kelvynThai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eat&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Nhoammmm&lt;/span&gt;

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

&lt;/div&gt;






&lt;p&gt;Now let's move to constructor inheritance:&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;function&lt;/span&gt; &lt;span class="nf"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nx"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Nhoammmm&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;function&lt;/span&gt; &lt;span class="nf"&gt;inheritPrototype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;proto&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ChainLink&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
  &lt;span class="nx"&gt;ChainLink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;proto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ChainLink&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Human&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nx"&gt;Human&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inheritPrototype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;Human&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;work&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Coding and earning money!&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;kelvynThai&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Human&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;Human&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;Human&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// function, object&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;kelvynThai&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;Human&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;kelvynThai&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// object&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPrototypeOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;kelvynThai&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;Human&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPrototypeOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Human&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="nx"&gt;kelvynThai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;work&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Coding and earning money!&lt;/span&gt;
&lt;span class="nx"&gt;kelvynThai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eat&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Nhoammmm&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;That means our &lt;code&gt;ConstructDate&lt;/code&gt; type should be adjusted from the&lt;br&gt;
primitive &lt;code&gt;'string'&lt;/code&gt; → built-in object &lt;code&gt;String&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="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;MyDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// eslint-disable-next-line&lt;/span&gt;
  &lt;span class="k"&gt;new &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;dateStr&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="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// constructor -&amp;gt; object&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dateNum&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&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="c1"&gt;// call -&amp;gt; primitive&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MyDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&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="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;number&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="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// always ensure constructor returns an object&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`PARSED_DATE:&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="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&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;`TIMESTAMP:&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DEFAULT_DATE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// OR: throw new TypeError(`Class constructor ShiftedDate cannot be invoked without 'new'`)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;MyDate&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;dateWithNew&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MyDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sat 7 Feb 2026&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// String object&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dateWithoutNew&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MyDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;123456&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// primitive string&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dateWithNew&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;dateWithNew&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
&lt;span class="c1"&gt;// [String: 'PARSED_DATE:Sat 7 Feb 2026']  object&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dateWithoutNew&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;dateWithoutNew&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
&lt;span class="c1"&gt;// TIMESTAMP:123456 string&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; 
&lt;span class="c1"&gt;// 2026-02-07T13:58:57.212Z object&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Understanding the difference between constructor signatures and call&lt;br&gt;
signatures helps us model JavaScript behavior more accurately in&lt;br&gt;
TypeScript.&lt;/p&gt;

&lt;p&gt;TypeScript constructor typings become much easier to reason about.&lt;/p&gt;

&lt;p&gt;This knowledge is especially useful when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Reading built-in APIs\&lt;/li&gt;
&lt;li&gt;  Designing flexible libraries\&lt;/li&gt;
&lt;li&gt;  Modeling function behaviors in TypeScript\&lt;/li&gt;
&lt;li&gt;  Understanding how JavaScript works under the hood&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hope this helps clarify the differences. Happy coding 🚀&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Build a simple Mini Day.js Clone &amp; Logger Middleware by Call Signatures in TypeScript</title>
      <dc:creator>Kelvyn Thai</dc:creator>
      <pubDate>Wed, 04 Feb 2026 05:15:51 +0000</pubDate>
      <link>https://forem.com/kelvynthai/build-a-simple-mini-dayjs-clone-logger-middleware-by-call-signatures-in-typescript-8pj</link>
      <guid>https://forem.com/kelvynthai/build-a-simple-mini-dayjs-clone-logger-middleware-by-call-signatures-in-typescript-8pj</guid>
      <description>&lt;p&gt;Hi everyone,&lt;/p&gt;

&lt;p&gt;Yesterday I spent some time learning more about TypeScript, and I came&lt;br&gt;
across the term &lt;strong&gt;"call signature."&lt;/strong&gt; It's quite helpful when you want&lt;br&gt;
to define something like a logger middleware or understand how libraries&lt;br&gt;
such as &lt;strong&gt;dayjs&lt;/strong&gt; declare their typings.&lt;br&gt;
Source: &lt;a href="https://www.typescriptlang.org/docs/handbook/2/functions.html#call-signatures" rel="noopener noreferrer"&gt;https://www.typescriptlang.org/docs/handbook/2/functions.html#call-signatures&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  First, understand that a function is an object
&lt;/h3&gt;

&lt;p&gt;You should know that the &lt;strong&gt;typeof a function prototype is &lt;code&gt;object&lt;/code&gt;&lt;/strong&gt; ---&lt;br&gt;
I'm referring to the prototype, not &lt;code&gt;typeof function&lt;/code&gt; itself.&lt;/p&gt;

&lt;p&gt;If you are not familiar with prototypes, I strongly suggest reading&lt;br&gt;
about them first before continuing (&lt;a href="https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Object_prototypes" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Object_prototypes&lt;/a&gt;).&lt;/p&gt;




&lt;h3&gt;
  
  
  The problem we want to solve
&lt;/h3&gt;

&lt;p&gt;How can we configure a function and then execute it with those&lt;br&gt;
configurations?&lt;/p&gt;

&lt;p&gt;This is somewhat similar to a &lt;strong&gt;closure&lt;/strong&gt;, but closures encapsulate&lt;br&gt;
state using variable scope. With a call signature, the state is exposed&lt;br&gt;
outside --- meaning we &lt;em&gt;can mutate the properties&lt;/em&gt; if needed.&lt;/p&gt;

&lt;p&gt;You might encounter this pattern somewhere in your codebase, especially&lt;br&gt;
in libraries like &lt;strong&gt;dayjs&lt;/strong&gt; or when implementing a logger middleware.&lt;/p&gt;

&lt;p&gt;But how do we define it properly in TypeScript?&lt;/p&gt;




&lt;h3&gt;
  
  
  Call Signatures in TypeScript
&lt;/h3&gt;

&lt;p&gt;In JavaScript, functions can have properties in addition to being&lt;br&gt;
callable.&lt;/p&gt;

&lt;p&gt;However, the typical function type syntax does &lt;strong&gt;not allow declaring&lt;br&gt;
properties&lt;/strong&gt;. If we want to describe something callable &lt;strong&gt;with&lt;br&gt;
properties&lt;/strong&gt;, we can use a &lt;strong&gt;call signature inside an object type&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Before continuing, it's helpful if you already understand closures or&lt;br&gt;
have used them to encapsulate function state (for example, in React&lt;br&gt;
hooks).&lt;/p&gt;

&lt;p&gt;A call signature is similar to a closure --- but the key difference is&lt;br&gt;
that the state lives &lt;strong&gt;outside the function&lt;/strong&gt;, so it can be modified.&lt;/p&gt;

&lt;p&gt;Let's look at an example.&lt;/p&gt;




&lt;h2&gt;
  
  
  Example 1 --- Mini Day.js Clone
&lt;/h2&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;DayJS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;timezone&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="nl"&gt;description&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="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;Date&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dayjs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;Date&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;tz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dayjs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timezone&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;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DateTimeFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;en-US&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="na"&gt;timeZone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;year&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;numeric&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;month&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2-digit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;day&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2-digit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;hour&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2-digit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;minute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2-digit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;second&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2-digit&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;formatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;DayJS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;dayjs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timezone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Asia/Ho_Chi_Minh&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;dayjs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Mini DayJS clone&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;dayjs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2026-02-04&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="c1"&gt;// 02/04/2026, 07:00:00 AM&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;👉 Here, &lt;code&gt;dayjs&lt;/code&gt; is both:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  callable like a function\&lt;/li&gt;
&lt;li&gt;  configurable via properties&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is exactly what a &lt;strong&gt;call signature&lt;/strong&gt; enables.&lt;/p&gt;




&lt;h2&gt;
  
  
  Example 2 --- Logger Middleware
&lt;/h2&gt;

&lt;p&gt;Now let's implement a logger middleware with configurable properties&lt;br&gt;
such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;isEnabled&lt;/code&gt; (based on production env)&lt;/li&gt;
&lt;li&gt;  log type (&lt;code&gt;warning&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt;, &lt;code&gt;info&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;  message payload
&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;LoggerData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;message&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="nl"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;unknown&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;LoggerType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;warning&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;info&lt;/span&gt;&lt;span class="dl"&gt;"&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;LoggerMiddleware&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;isEnabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&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="nx"&gt;LoggerType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;LoggerData&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&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;initializedSomeService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&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="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;warningLogger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;LoggerMiddleware&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;warningLogger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isEnabled&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`TYPE:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;warningLogger&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="s2"&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;data&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c1"&gt;// configure properties&lt;/span&gt;
  &lt;span class="nx"&gt;warningLogger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isEnabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;warningLogger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&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;warning&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// execute like a function&lt;/span&gt;
  &lt;span class="nf"&gt;warningLogger&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;This is a warning message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ab85592d-b7cf-4623-9884-aa70f40814f7&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="nf"&gt;initializedSomeService&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;Result:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TYPE:warning {
  data: {
    message: 'This is a warning message',
    metadata: { id: 'ab85592d-b7cf-4623-9884-aa70f40814f7' }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;h2&gt;
  
  
  When should you use call signatures?
&lt;/h2&gt;

&lt;p&gt;Use them when you want something that is:&lt;/p&gt;

&lt;p&gt;✅ callable like a function\&lt;br&gt;
✅ configurable via properties\&lt;br&gt;
✅ strongly typed&lt;/p&gt;

&lt;p&gt;Typical real-world use cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  logger utilities\&lt;/li&gt;
&lt;li&gt;  analytics trackers\&lt;/li&gt;
&lt;li&gt;  feature flag executors\&lt;/li&gt;
&lt;li&gt;  SDK-style APIs\&lt;/li&gt;
&lt;li&gt;  libraries like &lt;strong&gt;dayjs&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Closure vs Call Signature (Quick Insight)
&lt;/h2&gt;

&lt;p&gt;Closure                 Call Signature&lt;/p&gt;




&lt;p&gt;Encapsulates state      Exposes state&lt;br&gt;
  Safer from mutation     Mutable&lt;br&gt;
  More functional style   More library-style&lt;br&gt;
  Common in hooks         Common in SDK design&lt;/p&gt;

&lt;p&gt;Neither is better --- choose based on your design goal.&lt;/p&gt;

&lt;p&gt;If you want immutability → prefer closures.\&lt;br&gt;
If you want configurability → call signatures shine.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Call signatures are a small TypeScript feature, but once you understand&lt;br&gt;
them, you'll start noticing this pattern in many libraries.&lt;/p&gt;

&lt;p&gt;They provide a powerful way to design APIs that feel natural to&lt;br&gt;
JavaScript --- configurable, callable, and strongly typed.&lt;/p&gt;

&lt;p&gt;Definitely worth adding to your TypeScript toolbox 🚀&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>typescript</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How to integrate AI Agent Skills to NextJS App &amp; Cursor AI ?</title>
      <dc:creator>Kelvyn Thai</dc:creator>
      <pubDate>Thu, 22 Jan 2026 11:48:57 +0000</pubDate>
      <link>https://forem.com/kelvynthai/how-to-integrate-ai-agent-skills-to-nextjs-app-cursor-ai--o23</link>
      <guid>https://forem.com/kelvynthai/how-to-integrate-ai-agent-skills-to-nextjs-app-cursor-ai--o23</guid>
      <description>&lt;p&gt;Hi everyone, I’m Kelvyn, a Software Engineer.&lt;/p&gt;

&lt;p&gt;Recently, I’ve been reading many articles related to &lt;strong&gt;React Best Practices for AI Agent Skills&lt;/strong&gt;. This topic is being discussed widely in companies and also appears frequently in my feeds.&lt;br&gt;
So if you’re someone working with &lt;strong&gt;Cursor, React, or Next.js&lt;/strong&gt;, and you’re curious about how to integrate AI Agent Skills into your workflow, this article is for you.&lt;/p&gt;

&lt;p&gt;When I first started reading about this topic, I asked myself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What are AI Agent Skills?&lt;/li&gt;
&lt;li&gt;What are the real use cases?&lt;/li&gt;
&lt;li&gt;Even if we understand them, how do we integrate them?&lt;/li&gt;
&lt;li&gt;And how can we ensure they’re working as expected?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So let’s start by breaking this problem down step by step.&lt;/p&gt;


&lt;h2&gt;
  
  
  &lt;strong&gt;I. Rules vs Skills — Summary&lt;/strong&gt;
&lt;/h2&gt;
&lt;h3&gt;
  
  
  What are Rules?
&lt;/h3&gt;

&lt;p&gt;Rules define the &lt;strong&gt;law&lt;/strong&gt;.&lt;br&gt;
Very straightforward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Red light → stop&lt;/li&gt;
&lt;li&gt;Green light → go&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;FYI:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Rules provide system-level instructions to an Agent. They bundle prompts, scripts, and more together, making it easy to manage and share workflows across your team.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  How Rules Work
&lt;/h3&gt;

&lt;p&gt;Large Language Models &lt;strong&gt;don’t retain memory between completions&lt;/strong&gt;.&lt;br&gt;
Rules provide &lt;strong&gt;persistent, reusable context&lt;/strong&gt; at the prompt level.&lt;/p&gt;

&lt;p&gt;When applied, rule contents are included at the &lt;strong&gt;start of the model context&lt;/strong&gt;, giving the AI consistent guidance for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;generating code&lt;/li&gt;
&lt;li&gt;interpreting edits&lt;/li&gt;
&lt;li&gt;helping with workflows&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Examples
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;“Whenever you write TypeScript, enforce exhaustive handling for union types.”&lt;/li&gt;
&lt;li&gt;“UI modules must follow container/presentational separation. Presentational components must be pure (no hooks, no side effects).”&lt;/li&gt;
&lt;li&gt;“Enforce a maximum of 100 lines of code per UI file.”&lt;/li&gt;
&lt;li&gt;“Enforce &lt;code&gt;.webp&lt;/code&gt; format for images.”&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  What Rules Are
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Standards, requirements, and guardrails&lt;/li&gt;
&lt;li&gt;Define what must &lt;strong&gt;always&lt;/strong&gt; be true&lt;/li&gt;
&lt;li&gt;Shape how the AI reasons and makes decisions&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Skills
&lt;/h2&gt;
&lt;h3&gt;
  
  
  What Are Skills?
&lt;/h3&gt;

&lt;p&gt;Skills define the &lt;strong&gt;method or capability&lt;/strong&gt; to do something.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;FYI:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Agent Skills are an open standard for extending AI agents with specialized capabilities. Skills package domain-specific knowledge and workflows that agents can use to perform specific tasks.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Examples
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;“How to enforce exhaustiveness in a TypeScript switch.”&lt;/li&gt;
&lt;li&gt;“How to refactor a component into container + presentational.”&lt;/li&gt;
&lt;li&gt;“How to review a module structure.”&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  What Skills Are
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Named capabilities or playbooks&lt;/li&gt;
&lt;li&gt;Teach the AI &lt;strong&gt;how to do something well&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;th&gt;Skill&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Enforce exhaustive union handling&lt;/td&gt;
&lt;td&gt;TS Exhaustive Switch Checking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enforce container/presentational architecture&lt;/td&gt;
&lt;td&gt;Container–Presentational Review &amp;amp; Refactor&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  &lt;strong&gt;II. What Is React Best Practices?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;React Best Practices is a structured repository for creating and maintaining React best practices optimized for AI agents and LLMs.&lt;/p&gt;

&lt;p&gt;Imagine your team has rules for folder structure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enforcing container/presentational patterns&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Limiting each feature to &lt;strong&gt;3 files&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;index.ts&lt;/code&gt; (exports)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ComponentName.tsx&lt;/code&gt; (UI)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ComponentName.actions.ts&lt;/code&gt; (logic, hooks, queries)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Normally, you’d document this in something like &lt;code&gt;docs/component-structure.md&lt;/code&gt;.&lt;br&gt;
Every time you want a code review, you’d need Cursor to re-read that document.&lt;/p&gt;

&lt;p&gt;Another example is enforcing &lt;strong&gt;exhaustive switch checking&lt;/strong&gt; for render blocks or feature flags.&lt;/p&gt;

&lt;p&gt;So the question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can we make these rules &lt;strong&gt;global&lt;/strong&gt;, reusable, and automatically applied — without re-injecting context every time?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Rules file format: &lt;a href="https://cursor.com/docs/context/rules#rule-file-format" rel="noopener noreferrer"&gt;https://cursor.com/docs/context/rules#rule-file-format&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Rules file structure: &lt;a href="https://github.com/vercel-labs/agent-skills/tree/main/skills/react-best-practices#rule-file-structure" rel="noopener noreferrer"&gt;https://github.com/vercel-labs/agent-skills/tree/main/skills/react-best-practices#rule-file-structure&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  &lt;strong&gt;III. How to Integrate with a Next.js Application&lt;/strong&gt;
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Initialize a Next.js app: &lt;a href="https://nextjs.org/docs/app/getting-started/installation" rel="noopener noreferrer"&gt;https://nextjs.org/docs/app/getting-started/installation&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  Step 1: Set Up React Best Practices
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx add-skill vercel-labs/agent-skills &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You should see new folders created.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F6bn69ygfhiszoto40y2o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F6bn69ygfhiszoto40y2o.png" alt="React Best Practices Installed" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great! The repository has been cloned successfully.&lt;/p&gt;

&lt;p&gt;Reference: &lt;a href="https://vercel.com/blog/introducing-react-best-practices" rel="noopener noreferrer"&gt;https://vercel.com/blog/introducing-react-best-practices&lt;/a&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  Step 2: Enable “Nightly” Mode in Cursor
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open Cursor Settings&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Mac: &lt;code&gt;Cmd + Shift + J&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Windows/Linux: &lt;code&gt;Ctrl + Shift + J&lt;/code&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to the &lt;strong&gt;Beta&lt;/strong&gt; tab&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Nightly&lt;/strong&gt; mode&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;You may need to restart Cursor to ensure it’s enabled.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fzy79mdd6op5waq18axw2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fzy79mdd6op5waq18axw2.png" alt="Enable Nightly Mode" width="800" height="434"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  Step 3: Ensure Skills Are Applied
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open Cursor Settings&lt;/li&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Rules, Skills, Subagents&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Verify skills appear under &lt;strong&gt;Agent Decides&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fjop8sj4918r6e0g6oc1z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fjop8sj4918r6e0g6oc1z.png" alt="Skills Applied" width="800" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fantastic! We’ve successfully set up AI Agent Skills for a Next.js app.&lt;/p&gt;

&lt;p&gt;Reference: &lt;a href="https://cursor.com/docs/context/skills" rel="noopener noreferrer"&gt;https://cursor.com/docs/context/skills&lt;/a&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  Step 4: Verify That Skills Work
&lt;/h3&gt;

&lt;p&gt;Try a simple prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Review this component
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.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%2Ft7kd041l4lzjf5rmiej8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ft7kd041l4lzjf5rmiej8.png" alt="Initial Review" width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cursor reports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ No waterfall issues&lt;/li&gt;
&lt;li&gt;✅ No bundle size concerns&lt;/li&gt;
&lt;li&gt;✅ No re-render issues&lt;/li&gt;
&lt;li&gt;✅ No JavaScript performance issues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Looks good — but are we fully convinced?&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 5: Create a Rule and Test It
&lt;/h3&gt;

&lt;p&gt;Now let’s add a new rule enforcing module structure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Max 3 files per submodule&lt;/li&gt;
&lt;li&gt;Kebab-case naming&lt;/li&gt;
&lt;li&gt;Container/presentational pattern&lt;/li&gt;
&lt;li&gt;UI must be pure&lt;/li&gt;
&lt;li&gt;Logic in &lt;code&gt;.actions.ts(x)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Re-export via &lt;code&gt;index.ts&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rule reference:&lt;br&gt;
&lt;a href="https://gitlab.com/kelvyn-labs/nextjs-ai-skills-template/-/blob/main/.agents/skills/vercel-react-best-practices/rules/advanced-module-structure.md" rel="noopener noreferrer"&gt;https://gitlab.com/kelvyn-labs/nextjs-ai-skills-template/-/blob/main/.agents/skills/vercel-react-best-practices/rules/advanced-module-structure.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Try again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Review HomePage.tsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.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%2Fvu6q26xyuxphisckkoj5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fvu6q26xyuxphisckkoj5.png" alt="Rule Not Applied" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hmm… this looks strange.&lt;br&gt;
It seems the new skill &lt;strong&gt;wasn’t applied&lt;/strong&gt;.&lt;/p&gt;


&lt;h3&gt;
  
  
  Step 6: Integrate &lt;code&gt;react-best-practices-build&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;After reviewing the documentation and source code, it turns out that AGENTS.md and SKILL.md must be regenerated whenever new rules are added.&lt;/p&gt;

&lt;p&gt;This step is not clearly mentioned in the official docs.&lt;/p&gt;

&lt;p&gt;Clone the repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/vercel-labs/agent-skills.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Move the build package into your workspace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir -p packages
cp -r agent-skills/packages/react-best-practices-build packages/
cp agent-skills/skills/react-best-practices/metadata.json .agents/skills/vercel-react-best-practices
cp agent-skills/skills/react-best-practices/rules/_sections.md \
   agent-skills/skills/react-best-practices/rules/_template.md \
   .agents/skills/vercel-react-best-practices/rules/
rm -rf agent-skills
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update pnpm-workspace.yaml:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;packages:
  - packages/*
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd packages/react-best-practices-build
pnpm install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update SKILL_DIR:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// packages/react-best-practices-build/src/build.ts
export const SKILL_DIR = join(
  __dirname,
  "../../..",
  ".agents/skills/vercel-react-best-practices"
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Validate rules:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Build skills:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.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%2F2x1trv3q4pvyhv9x9i3j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F2x1trv3q4pvyhv9x9i3j.png" alt=" " width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check &lt;code&gt;AGENTS.md&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Frux2ggyvs3i35iy1g389.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Frux2ggyvs3i35iy1g389.png" alt="AGENTS.md Updated" width="800" height="548"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Final Test
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Review this component
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.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%2Fp5obhtbaya5hti3g1vs4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fp5obhtbaya5hti3g1vs4.png" alt="Final Success" width="800" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NICE!&lt;/strong&gt;&lt;br&gt;
The new rule is now enforced automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Rules define &lt;strong&gt;what must always be true&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Skills define &lt;strong&gt;how the AI performs tasks&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Custom rules require rebuilding &lt;code&gt;AGENTS.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Once set up, no extra prompting or context is needed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Full source code:&lt;br&gt;
&lt;a href="https://gitlab.com/kelvyn-labs/nextjs-ai-skills-template" rel="noopener noreferrer"&gt;https://gitlab.com/kelvyn-labs/nextjs-ai-skills-template&lt;/a&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>ai</category>
      <category>cursor</category>
    </item>
    <item>
      <title>Cảm ơn cuộc đời vì mỗi sáng vẫn còn một hơi thở</title>
      <dc:creator>Kelvyn Thai</dc:creator>
      <pubDate>Tue, 13 Jan 2026 09:52:27 +0000</pubDate>
      <link>https://forem.com/kelvynthai/cam-on-cuoc-doi-vi-moi-sang-van-con-mot-hoi-tho-fih</link>
      <guid>https://forem.com/kelvynthai/cam-on-cuoc-doi-vi-moi-sang-van-con-mot-hoi-tho-fih</guid>
      <description>&lt;h1&gt;
  
  
  Cảm ơn cuộc đời vì mỗi sáng vẫn còn một hơi thở
&lt;/h1&gt;

&lt;p&gt;Vừa rồi tôi bị tức ngực đến mức mỗi lần ngồi vào bàn làm việc là không thể tập trung nổi, cơn đau nhức hành hạ khiến tôi không thở được.&lt;br&gt;&lt;br&gt;
Với một người yêu công việc, thích học cái mới như tôi, đó là một thử thách không hề nhỏ.&lt;br&gt;&lt;br&gt;
Sức khỏe cảnh báo, và tâm cũng bắt đầu lên tiếng.&lt;/p&gt;

&lt;p&gt;Suốt một năm qua, buổi tối nào tôi cũng dành cho &lt;strong&gt;learning&lt;/strong&gt; và &lt;strong&gt;growing&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
Tôi hoàn thành JSNAD (Linux Foundation), GitLab CI Fundamentals, LFW211, Coursera Meta, NestJS…&lt;br&gt;&lt;br&gt;
Tôi viết blog chia sẻ về Yarn → PNPM, optimize Docker image, GitLab CI/CD, Next.js…&lt;/p&gt;

&lt;p&gt;Tôi làm tất cả với một niềm tin rất đơn giản:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Cứ gieo hạt tử tế, rồi sẽ có ngày hái quả.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Nhưng rồi, đến lúc nhìn lại, tôi nhận ra sức khỏe mình ngày một kém.&lt;br&gt;&lt;br&gt;
Tôi đi khám cả Đông lẫn Tây y.&lt;/p&gt;

&lt;p&gt;Khi sức khỏe tạm ổn, tôi cố gắng quay lại công ty.&lt;br&gt;&lt;br&gt;
Và rồi tôi nhận ra một sự thật rất giản dị:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;công ty có tôi hay không, mọi thứ vẫn tiếp tục chạy.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sau một tuần nghỉ rồi trở lại, mọi thứ vẫn như cũ.&lt;br&gt;&lt;br&gt;
Lạ là tôi không buồn.&lt;br&gt;&lt;br&gt;
Tôi chỉ thấy một cảm giác bình an, nhẹ nhõm đến khó tin.&lt;/p&gt;

&lt;p&gt;Một cảm giác như thể:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;mình không còn gì để mất.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Không còn làm việc để cầu hạnh phúc,&lt;br&gt;&lt;br&gt;
không còn làm để mong được tăng lương, thăng chức,&lt;br&gt;&lt;br&gt;
không còn làm để tìm ánh mắt ngưỡng mộ hay sự công nhận.&lt;/p&gt;

&lt;p&gt;Chỉ đơn giản là làm – vì đó là việc mình làm được trong kiếp này.&lt;/p&gt;

&lt;p&gt;Mình may mắn hơn rất nhiều người ngoài kia.&lt;br&gt;&lt;br&gt;
Có những người đang thất nghiệp, Tết đến vẫn phải lo từng bữa ăn, từng khoản tiền nhà.&lt;/p&gt;

&lt;p&gt;Còn tôi, ít nhất trong giai đoạn này,&lt;br&gt;&lt;br&gt;
vẫn có một công việc để trở về,&lt;br&gt;&lt;br&gt;
một bàn làm việc để ngồi xuống,&lt;br&gt;&lt;br&gt;
và một nhịp sống đủ ổn để không phải hoảng loạn trước ngày mai.&lt;/p&gt;

&lt;p&gt;Chừng đó thôi,&lt;br&gt;&lt;br&gt;
đã là một phước lớn.&lt;/p&gt;

&lt;p&gt;May mắn là trong khoảng thời gian đó, tôi gặp được &lt;strong&gt;Kinh Phổ Môn&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
Chỉ vài trang chữ thôi, nhưng mỗi lần đọc, tôi lại ngộ ra rất nhiều điều.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tôi trước khi đọc Phổ Môn – và sau khi
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Trước đây&lt;/strong&gt;,&lt;br&gt;&lt;br&gt;
khi được khen tôi phấn khích,&lt;br&gt;&lt;br&gt;
khi được tăng lương tôi nghĩ “mình xứng đáng”,&lt;br&gt;&lt;br&gt;
khi bị bỏ qua tôi thấy tức, thấy đau, thấy uất.&lt;/p&gt;

&lt;p&gt;Tôi nghĩ:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Nếu không được thì coi như công cốc.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Sau khi đọc Phổ Môn&lt;/strong&gt;,&lt;br&gt;&lt;br&gt;
khi được khen tôi biết đó là duyên,&lt;br&gt;&lt;br&gt;
khi được tăng lương tôi chỉ thấy “duyên đang thuận”,&lt;br&gt;&lt;br&gt;
khi bị bỏ qua tôi tự nhắc mình “duyên chưa chín”.&lt;/p&gt;

&lt;p&gt;Và tôi nghĩ khác đi:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“Ta đã gieo, cây đang mọc – dù ai hái trái thì cây vẫn lành.”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Cuối năm nay, tôi mất đi nhiều thứ:&lt;br&gt;&lt;br&gt;
tham lam, ích kỷ, sân si, vô minh.&lt;/p&gt;

&lt;p&gt;Nhưng tôi lại được một thứ rất lớn:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;một trái tim nhẹ hơn.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Và thế là đủ.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cảm ơn cuộc đời&lt;br&gt;&lt;br&gt;
vì mỗi sáng&lt;br&gt;&lt;br&gt;
tôi vẫn còn một hơi thở.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>crisis</category>
      <category>career</category>
      <category>thirty</category>
    </item>
    <item>
      <title>How 'never' Type Prevents Broken Code in Production</title>
      <dc:creator>Kelvyn Thai</dc:creator>
      <pubDate>Tue, 06 Jan 2026 07:51:26 +0000</pubDate>
      <link>https://forem.com/kelvynthai/how-the-never-type-prevents-broken-code-in-production-3ci</link>
      <guid>https://forem.com/kelvynthai/how-the-never-type-prevents-broken-code-in-production-3ci</guid>
      <description>&lt;p&gt;Most production bugs don’t come from syntax errors.&lt;br&gt;
They come from &lt;strong&gt;valid code paths that nobody noticed&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;TypeScript helps a lot — but there’s one class of bugs it &lt;strong&gt;does not catch by default&lt;/strong&gt;, especially in React codebases that evolve over time.&lt;/p&gt;

&lt;p&gt;This article shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a real React bug that silently ships to production&lt;/li&gt;
&lt;li&gt;why TypeScript doesn’t warn you&lt;/li&gt;
&lt;li&gt;how the &lt;code&gt;never&lt;/code&gt; type turns that bug into a &lt;strong&gt;compile-time error&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  What Is the &lt;code&gt;never&lt;/code&gt; Type?
&lt;/h2&gt;

&lt;p&gt;In TypeScript, &lt;code&gt;never&lt;/code&gt; represents &lt;strong&gt;something that cannot happen&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Not “returns nothing”.&lt;br&gt;
Not “empty”.&lt;/p&gt;

&lt;p&gt;But this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A code path that is impossible to reach.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If a value has type &lt;code&gt;never&lt;/code&gt;, it means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;there is no possible runtime value&lt;/li&gt;
&lt;li&gt;if execution reaches here, something is wrong&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Simple Examples
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;crash&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="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Boom&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This function never finishes normally.&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;function&lt;/span&gt; &lt;span class="nf"&gt;infiniteLoop&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="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Execution can never continue past it.&lt;/p&gt;

&lt;p&gt;That’s why the return type is &lt;code&gt;never&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The One Rule That Makes &lt;code&gt;never&lt;/code&gt; Powerful
&lt;/h2&gt;

&lt;p&gt;This rule is the key:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;never&lt;/code&gt; is assignable to every type,&lt;br&gt;
but no type is assignable to &lt;code&gt;never&lt;/code&gt; (except &lt;code&gt;never&lt;/code&gt; itself).&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Example:&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;let&lt;/span&gt; &lt;span class="nx"&gt;x&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="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// ❌ error&lt;/span&gt;
&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// ❌ error&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If TypeScript ever sees a &lt;strong&gt;real value&lt;/strong&gt; being assigned to &lt;code&gt;never&lt;/code&gt;,&lt;br&gt;
it immediately fails the build.&lt;/p&gt;

&lt;p&gt;We’ll use this rule to stop production bugs.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Setup: A Common React Pattern
&lt;/h2&gt;

&lt;p&gt;Imagine a component that renders UI blocks based on a &lt;code&gt;type&lt;/code&gt; field&lt;br&gt;
(CMS blocks, feature flags, workflows — very common).&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;Block&lt;/span&gt; &lt;span class="o"&gt;=&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;hero&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;title&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="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;cta&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Renderer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;RenderBlock&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;block&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Block&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;block&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;hero&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;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;cta&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&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;Everything looks fine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TypeScript is happy&lt;/li&gt;
&lt;li&gt;CI passes&lt;/li&gt;
&lt;li&gt;No runtime error&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Production Bug (Silent)
&lt;/h2&gt;

&lt;p&gt;A teammate adds a new block:&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;Block&lt;/span&gt; &lt;span class="o"&gt;=&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;hero&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;title&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="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;cta&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="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;banner&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;image&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They forget to update &lt;code&gt;RenderBlock&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;What happens?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No TypeScript error&lt;/li&gt;
&lt;li&gt;No build failure&lt;/li&gt;
&lt;li&gt;No runtime crash&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The component returns &lt;code&gt;undefined&lt;/code&gt;.&lt;br&gt;
Nothing renders.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Production UI is broken silently.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the worst kind of bug.&lt;/p&gt;


&lt;h2&gt;
  
  
  Why TypeScript Didn’t Save You
&lt;/h2&gt;

&lt;p&gt;From TypeScript’s point of view:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the function might return &lt;code&gt;JSX.Element | undefined&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;that is technically valid&lt;/li&gt;
&lt;li&gt;TypeScript does not assume your &lt;code&gt;switch&lt;/code&gt; is exhaustive&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the bug passes.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Wrong “Fix”
&lt;/h2&gt;

&lt;p&gt;You might add a &lt;code&gt;default&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;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 makes things worse:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;still no compiler error&lt;/li&gt;
&lt;li&gt;still broken UI&lt;/li&gt;
&lt;li&gt;now even harder to notice&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We need TypeScript to &lt;strong&gt;fail loudly&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Introducing &lt;code&gt;never&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Now we change &lt;strong&gt;one line&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;RenderBlock&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;block&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Block&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;block&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;hero&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;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;cta&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;

    &lt;span class="nl"&gt;default&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;_exhaustiveCheck&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;*&lt;em&gt;The result: *&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F101s9robdza8xdpiy6rr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F101s9robdza8xdpiy6rr.png" alt=" " width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This line is the key:&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;const&lt;/span&gt; &lt;span class="nx"&gt;_exhaustiveCheck&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What Changed?
&lt;/h2&gt;

&lt;p&gt;This line tells TypeScript:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“If &lt;code&gt;block&lt;/code&gt; reaches here, this code is wrong.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When &lt;code&gt;banner&lt;/code&gt; exists, TypeScript now sees:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;block&lt;/code&gt; can be &lt;code&gt;{ type: "banner" }&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;banner&lt;/code&gt; is not assignable to &lt;code&gt;never&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Compile-time error.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;CI fails.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;The bug never ships.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real Win
&lt;/h2&gt;

&lt;p&gt;This is not about exhaustiveness checking.&lt;/p&gt;

&lt;p&gt;It’s about this guarantee:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;If the code compiles, all cases are handled.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s production safety.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Matters in Real Codebases
&lt;/h2&gt;

&lt;p&gt;This pattern shines when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CMS schemas evolve&lt;/li&gt;
&lt;li&gt;multiple teams touch the same union types&lt;/li&gt;
&lt;li&gt;UI rendering depends on configs or API responses&lt;/li&gt;
&lt;li&gt;reducers or workflows grow over time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Anywhere &lt;strong&gt;types change faster than implementations&lt;/strong&gt;, &lt;code&gt;never&lt;/code&gt; protects you.&lt;/p&gt;




&lt;h2&gt;
  
  
  Mental Model
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;never&lt;/code&gt; means:&lt;br&gt;
“This code path must not exist.”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If TypeScript ever proves that it &lt;em&gt;can&lt;/em&gt; exist,&lt;br&gt;
your build fails — by design.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;TypeScript does not guarantee exhaustive handling by default&lt;/li&gt;
&lt;li&gt;React components can silently break in production&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;never&lt;/code&gt; turns missing cases into compiler errors&lt;/li&gt;
&lt;li&gt;One line prevents an entire class of bugs
&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;const&lt;/span&gt; &lt;span class="nx"&gt;_exhaustiveCheck&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use it where silence is dangerous.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>typescript</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Stop trying `typeof x === Fish`: A practical guide to TypeScript type verification (Narrowing + Type Predicates)</title>
      <dc:creator>Kelvyn Thai</dc:creator>
      <pubDate>Mon, 05 Jan 2026 14:45:16 +0000</pubDate>
      <link>https://forem.com/kelvynthai/stop-trying-typeof-x-fish-a-practical-guide-to-typescript-type-verification-narrowing--4g9p</link>
      <guid>https://forem.com/kelvynthai/stop-trying-typeof-x-fish-a-practical-guide-to-typescript-type-verification-narrowing--4g9p</guid>
      <description>&lt;p&gt;When you come from a “typed mindset”, it’s natural to want something like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;typeof animal === Fish&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But JavaScript doesn’t work that way.&lt;/p&gt;

&lt;h2&gt;
  
  
  1) The key idea: types don’t exist at runtime
&lt;/h2&gt;

&lt;p&gt;TypeScript types are erased after compilation. At runtime, you only have JavaScript values.&lt;/p&gt;

&lt;p&gt;So runtime checks are always things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;typeof x === "string"&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;x instanceof Date&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;"swim" in animal&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;animal.kind === "fish"&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;TypeScript then uses those checks to &lt;strong&gt;narrow&lt;/strong&gt; union types.&lt;/p&gt;




&lt;h2&gt;
  
  
  2) Narrowing: TypeScript understands common JS patterns
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;typeof&lt;/code&gt; narrowing (primitives)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;padLeft&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&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;input&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;padding&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;input&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;padding&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;input&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;h3&gt;
  
  
  &lt;code&gt;instanceof&lt;/code&gt; narrowing (classes / constructors)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;logValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt; &lt;span class="o"&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nb"&gt;Date&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;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUTCString&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;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&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;h3&gt;
  
  
  &lt;code&gt;"in"&lt;/code&gt; narrowing (property existence)
&lt;/h3&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;Fish&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;swim&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;gt;&lt;/span&gt; &lt;span class="k"&gt;void&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;Bird&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;fly&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;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Fish&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;Bird&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;swim&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;swim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nx"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fly&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;⚠️ Note: &lt;code&gt;in&lt;/code&gt; works on the prototype chain and optional props affect both branches.&lt;/p&gt;




&lt;h2&gt;
  
  
  3) Type predicates: make narrowing reusable (the real win)
&lt;/h2&gt;

&lt;p&gt;Sometimes you want to reuse a check across multiple places (especially in &lt;code&gt;.filter()&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;That’s where &lt;strong&gt;type predicates&lt;/strong&gt; (user-defined type guards) shine:&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;Fish&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;swim&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;gt;&lt;/span&gt; &lt;span class="k"&gt;void&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;Bird&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;fly&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;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isFish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Fish&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;Bird&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;animal&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;Fish&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;swim&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Fish&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;Bird&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="nf"&gt;isFish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="nx"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;swim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nx"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fly&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 the same function can be reused:&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;const&lt;/span&gt; &lt;span class="nx"&gt;zoo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Fish&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;Bird&lt;/span&gt;&lt;span class="p"&gt;)[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="cm"&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;fishes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;zoo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isFish&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Fish[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&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;ButtonAsLink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;href&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="nl"&gt;onClick&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;ButtonAsAction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;onClick&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;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;href&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;Props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&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="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ButtonAsLink&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;ButtonAsAction&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isLinkProps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&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="nx"&gt;p&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;ButtonAsLink&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;href&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;SmartButton&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="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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isLinkProps&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="k"&gt;return&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="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&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="nx"&gt;href&lt;/span&gt;&lt;span class="p"&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="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/a&amp;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;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&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="nx"&gt;onClick&lt;/span&gt;&lt;span class="p"&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="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;






&lt;h2&gt;
  
  
  4) Best practice when you control the model: discriminated unions
&lt;/h2&gt;

&lt;p&gt;If you can change the data shape, this is the most robust approach:&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;Fish&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fish&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;swim&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;gt;&lt;/span&gt; &lt;span class="k"&gt;void&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;Bird&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bird&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;fly&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;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Fish&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;Bird&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;animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fish&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;swim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nx"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fly&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 is clearer than property checks and scales well as unions grow.&lt;/p&gt;




&lt;h2&gt;
  
  
  5) Common pitfalls (learn these once)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;typeof null === "object"&lt;/code&gt; (historic JS quirk)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;!value&lt;/code&gt; checks &lt;em&gt;falsy&lt;/em&gt; (0, "", false) — not just null/undefined&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"prop" in obj&lt;/code&gt; can be true because of prototypes&lt;/li&gt;
&lt;li&gt;Optional properties can cause both branches to still include a type (e.g., Human can &lt;code&gt;swim?()&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Runtime verification&lt;/strong&gt; comes from JavaScript checks.&lt;br&gt;
&lt;strong&gt;Compile-time safety&lt;/strong&gt; comes from TypeScript narrowing.&lt;br&gt;
When you want reuse, wrap it in a &lt;strong&gt;type predicate&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you remember one line:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Use JS checks to narrow, and type predicates to reuse the narrowing.”&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>typescript</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>GitLab CI/CD for Next.js — Part 2: Build Job Setup &amp; Docker Image Caching</title>
      <dc:creator>Kelvyn Thai</dc:creator>
      <pubDate>Mon, 29 Dec 2025 04:31:38 +0000</pubDate>
      <link>https://forem.com/kelvynthai/gitlab-cicd-for-nextjs-part-2-setup-build-job-3cc3</link>
      <guid>https://forem.com/kelvynthai/gitlab-cicd-for-nextjs-part-2-setup-build-job-3cc3</guid>
      <description>&lt;p&gt;Hi everyone 👋&lt;/p&gt;

&lt;p&gt;I’m &lt;strong&gt;Kelvyn&lt;/strong&gt;, a Frontend Engineer with &lt;strong&gt;8 years of experience&lt;/strong&gt;, mainly working with &lt;strong&gt;React and Next.js&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this series, I’ll walk you through how to set up a &lt;strong&gt;comprehensive, production-grade GitLab CI/CD pipeline for a Next.js application&lt;/strong&gt;, based on real-world experience running CI/CD at scale.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Part 2&lt;/strong&gt;, we’ll focus on building a &lt;strong&gt;fast and reliable build pipeline&lt;/strong&gt;, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setting up Docker jobs with &lt;code&gt;docker-cli&lt;/code&gt; and &lt;code&gt;docker-dind&lt;/code&gt; (Docker BuildKit)&lt;/li&gt;
&lt;li&gt;Authenticating with &lt;strong&gt;GitLab Dependency Proxy&lt;/strong&gt; and &lt;strong&gt;GitLab Container Registry&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Building images with &lt;code&gt;docker buildx&lt;/code&gt;, tagging, and caching strategies&lt;/li&gt;
&lt;li&gt;Separating environments (production, staging, development)&lt;/li&gt;
&lt;li&gt;Running GitLab CI/CD in a cost-efficient way&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you haven’t read it yet, you may want to start with:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://dev.to/kelvynthai/gitlab-cicd-for-nextjs-part-0-project-repository-setup-54mp"&gt;GitLab CI/CD for Next.js — Part 0: Project &amp;amp; Repository Setup&lt;/a&gt;&lt;br&gt;
👉 &lt;a href="https://dev.to/kelvynthai/gitlab-cicd-for-nextjs-part-1-validate-job-lint-check-types-5dp8"&gt;GitLab CI/CD for Next.js — Part 1: Validate Job Lint &amp;amp; Check Types&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  I. Set up Docker jobs with docker-cli and docker-dind
&lt;/h2&gt;

&lt;p&gt;In this section, we set up a reusable &lt;strong&gt;Docker job template&lt;/strong&gt; using &lt;code&gt;docker-cli&lt;/code&gt; and &lt;code&gt;docker-dind&lt;/code&gt;.&lt;br&gt;
This template will be reused later to &lt;strong&gt;build and push Docker images&lt;/strong&gt; using &lt;strong&gt;Docker BuildKit&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Files involved
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;templates/docker-job.yml&lt;/code&gt;&lt;/strong&gt;
Defines a shared Docker job template used by build jobs.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.docker.com/products/cli/" rel="noopener noreferrer"&gt;Docker CLI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.docker.com/resources/docker-in-docker-containerized-ci-workflows-dockercon-2023/" rel="noopener noreferrer"&gt;Docker DIND&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.gitlab.com/ci/docker/using_buildkit/#build-basic-images" rel="noopener noreferrer"&gt;GitLab CI/CD Docker BuildKit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  &lt;code&gt;templates/docker-job.yml&lt;/code&gt;
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;.docker-job&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/docker:24.0.5-cli&lt;/span&gt;
  &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/docker:24.0.5-dind&lt;/span&gt;
      &lt;span class="na"&gt;alias&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker&lt;/span&gt;
  &lt;span class="na"&gt;before_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "Authenticate with Dependency Proxy"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "$CI_DEPENDENCY_PROXY_PASSWORD" | docker login \&lt;/span&gt;
        &lt;span class="s"&gt;$CI_DEPENDENCY_PROXY_SERVER \&lt;/span&gt;
        &lt;span class="s"&gt;-u $CI_DEPENDENCY_PROXY_USER \&lt;/span&gt;
        &lt;span class="s"&gt;--password-stdin&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "Authenticate with GitLab Container Registry"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Breakdown
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;code&gt;CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://docs.gitlab.com/user/packages/dependency_proxy/" rel="noopener noreferrer"&gt;https://docs.gitlab.com/user/packages/dependency_proxy/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Allows GitLab CI to pull base images and cached layers via &lt;strong&gt;GitLab Dependency Proxy&lt;/strong&gt;, instead of directly from Docker Hub.&lt;/p&gt;

&lt;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Avoids Docker Hub rate limits&lt;/li&gt;
&lt;li&gt;Improves image pull speed&lt;/li&gt;
&lt;li&gt;Increases pipeline reliability&lt;/li&gt;
&lt;/ul&gt;


&lt;h4&gt;
  
  
  &lt;code&gt;CI_REGISTRY&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://docs.gitlab.com/ci/docker/using_buildkit/#authenticate-with-the-gitlab-container-registry" rel="noopener noreferrer"&gt;https://docs.gitlab.com/ci/docker/using_buildkit/#authenticate-with-the-gitlab-container-registry&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Used to &lt;strong&gt;push and pull&lt;/strong&gt; Docker images from the &lt;strong&gt;GitLab Container Registry&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This enables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reusing images across jobs (build → test → deploy)&lt;/li&gt;
&lt;li&gt;Treating Docker images as deployable artifacts&lt;/li&gt;
&lt;li&gt;Immutable image tagging per environment for better caching&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  II. Set up build job with Docker buildx
&lt;/h2&gt;

&lt;p&gt;In this section, we set up a build job using &lt;strong&gt;Docker Buildx&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Files involved
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;templates/build-job.yml&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Defines a build job template.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Dockerfile&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Defines a Dockerfile with &lt;strong&gt;multi-layer caching&lt;/strong&gt;, encapsulating our application.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.gitlab.com/runner/executors/docker/" rel="noopener noreferrer"&gt;GitLab Docker Executor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/reference/cli/docker/buildx/" rel="noopener noreferrer"&gt;Docker Buildx&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.gitlab.com/ci/docker/using_buildkit/#docker-buildx" rel="noopener noreferrer"&gt;GitLab CI/CD Docker Buildx&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  &lt;code&gt;templates/build-job.yml&lt;/code&gt;
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
include:
  - local: "templates/docker-job.yml"
.build_job:
  stage: build
  variables:
    IMAGE_TAG: $CI_REGISTRY_IMAGE:tag-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA
    DOCKER_TLS_CERTDIR: "/certs"
  extends:
    - .docker-job
  script:
    - |
      echo "Environment: $ENV"
      echo "Image tag: $IMAGE_TAG"
      echo "CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}"

    - docker context create buildx-context
    - docker buildx create buildx-context --name builder --bootstrap --driver \
        docker-container --platform linux/amd64 --use
    - docker buildx inspect --bootstrap
    - |
      docker buildx build \
        --platform linux/amd64 \
        --build-arg BUILD_ENV=$ENV \
        --build-arg CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX=$CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX \
        --file Dockerfile \
        --tag $IMAGE_TAG \
        --push .
  after_script:
    - docker buildx rm builder
    - docker context rm buildx-context
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Breakdown
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Docker BuildKit
&lt;/h4&gt;

&lt;p&gt;BuildKit is an improved backend that replaces the legacy Docker builder. It is the default builder for users on Docker Desktop and Docker Engine as of version 23.0.&lt;/p&gt;

&lt;p&gt;Docker Buildx extends the Docker CLI to leverage the BuildKit engine, providing enhanced features such as better caching, multi-platform build execution, and builder instance control.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you want to compare building &lt;strong&gt;with and without BuildKit&lt;/strong&gt;, you can run the following command:&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DOCKER_BUILDKIT=0 docker build -t nextjs-cicd-template-image .
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  IMAGE_TAG
&lt;/h4&gt;

&lt;p&gt;Defines the image tag artifact for our application, allowing it to be reused in other jobs (E2E tests, deployment, etc.).&lt;/p&gt;

&lt;p&gt;The format will look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;registry.gitlab.com/your-group/nextjs-cicd-template:tag-v1-0-33-d34e872b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  III. Set up GitLab CI job
&lt;/h2&gt;

&lt;p&gt;In this section, we set up a &lt;strong&gt;simple build job&lt;/strong&gt; to quickly test the pipeline.&lt;/p&gt;

&lt;h3&gt;
  
  
  Files involved
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.gitlab-ci.yml&lt;/code&gt;&lt;/strong&gt;
Defines the GitLab CI configuration for our repository.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;include:
  # ... existing config
  - local: "templates/build-job.yml" # add build job template

# Build Dev
build_dev:
  extends:
    - .build_job
    - .rules_dev
  variables:
    ENV: "dev"

# Build Staging
build_staging:
  extends:
    - .build_job
    - .rules_staging
  variables:
    ENV: "staging"

# Build Production
build_prod:
  extends:
    - .build_job
    - .rules_prod
  variables:
    ENV: "prod"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great! Let’s commit the code and run our build job.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fn999b93tedqwq6wx220y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fn999b93tedqwq6wx220y.png" alt=" " width="800" height="346"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a result, the job completed in ~&lt;strong&gt;1.47s&lt;/strong&gt;, and the script execution time was ~&lt;strong&gt;1.19s&lt;/strong&gt;. &lt;strong&gt;Fantastic!&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In practice, we usually focus on the &lt;strong&gt;script execution time&lt;/strong&gt;, because the total job duration can include runner scheduling and time spent waiting for cloud resources to be allocated.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  IV. Set up Docker Image Caching
&lt;/h2&gt;

&lt;p&gt;In this section, we’ll take advantage of &lt;strong&gt;Docker Buildx caching&lt;/strong&gt; to speed up subsequent builds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Files involved
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;templates/build-job.yml&lt;/code&gt;&lt;/strong&gt;
Defines a build job template.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.gitlab.com/ci/docker/using_buildkit/#use-build-caching" rel="noopener noreferrer"&gt;GitLab Docker Buildx Caching&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s modify our build job template and &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; to enable cache.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;.gitlab-ci.yml&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Define a cache image per environment (dev, staging, production):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Build Dev&lt;/span&gt;
&lt;span class="na"&gt;build_dev&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dev"&lt;/span&gt;
    &lt;span class="na"&gt;CACHE_IMAGE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$CI_REGISTRY_IMAGE:cache-dev&lt;/span&gt; &lt;span class="c1"&gt;# cache image tag for dev&lt;/span&gt;

&lt;span class="c1"&gt;# Build Staging&lt;/span&gt;
&lt;span class="na"&gt;build_staging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;staging"&lt;/span&gt;
    &lt;span class="na"&gt;CACHE_IMAGE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$CI_REGISTRY_IMAGE:cache-staging&lt;/span&gt; &lt;span class="c1"&gt;# cache image tag for staging&lt;/span&gt;
  &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;test_staging&lt;/span&gt;

&lt;span class="c1"&gt;# Build Production&lt;/span&gt;
&lt;span class="na"&gt;build_prod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prod"&lt;/span&gt;
    &lt;span class="na"&gt;CACHE_IMAGE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$CI_REGISTRY_IMAGE:cache-prod&lt;/span&gt; &lt;span class="c1"&gt;# cache image tag for production&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;templates/build-job.yml&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;local&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;templates/docker-job.yml"&lt;/span&gt;

&lt;span class="na"&gt;.build_job&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;echo "Environment: $ENV"&lt;/span&gt;
      &lt;span class="s"&gt;echo "Image tag: $IMAGE_TAG"&lt;/span&gt;
      &lt;span class="s"&gt;echo "Cache image: $CACHE_IMAGE"&lt;/span&gt;
      &lt;span class="s"&gt;echo "CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}"&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker context create buildx-context&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker buildx create buildx-context --name builder --bootstrap --driver \&lt;/span&gt;
        &lt;span class="s"&gt;docker-container --platform linux/amd64 --use&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker buildx inspect --bootstrap&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;# Use BuildKit registry cache (pull + push)&lt;/span&gt;
      &lt;span class="s"&gt;docker buildx build \&lt;/span&gt;
        &lt;span class="s"&gt;--platform linux/amd64 \&lt;/span&gt;
        &lt;span class="s"&gt;--build-arg BUILD_ENV=$ENV \&lt;/span&gt;
        &lt;span class="s"&gt;--build-arg CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX=$CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX \&lt;/span&gt;
        &lt;span class="s"&gt;--file Dockerfile \&lt;/span&gt;
        &lt;span class="s"&gt;--tag $IMAGE_TAG \&lt;/span&gt;
        &lt;span class="s"&gt;--cache-from type=registry,ref=$CACHE_IMAGE \&lt;/span&gt;
        &lt;span class="s"&gt;--cache-to type=registry,ref=$CACHE_IMAGE,mode=max \&lt;/span&gt;
        &lt;span class="s"&gt;--push .&lt;/span&gt;

  &lt;span class="na"&gt;after_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker buildx rm builder&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker context rm buildx-context&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Breakdown
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;CACHE_IMAGE&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;We create &lt;strong&gt;three separate cache tags&lt;/strong&gt;, one per environment (dev, staging, prod).&lt;br&gt;
This avoids cache pollution and prevents cross-environment interference (e.g., different dependencies or build settings). Each environment owns its own cache image.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;--cache-from&lt;/code&gt; and &lt;code&gt;--cache-to&lt;/code&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--cache-from type=registry,ref=$CACHE_IMAGE&lt;/code&gt; pulls cached layers from the registry.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--cache-to type=registry,ref=$CACHE_IMAGE,mode=max&lt;/code&gt; pushes the updated cache back to the registry to speed up future builds.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s wrap up and commit our code to see what happens!&lt;/p&gt;

&lt;p&gt;On the first run, the build will take longer because BuildKit needs to &lt;strong&gt;generate the cache&lt;/strong&gt; and &lt;strong&gt;push it to the registry&lt;/strong&gt;. You may also see a warning like:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ERROR: failed to configure registry cache importer: registry.gitlab.com/your-group/nextjs-cicd-template:cache-dev: not found&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That’s expected, because the cache image doesn’t exist yet.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can navigate to &lt;strong&gt;Deploy → Container Registry → nextjs-cicd-template&lt;/strong&gt; to check the cache image size.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F8ag9blqqwe3hm7uokwi4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F8ag9blqqwe3hm7uokwi4.png" alt=" " width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let’s run the pipeline again!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F6l1d68dricoxkb6id8h9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F6l1d68dricoxkb6id8h9.png" alt=" " width="800" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a result, thanks to caching, the script execution time dropped to ~&lt;strong&gt;1m&lt;/strong&gt; (about &lt;strong&gt;15–20% faster&lt;/strong&gt;), and the total job time was ~&lt;strong&gt;1.27m&lt;/strong&gt;. Great!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In reality, smaller images not only benefit the build job, but also deployment and E2E testing of your application. Smaller images mean faster pull times.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Summary (Part 2 Recap)
&lt;/h3&gt;

&lt;p&gt;In &lt;strong&gt;Part 2&lt;/strong&gt; of this series, we built a &lt;strong&gt;production-grade Docker build pipeline&lt;/strong&gt; for a Next.js application using &lt;strong&gt;GitLab CI/CD&lt;/strong&gt; and &lt;strong&gt;Docker Buildx&lt;/strong&gt;, with a strong focus on &lt;strong&gt;performance, reliability, and scalability&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We covered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reusable Docker job templates&lt;/strong&gt; using &lt;code&gt;docker-cli&lt;/code&gt; and &lt;code&gt;docker-dind&lt;/code&gt;, enabling Docker BuildKit in GitLab CI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secure authentication&lt;/strong&gt; with GitLab Dependency Proxy and GitLab Container Registry to improve reliability and avoid Docker Hub rate limits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker Buildx-based build jobs&lt;/strong&gt;, allowing better caching, multi-platform support, and controlled builder instances.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment-based builds&lt;/strong&gt; (dev, staging, production) with isolated configurations and immutable image tagging.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Registry-based BuildKit caching&lt;/strong&gt;, using &lt;code&gt;--cache-from&lt;/code&gt; and &lt;code&gt;--cache-to&lt;/code&gt;, significantly reducing build times after the first run.&lt;/li&gt;
&lt;li&gt;Practical insights into &lt;strong&gt;CI performance metrics&lt;/strong&gt;, highlighting why script execution time matters more than total job duration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By introducing &lt;strong&gt;environment-specific cache images&lt;/strong&gt;, we avoided cache pollution while achieving &lt;strong&gt;15–20% faster builds&lt;/strong&gt; on subsequent runs.&lt;br&gt;
Beyond CI speed, smaller and well-cached images also improve &lt;strong&gt;deployment speed&lt;/strong&gt; and &lt;strong&gt;E2E testing performance&lt;/strong&gt; due to faster image pulls.&lt;/p&gt;

&lt;p&gt;In the next part, we’ll continue building on this foundation to further optimize and productionize the pipeline.&lt;/p&gt;

&lt;p&gt;Full source code: &lt;br&gt;
&lt;a href="https://gitlab.com/kelvyn-labs/nextjs-cicd-template" rel="noopener noreferrer"&gt;https://gitlab.com/kelvyn-labs/nextjs-cicd-template&lt;/a&gt;&lt;/p&gt;

</description>
      <category>gitlab</category>
      <category>cicd</category>
      <category>docker</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>GitLab CI/CD for Next.js — Part 1: Validate Job Lint &amp; Check Types</title>
      <dc:creator>Kelvyn Thai</dc:creator>
      <pubDate>Mon, 22 Dec 2025 03:16:10 +0000</pubDate>
      <link>https://forem.com/kelvynthai/gitlab-cicd-for-nextjs-part-1-validate-job-lint-check-types-5dp8</link>
      <guid>https://forem.com/kelvynthai/gitlab-cicd-for-nextjs-part-1-validate-job-lint-check-types-5dp8</guid>
      <description>&lt;p&gt;Hi everyone 👋&lt;/p&gt;

&lt;p&gt;I’m &lt;strong&gt;Kelvyn&lt;/strong&gt;, a Frontend Engineer with &lt;strong&gt;8 years of experience&lt;/strong&gt;, mainly working with &lt;strong&gt;React and Next.js&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this series, I’ll walk you through how to set up a &lt;strong&gt;comprehensive, production-grade GitLab CI/CD pipeline for a Next.js application&lt;/strong&gt;, based on real-world experience running CI/CD at scale.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Part 1&lt;/strong&gt;, we’ll focus on building a &lt;strong&gt;fast and safe validation pipeline&lt;/strong&gt;, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Linting&lt;/li&gt;
&lt;li&gt;Type checking&lt;/li&gt;
&lt;li&gt;Environment-aware caching with &lt;strong&gt;PNPM&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Cost-efficient GitLab CI/CD execution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you haven’t read it yet, you may want to start with:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://dev.to/kelvynthai/gitlab-cicd-for-nextjs-part-0-project-repository-setup-54mp"&gt;GitLab CI/CD for Next.js — Part 0: Project &amp;amp; Repository Setup&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  I. Setting up validation jobs (linting, type checking)
&lt;/h2&gt;

&lt;p&gt;We’ll start by preparing the following files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;.gitlab.yml&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Defines the CI pipeline and validation jobs at the project root.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;templates/node-job.yml&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Prepares the Node.js Alpine image and sets up &lt;strong&gt;PNPM store caching&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.gitlab.com/ci/yaml/yaml_optimization/#use-extends-to-reuse-configuration-sections" rel="noopener noreferrer"&gt;GitLab CI/CD &lt;code&gt;extends&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.gitlab.com/ci/caching/" rel="noopener noreferrer"&gt;GitLab CI/CD cache&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;.gitlab.yml&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;stages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;validate&lt;/span&gt;

&lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;local&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;templates/node-job.yml"&lt;/span&gt;

&lt;span class="na"&gt;validate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;validate&lt;/span&gt;
  &lt;span class="na"&gt;extends&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.node-job&lt;/span&gt; &lt;span class="c1"&gt;# inherit from node-job template&lt;/span&gt;
  &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt;          &lt;span class="c1"&gt;# trigger this job immediately&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pnpm install --frozen-lockfile --ignore-scripts --prefer-offline&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pnpm lint&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pnpm check-types&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;At this point, we have a &lt;strong&gt;basic validation pipeline&lt;/strong&gt; that ensures code quality before moving forward.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;templates/node-job.yml&lt;/code&gt;
&lt;/h3&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://pnpm.io/continuous-integration#gitlab-ci" rel="noopener noreferrer"&gt;Setup PNPM on GitLab CI/CD&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.gitlab.com/ci/variables/predefined_variables/" rel="noopener noreferrer"&gt;GitLab predefined variables&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;.node-job&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/node:22.20.0-alpine3.22&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;PNPM_STORE_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$CI_PROJECT_DIR/.pnpm-store&lt;/span&gt;
  &lt;span class="na"&gt;before_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;corepack enable &amp;amp;&amp;amp; corepack prepare pnpm@10 --activate&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pnpm --version&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pnpm config set store-dir $PNPM_STORE_KEY&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;export PNPM_HOME="$CI_PROJECT_DIR/.pnpm"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;export PATH="$PNPM_HOME:$PATH"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great! Our validation job is now set up successfully.&lt;br&gt;
Let’s commit the code and run the first pipeline.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Frj06j1cc6bixhve9kl7z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Frj06j1cc6bixhve9kl7z.png" alt=" " width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The pipeline finishes in &lt;strong&gt;36 seconds&lt;/strong&gt;, with dependency installation taking roughly &lt;strong&gt;~8.1 seconds&lt;/strong&gt; 👍&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, we’ll improve this further by introducing &lt;strong&gt;GitLab CI/CD caching&lt;/strong&gt; together with &lt;strong&gt;environment-based variables&lt;/strong&gt; to avoid cache conflicts or accidental mutations between development, staging, and production.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In real projects, &lt;strong&gt;CI/CD cost and execution time matter&lt;/strong&gt;.&lt;br&gt;
Running validation on every commit quickly becomes noisy, slow, and expensive.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  NEW: Create &lt;code&gt;templates/rules.yml&lt;/code&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Reference
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://semver.org" rel="noopener noreferrer"&gt;Semantic Versioning&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;.rules_dev&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$CI_COMMIT_REF_SLUG == "develop"&lt;/span&gt;

&lt;span class="na"&gt;.rules_staging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$CI_COMMIT_TAG =~ /^staging-\d+\.\d+\.\d+$/&lt;/span&gt;

&lt;span class="na"&gt;.rules_prod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/&lt;/span&gt;

&lt;span class="na"&gt;.rules_protected&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$CI_COMMIT_REF_SLUG&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;==&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"develop"'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$CI_COMMIT_TAG&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;=~&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;/^staging-\d+\.\d+\.\d+$/'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$CI_COMMIT_TAG&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;=~&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;/^v\d+\.\d+\.\d+$/'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We define a few rules to control &lt;strong&gt;when CI/CD jobs should run&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The validation jobs will only be triggered when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code is &lt;strong&gt;merged&lt;/strong&gt; into the &lt;code&gt;develop&lt;/code&gt; branch&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;staging tag&lt;/strong&gt; is created (e.g. &lt;code&gt;staging-1.0.0&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;production tag&lt;/strong&gt; is created (e.g. &lt;code&gt;v1.0.0&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In all other cases, the pipeline won’t run — helping us &lt;strong&gt;save CI minutes and reduce unnecessary cost&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Update: &lt;code&gt;.gitlab.yml&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;local&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;templates/node-job.yml"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;local&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;templates/rules.yml"&lt;/span&gt; &lt;span class="c1"&gt;# include rules template&lt;/span&gt;

&lt;span class="c1"&gt;# Convert validate job into a reusable template&lt;/span&gt;
&lt;span class="na"&gt;.validate_job&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;validate&lt;/span&gt;
  &lt;span class="c1"&gt;# old config...&lt;/span&gt;
  &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pull-push&lt;/span&gt; &lt;span class="c1"&gt;# allow validate jobs to revalidate cache when needed&lt;/span&gt;

&lt;span class="c1"&gt;# Validate Dev&lt;/span&gt;
&lt;span class="na"&gt;validate_dev&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;extends&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.validate_job&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.rules_dev&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dev"&lt;/span&gt;

&lt;span class="c1"&gt;# Validate Staging&lt;/span&gt;
&lt;span class="na"&gt;validate_staging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;extends&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.validate_job&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.rules_staging&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;staging"&lt;/span&gt;

&lt;span class="c1"&gt;# Validate Prod&lt;/span&gt;
&lt;span class="na"&gt;validate_prod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;extends&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.validate_job&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.rules_prod&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prod"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now define &lt;strong&gt;separate validation jobs&lt;/strong&gt; for each environment:&lt;/p&gt;

&lt;p&gt;Each job:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Runs only when the correct branch or tag is used&lt;/li&gt;
&lt;li&gt;Uses environment-specific variables for isolation&lt;/li&gt;
&lt;li&gt;Prevents &lt;strong&gt;PNPM cache pollution&lt;/strong&gt; across environments&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Update: &lt;code&gt;templates/node-job.yml&lt;/code&gt; (cache configuration)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;.node-job&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/node:22.20.0-alpine3.22&lt;/span&gt;
  &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pnpm-lock.yaml&lt;/span&gt;
      &lt;span class="na"&gt;prefix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$ENV&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;$PNPM_STORE_KEY&lt;/span&gt;
    &lt;span class="na"&gt;policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pull&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Breakdown
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PNPM store caching&lt;/strong&gt;
We cache the &lt;strong&gt;PNPM store&lt;/strong&gt;, which contains all downloaded dependencies.
We &lt;strong&gt;do not cache &lt;code&gt;node_modules&lt;/code&gt;&lt;/strong&gt; because compressing and transferring it is expensive and often slower.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to understand &lt;em&gt;why PNPM works so well&lt;/em&gt;, check out:&lt;br&gt;
  👉 &lt;a href="https://dev.to/kelvynthai/pnpm-content-addressable-store-looks-broken-on-macos-30eh"&gt;PNPM Content-Addressable Store Looks Broken on macOS&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Default cache policy: &lt;code&gt;pull&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Jobs extending &lt;code&gt;node-job&lt;/code&gt; can &lt;strong&gt;read from the cache only&lt;/strong&gt;, preventing accidental cache mutation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cache prefix: &lt;code&gt;$ENV&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Scopes caches by environment (dev / staging / prod) to avoid conflicts and concurrency issues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cache files: &lt;code&gt;pnpm-lock.yaml&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Cache is revalidated &lt;strong&gt;only when dependencies change&lt;/strong&gt;, keeping it stable and predictable.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Allows GitLab to pull images and cached layers &lt;strong&gt;directly from the GitLab registry&lt;/strong&gt;, instead of hitting Docker Hub or AWS ECR.&lt;br&gt;
&lt;a href="https://docs.gitlab.com/user/packages/dependency_proxy/" rel="noopener noreferrer"&gt;https://docs.gitlab.com/user/packages/dependency_proxy/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Let’s commit the code and review the cache behavior.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F7wwtomke2jq4x4b2xek3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F7wwtomke2jq4x4b2xek3.png" alt=" " width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The pipeline now finishes slightly slower on the first run (&lt;strong&gt;~41s&lt;/strong&gt;) because the cache needs to be downloaded, extracted, and pushed.&lt;/p&gt;

&lt;p&gt;However, pay attention to the dependency installation time: &lt;strong&gt;~2.1 seconds&lt;/strong&gt; 🚀&lt;br&gt;
That’s &lt;strong&gt;more than 4× faster&lt;/strong&gt; compared to no caching.&lt;/p&gt;

&lt;p&gt;As the project grows and &lt;code&gt;node_modules&lt;/code&gt; becomes larger, this approach can &lt;strong&gt;save significant CI time and cost&lt;/strong&gt;, especially when reused across unit tests, E2E tests, and future pipeline stages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In &lt;strong&gt;Part 1&lt;/strong&gt;, we focused on building a &lt;strong&gt;fast, safe, and cost-efficient validation pipeline&lt;/strong&gt; for a Next.js application using GitLab CI/CD.&lt;/p&gt;

&lt;p&gt;Here’s what we achieved:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Set up &lt;strong&gt;linting and type checking&lt;/strong&gt; as early validation gates&lt;/li&gt;
&lt;li&gt;✅ Introduced &lt;strong&gt;reusable CI templates&lt;/strong&gt; using &lt;code&gt;extends&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;✅ Applied &lt;strong&gt;rule-based execution&lt;/strong&gt; to run pipelines only on meaningful events (develop, staging, production)&lt;/li&gt;
&lt;li&gt;✅ Implemented &lt;strong&gt;environment-aware PNPM store caching&lt;/strong&gt; to avoid cache pollution&lt;/li&gt;
&lt;li&gt;✅ Leveraged &lt;strong&gt;GitLab Dependency Proxy&lt;/strong&gt; to reduce external registry traffic&lt;/li&gt;
&lt;li&gt;✅ Reduced dependency installation time from &lt;strong&gt;~8s → ~2s&lt;/strong&gt;, saving CI time and cost&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This foundation ensures that only &lt;strong&gt;high-quality, type-safe code&lt;/strong&gt; moves forward — without wasting CI minutes on every commit.&lt;/p&gt;




&lt;h2&gt;
  
  
  What’s Next — Part 1.1: Unit Testing at Scale 🚀
&lt;/h2&gt;

&lt;p&gt;In &lt;strong&gt;Part 1.1&lt;/strong&gt;, we’ll level up the pipeline by introducing &lt;strong&gt;fast and scalable unit testing&lt;/strong&gt; using &lt;strong&gt;Vitest&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We’ll cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setting up &lt;strong&gt;Vitest&lt;/strong&gt; in GitLab CI/CD&lt;/li&gt;
&lt;li&gt;Running &lt;strong&gt;unit tests in parallel&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test sharding&lt;/strong&gt; to split large test suites across multiple jobs&lt;/li&gt;
&lt;li&gt;Reusing the &lt;strong&gt;PNPM store cache&lt;/strong&gt; to keep test jobs blazing fast&lt;/li&gt;
&lt;li&gt;Reducing total pipeline time as the codebase grows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the end of Part 1.1, you’ll have a &lt;strong&gt;production-ready testing pipeline&lt;/strong&gt; that scales smoothly with your application — without sacrificing speed or cost.&lt;/p&gt;

&lt;p&gt;👉 Stay tuned for &lt;strong&gt;GitLab CI/CD for Next.js — Part 1.1: Unit Testing with Vitest&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;*Full source code: *&lt;br&gt;
&lt;a href="https://gitlab.com/kelvyn-labs/nextjs-cicd-template" rel="noopener noreferrer"&gt;https://gitlab.com/kelvyn-labs/nextjs-cicd-template&lt;/a&gt;&lt;/p&gt;

</description>
      <category>gitlab</category>
      <category>cicd</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>GitLab CI/CD for Next.js — Part 0: Project &amp; Repository Setup</title>
      <dc:creator>Kelvyn Thai</dc:creator>
      <pubDate>Fri, 19 Dec 2025 07:31:50 +0000</pubDate>
      <link>https://forem.com/kelvynthai/gitlab-cicd-for-nextjs-part-0-project-repository-setup-54mp</link>
      <guid>https://forem.com/kelvynthai/gitlab-cicd-for-nextjs-part-0-project-repository-setup-54mp</guid>
      <description>&lt;p&gt;Hi everyone 👋&lt;br&gt;
I’m Kelvyn, a Frontend Engineer with 8 years of experience, mainly working with &lt;strong&gt;React&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this series, I’ll walk you through how to set up a &lt;strong&gt;comprehensive GitLab CI/CD pipeline for a Next.js application&lt;/strong&gt;, based on real-world production experience.&lt;/p&gt;
&lt;h3&gt;
  
  
  Who is this article for?
&lt;/h3&gt;

&lt;p&gt;This article is best suited for engineers with &lt;strong&gt;5+ years of experience&lt;/strong&gt; who want to &lt;strong&gt;deep dive into how a modern application is built, tested, and deployed&lt;/strong&gt; in a structured and scalable way.&lt;/p&gt;

&lt;p&gt;It will be much easier to follow if you already have some basic knowledge of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://docs.gitlab.com/ci/" rel="noopener noreferrer"&gt;GitLab CI/CD&lt;/a&gt;&lt;/strong&gt;: GitLab Runner, executors (Shell, Docker), cache, artifacts, container/package registry, templates, components&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://docs.docker.com?_gl=1*y3sj81*_gcl_au*MTgwNTE5MDUzNy4xNzYzMDQ1NTAw*_ga*MTQ4MzU0Mjg2Mi4xNzYzMDQ1NTAw*_ga_XJWPQMJYHQ*czE3NjU5Mzk4MTAkbzgkZzEkdDE3NjU5NDE1MDUkajU5JGwwJGgw" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;&lt;/strong&gt;: Docker daemon, Docker Compose, Docker Buildx&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://training.linuxfoundation.org/training/introduction-to-linux/" rel="noopener noreferrer"&gt;Linux&lt;/a&gt;&lt;/strong&gt;: Ubuntu, AMD64 architecture, CLI, Shell, Vim, SSH&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://docs.ansible.com/projects/ansible/latest/reference_appendices/YAMLSyntax.html" rel="noopener noreferrer"&gt;YAML&lt;/a&gt;&lt;/strong&gt;: &lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  What we’ll do in Part 0
&lt;/h3&gt;

&lt;p&gt;In this first part, we’ll focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Initialize repository &lt;/li&gt;
&lt;li&gt;Initialize our Next.js application&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;I. Initialize repository&lt;/strong&gt;&lt;br&gt;
To get started, create a &lt;strong&gt;new GitLab group&lt;/strong&gt;:&lt;br&gt;
&lt;a href="https://gitlab.com/groups/new#create-group-pane" rel="noopener noreferrer"&gt;https://gitlab.com/groups/new#create-group-pane&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fjptrd10iy84kjjq1y8ng.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fjptrd10iy84kjjq1y8ng.png" alt=" " width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, create a &lt;strong&gt;blank project&lt;/strong&gt; named &lt;strong&gt;nextjs-cicd-template&lt;/strong&gt; inside the group.&lt;/p&gt;

&lt;p&gt;From the default main branch, create the following branches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;master&lt;/li&gt;
&lt;li&gt;staging&lt;/li&gt;
&lt;li&gt;develop&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F13oz2c045tpkuje3j7ys.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F13oz2c045tpkuje3j7ys.png" alt=" " width="800" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;II. Initialize the Next.js application&lt;/strong&gt;**&lt;/p&gt;

&lt;p&gt;Initialize a fresh Next.js app inside the project directory:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cd nextjs-cicd-template&lt;br&gt;
pnpm create next-app@latest ./ --yes&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Next, add a TypeScript type-checking script to your package.json:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;"check-types": "tsc --noEmit"&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
Finally, verify that linting and type checking work as expected:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pnpm lint &amp;amp;&amp;amp; pnpm check-types&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
If both commands pass, we’re ready to move on and integrate these checks into our GitLab CI pipeline.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fasjpluzohqkcblj11fhc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fasjpluzohqkcblj11fhc.png" alt=" " width="800" height="181"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Remember generate pnpm-lockfile.yaml in order to use at step setup job on Gitlab CI/CD 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What’s next?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In Part 1, we’ll set up:&lt;br&gt;
Linting &amp;amp; type-checking jobs&lt;br&gt;
PNPM store caching&lt;br&gt;
GitLab Dependency Proxy&lt;br&gt;
Secure and repeatable validation pipelines&lt;br&gt;
&lt;a href="https://dev.to/kelvynthai/gitlab-cicd-for-nextjs-part-1-validate-job-lint-check-types-5dp8"&gt;https://dev.to/kelvynthai/gitlab-cicd-for-nextjs-part-1-validate-job-lint-check-types-5dp8&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;*&lt;em&gt;Full source code: *&lt;/em&gt;&lt;br&gt;
&lt;a href="https://gitlab.com/kelvyn-labs/nextjs-cicd-template" rel="noopener noreferrer"&gt;https://gitlab.com/kelvyn-labs/nextjs-cicd-template&lt;/a&gt;&lt;/p&gt;

</description>
      <category>gitlab</category>
      <category>cicd</category>
      <category>nextjs</category>
      <category>docker</category>
    </item>
    <item>
      <title>Which DevOps certs are necessary for a Frontend Lead?</title>
      <dc:creator>Kelvyn Thai</dc:creator>
      <pubDate>Fri, 14 Nov 2025 08:03:26 +0000</pubDate>
      <link>https://forem.com/kelvynthai/which-devops-certs-are-necessary-for-a-frontend-lead-5pn</link>
      <guid>https://forem.com/kelvynthai/which-devops-certs-are-necessary-for-a-frontend-lead-5pn</guid>
      <description>&lt;p&gt;Hi everyone, I’m Kelvyn, 30 years old.&lt;br&gt;
I’ve been working as a Senior Frontend Engineer (React, Next.js,...) for ~8 years. Recently I’ve been spending more time expanding my DevOps knowledge.&lt;/p&gt;

&lt;p&gt;Even though my long-term direction is Frontend Lead / Frontend Platform / Frontend Architecture (not DevOps Engineer), I’ve realized that modern FE teams still need strong DevOps literacy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Linux&lt;/li&gt;
&lt;li&gt;Dockerizing Next.js&lt;/li&gt;
&lt;li&gt;Gitlab CI/CD pipelines, Gitlab runner, manage jobs,...&lt;/li&gt;
&lt;li&gt;S3 / CloudFront / ECR deployments&lt;/li&gt;
&lt;li&gt;Performance, cache, observability&lt;/li&gt;
&lt;li&gt;Edge/serverless architectures&lt;/li&gt;
&lt;li&gt;K8s&lt;/li&gt;
&lt;li&gt;Terraform for provisioning FE infra&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I’m exploring which DevOps or cloud certifications are genuinely useful for a Frontend leader, not someone aiming to become DevOps/Infra.&lt;/p&gt;

&lt;p&gt;Here’s what I’m considering some certs from Linux Foundation:&lt;/p&gt;

&lt;p&gt;📌 My Shortlist&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LFCA (Linux + cloud + DevOps fundamentals)&lt;/li&gt;
&lt;li&gt;KCNA (lightweight K8s &amp;amp; cloud-native literacy)&lt;/li&gt;
&lt;li&gt;AWS Developer Associate (seems highly relevant for FE deployments)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you were a Frontend Lead / FE Architect / FE Platform engineer,&lt;br&gt;
or if you've walked a similar path:&lt;/p&gt;

&lt;p&gt;👉 Which DevOps/cloud certification actually helped you?&lt;br&gt;
👉 Which ones were not worth it?&lt;br&gt;
👉 What would you learn if you were rebuilding your career today?&lt;/p&gt;

&lt;p&gt;I’d really appreciate any insight or personal experience — especially from FE leads who integrate DevOps into their workflow without going full-time infra.&lt;/p&gt;

&lt;p&gt;Thanks so much!&lt;/p&gt;

</description>
      <category>career</category>
      <category>frontend</category>
      <category>gitlab</category>
      <category>cicd</category>
    </item>
  </channel>
</rss>
