<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Darren Hwang</title>
    <description>The latest articles on DEV Community by Darren Hwang (@dhwang).</description>
    <link>https://dev.to/dhwang</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%2F610402%2Fc5b43a35-32ca-46b1-8990-8ddf3e87c90b.jpeg</url>
      <title>DEV Community: Darren Hwang</title>
      <link>https://dev.to/dhwang</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dhwang"/>
    <language>en</language>
    <item>
      <title>requestAnimationFrame vs requestIdleCallback</title>
      <dc:creator>Darren Hwang</dc:creator>
      <pubDate>Mon, 06 Apr 2026 18:41:02 +0000</pubDate>
      <link>https://dev.to/dhwang/requestanimationframe-vs-requestidlecallback-1m8c</link>
      <guid>https://dev.to/dhwang/requestanimationframe-vs-requestidlecallback-1m8c</guid>
      <description>&lt;p&gt;The primary difference between  (rAF) and  (rIC) is priority and timing within the browser's event loop. &lt;br&gt;
Key Differences &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;(rAF)&lt;/th&gt;
&lt;th&gt;(rIC)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Priority&lt;/td&gt;
&lt;td&gt;High: Critical for visual updates.&lt;/td&gt;
&lt;td&gt;Low: Non-critical background tasks.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Timing&lt;/td&gt;
&lt;td&gt;Executes before the next repaint/refresh.&lt;/td&gt;
&lt;td&gt;Executes during idle periods at the end of a frame.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Frequency&lt;/td&gt;
&lt;td&gt;Matches display refresh rate (usually 60Hz).&lt;/td&gt;
&lt;td&gt;Unpredictable; depends on CPU availability.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best For&lt;/td&gt;
&lt;td&gt;Animations, UI transitions, and DOM changes.&lt;/td&gt;
&lt;td&gt;Analytics, background data processing, or logging.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Browser Support&lt;/td&gt;
&lt;td&gt;Universal support across all modern browsers.&lt;/td&gt;
&lt;td&gt;Not supported in Safari; experimental in others.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  Detailed Comparison
&lt;/h2&gt;
&lt;h3&gt;
  
  
  requestAnimationFrame
&lt;/h3&gt;

&lt;p&gt;• Synchronization: It synchronizes your code with the browser's V-Sync, ensuring that visual changes happen exactly once per frame to prevent "jank" or screen tearing. &lt;br&gt;
• Power Efficiency: If the tab is backgrounded or hidden, the browser automatically pauses rAF calls to save battery and CPU. &lt;br&gt;
• Usage: Ideal for any task that modifies the DOM or CSS styles for visual feedback. &lt;/p&gt;
&lt;h3&gt;
  
  
  requestIdleCallback
&lt;/h3&gt;

&lt;p&gt;• Idle Deadline: It provides a  object with a  method, allowing you to check how many milliseconds are left before the browser needs to process higher-priority tasks. &lt;br&gt;
• Guaranteed Execution: You can pass an optional  (e.g., ) to force the browser to run the task even if it never becomes idle. &lt;br&gt;
• Restrictions: You should not modify the DOM inside rIC, as it occurs after the frame's layout and paint phases; doing so can cause expensive reflows in the next frame. &lt;/p&gt;
&lt;h3&gt;
  
  
  When to use which?
&lt;/h3&gt;

&lt;p&gt;• Use requestAnimationFrame when you need to update the UI or perform an animation. &lt;br&gt;
• Use requestIdleCallback for background tasks like sending telemetry, pre-fetching data, or non-urgent state calculations that shouldn't interrupt user interaction. &lt;/p&gt;
&lt;h2&gt;
  
  
  rAF, rIC and Deferred Value
&lt;/h2&gt;

&lt;p&gt;Use requestAnimationFrame (rAF) or requestIdleCallback (rIC) serve different performance goals compared to the built-in useDeferredValue hook. [1]  &lt;/p&gt;
&lt;h2&gt;
  
  
  Choosing the Right API
&lt;/h2&gt;

&lt;p&gt;• requestAnimationFrame (rAF): Best for visual synchronization. It ensures the value updates just before the next browser repaint (typically every 16.67ms), preventing visual stuttering during high-frequency updates like scrolling or resizing. &lt;br&gt;
• requestIdleCallback (rIC): Best for non-urgent background work. It waits until the browser is completely idle before updating the value, which avoids interfering with critical user interactions like typing. &lt;br&gt;
• useDeferredValue (React Built-in): If you are on React 18+, this is usually superior because it integrates with React's Concurrent Mode. It schedules a low-priority background render that is interruptible if a new update occurs. [2, 3, 4, 5, 6, 7]  &lt;/p&gt;
&lt;h3&gt;
  
  
  Implementation Guidelines
&lt;/h3&gt;

&lt;p&gt;When implementing these in a custom hook, follow these patterns to ensure stability: &lt;/p&gt;

&lt;p&gt;• Use  for IDs: Store the request ID returned by rAF/rIC in a useRef to track and cancel it during cleanup. &lt;br&gt;
• Cleanup on Unmount: Always return a cleanup function in  to call  or  to prevent memory leaks and "ghost" state updates. &lt;br&gt;
• Throttle State Updates: Be cautious with state updates inside these callbacks. High-refresh-rate displays (120Hz+) can trigger rAF more frequently than needed, potentially causing performance overhead if not throttled. &lt;br&gt;
• Browser Compatibility: While rAF is widely supported, requestIdleCallback is not supported in all browsers (e.g., older Safari versions). Use a polyfill or a  fallback for production apps. [3, 8, 9, 10, 11, 12]&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;let&lt;/span&gt; &lt;span class="nx"&gt;fps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;lastTime&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;animate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;animate&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;delta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentTime&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;lastTime&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;delta&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;fps&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="c1"&gt;// Skip frame if too early&lt;/span&gt;

  &lt;span class="nx"&gt;lastTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentTime&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;delta&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;fps&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="c1"&gt;// Perform throttled work&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;animate&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Summary Comparison
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Primary Goal&lt;/td&gt;
&lt;td&gt;Smooth animations/visuals&lt;/td&gt;
&lt;td&gt;Background/Low-priority tasks&lt;/td&gt;
&lt;td&gt;Non-blocking UI updates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Execution Timing&lt;/td&gt;
&lt;td&gt;Before next repaint&lt;/td&gt;
&lt;td&gt;During browser idle time&lt;/td&gt;
&lt;td&gt;React's idle time (interruptible)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best For&lt;/td&gt;
&lt;td&gt;Position/Layout updates&lt;/td&gt;
&lt;td&gt;Analytics, heavy data processing&lt;/td&gt;
&lt;td&gt;Expensive list rendering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;React Integration&lt;/td&gt;
&lt;td&gt;Manual (via )&lt;/td&gt;
&lt;td&gt;Manual (via )&lt;/td&gt;
&lt;td&gt;Native (Concurrent Mode)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;[1] &lt;a href="https://layonez.medium.com/performant-animations-with-requestanimationframe-and-react-hooks-99a32c5c9fbf" rel="noopener noreferrer"&gt;https://layonez.medium.com/performant-animations-with-requestanimationframe-and-react-hooks-99a32c5c9fbf&lt;/a&gt;&lt;br&gt;
[2] &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame&lt;/a&gt;&lt;br&gt;
[3] &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback&lt;/a&gt;&lt;br&gt;
[4] &lt;a href="https://en.blog.jasonzk.com/js/requestidlecallback-and-requestanimationframe/" rel="noopener noreferrer"&gt;https://en.blog.jasonzk.com/js/requestidlecallback-and-requestanimationframe/&lt;/a&gt;&lt;br&gt;
[6] &lt;a href="https://www.youtube.com/watch?v=yIpHTYo3PY0" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=yIpHTYo3PY0&lt;/a&gt;&lt;br&gt;
[10] &lt;a href="https://dev.to/tawe/requestanimationframe-explained-why-your-ui-feels-laggy-and-how-to-fix-it-3ep2"&gt;https://dev.to/tawe/requestanimationframe-explained-why-your-ui-feels-laggy-and-how-to-fix-it-3ep2&lt;/a&gt;&lt;br&gt;
[11] &lt;a href="https://github.com/Chalarangelo/30-seconds-of-code/blob/master/content/snippets/react/s/use-request-animation-frame.md" rel="noopener noreferrer"&gt;https://github.com/Chalarangelo/30-seconds-of-code/blob/master/content/snippets/react/s/use-request-animation-frame.md&lt;/a&gt;&lt;br&gt;
[12] &lt;a href="https://css-tricks.com/using-requestanimationframe-with-react-hooks/" rel="noopener noreferrer"&gt;https://css-tricks.com/using-requestanimationframe-with-react-hooks/&lt;/a&gt;&lt;br&gt;
[13] &lt;a href="https://stackoverflow.com/questions/38709923/why-is-requestanimationframe-better-than-setinterval-or-settimeout" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/38709923/why-is-requestanimationframe-better-than-setinterval-or-settimeout&lt;/a&gt;&lt;br&gt;
[14] &lt;a href="https://medium.com/@ignatovich.dm/understanding-usedeferredvalue-in-react-enhancing-performance-with-deferred-rendering-ec8eb28aa997" rel="noopener noreferrer"&gt;https://medium.com/@ignatovich.dm/understanding-usedeferredvalue-in-react-enhancing-performance-with-deferred-rendering-ec8eb28aa997&lt;/a&gt;&lt;br&gt;
[15] &lt;a href="https://medium.com/zestgeek/understanding-reacts-usedeferredvalue-hook-a-comprehensive-guide-with-examples-f8aa3361ee23" rel="noopener noreferrer"&gt;https://medium.com/zestgeek/understanding-reacts-usedeferredvalue-hook-a-comprehensive-guide-with-examples-f8aa3361ee23&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[2] &lt;a href="https://macarthur.me/posts/navigating-the-event-loop/" rel="noopener noreferrer"&gt;https://macarthur.me/posts/navigating-the-event-loop/&lt;/a&gt;&lt;br&gt;
[3] &lt;a href="https://developer.chrome.com/blog/using-requestidlecallback" rel="noopener noreferrer"&gt;https://developer.chrome.com/blog/using-requestidlecallback&lt;/a&gt;&lt;br&gt;
[4] &lt;a href="https://www.luisball.com/blog/request-animation-frame-versus-request-idle-callback" rel="noopener noreferrer"&gt;https://www.luisball.com/blog/request-animation-frame-versus-request-idle-callback&lt;/a&gt;&lt;br&gt;
[5] &lt;a href="https://stackoverflow.com/questions/61145102/using-requestanimationframe-inside-requestidlecallback" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/61145102/using-requestanimationframe-inside-requestidlecallback&lt;/a&gt;&lt;br&gt;
[6] &lt;a href="https://powerfulyang.com/post/104" rel="noopener noreferrer"&gt;https://powerfulyang.com/post/104&lt;/a&gt;&lt;br&gt;
[7] &lt;a href="https://andriii.hashnode.dev/windowrequestidlecallback-all-you-need-to-know" rel="noopener noreferrer"&gt;https://andriii.hashnode.dev/windowrequestidlecallback-all-you-need-to-know&lt;/a&gt;&lt;br&gt;
[10] &lt;a href="https://www.clicktorelease.com/blog/calculating-fps-with-requestIdleCallback/" rel="noopener noreferrer"&gt;https://www.clicktorelease.com/blog/calculating-fps-with-requestIdleCallback/&lt;/a&gt;&lt;br&gt;
[11] &lt;a href="https://andriii.hashnode.dev/requestanimationframe-all-you-need-to-know" rel="noopener noreferrer"&gt;https://andriii.hashnode.dev/requestanimationframe-all-you-need-to-know&lt;/a&gt;&lt;br&gt;
[12] &lt;a href="https://dev.to/sylwia-lask/9-things-youre-overengineering-the-browser-already-solved-them-o99"&gt;https://dev.to/sylwia-lask/9-things-youre-overengineering-the-browser-already-solved-them-o99&lt;/a&gt;&lt;br&gt;
[13] &lt;a href="https://dev.to/codewithrajat/boost-your-web-performance-mastering-javascript-scheduling-methods-56eh"&gt;https://dev.to/codewithrajat/boost-your-web-performance-mastering-javascript-scheduling-methods-56eh&lt;/a&gt;&lt;br&gt;
[15] &lt;a href="https://caniuse.com/requestidlecallback" rel="noopener noreferrer"&gt;https://caniuse.com/requestidlecallback&lt;/a&gt;&lt;br&gt;
[16] &lt;a href="https://www.youtube.com/watch?v=oDYT2J5zNHc" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=oDYT2J5zNHc&lt;/a&gt;&lt;br&gt;
[17] &lt;a href="https://medium.com/imgcook/a-closer-look-at-react-fiber-b2ab072fcc2a" rel="noopener noreferrer"&gt;https://medium.com/imgcook/a-closer-look-at-react-fiber-b2ab072fcc2a&lt;/a&gt;&lt;br&gt;
[18] &lt;a href="https://www.youtube.com/watch?v=nect7xrF2go" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=nect7xrF2go&lt;/a&gt;&lt;br&gt;
[19] &lt;a href="https://www.debugbear.com/blog/requestanimationframe" rel="noopener noreferrer"&gt;https://www.debugbear.com/blog/requestanimationframe&lt;/a&gt;&lt;br&gt;
[20] &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Performance/Guides/CSS_JavaScript_animation_performance" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/Performance/Guides/CSS_JavaScript_animation_performance&lt;/a&gt;&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>javascript</category>
      <category>performance</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The most powerful pattern in TypeScript, Discriminated Unions</title>
      <dc:creator>Darren Hwang</dc:creator>
      <pubDate>Mon, 06 Apr 2026 18:19:42 +0000</pubDate>
      <link>https://dev.to/dhwang/the-most-powerful-patterns-in-typescript-discriminated-unions-2inb</link>
      <guid>https://dev.to/dhwang/the-most-powerful-patterns-in-typescript-discriminated-unions-2inb</guid>
      <description>&lt;p&gt;Discriminated Unions is useful in cases where you have a discriminant, which is a common property with a literal type (like 'idle', 'loading') that exists in every member.&lt;/p&gt;

&lt;p&gt;TypeScript will narrow the type based on the discriminator, a common property to discriminate between union members. , making your code much safer.&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;State&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;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;idle&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;loading&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;success&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;data&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;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Error&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;handleState&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;State&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;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&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="s1"&gt;idle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;// TypeScript knows state is { status: 'idle' }&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;loading&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;// TypeScript knows state is { status: 'loading' }&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;success&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;// TypeScript knows state has 'data' property&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;state&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="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;// TypeScript knows state has 'error' property&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;error&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;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="k"&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;_exhaustive&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;status&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="s2"&gt;`Unhandled status: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;status&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The most powerful patterns in TypeScript. &lt;strong&gt;Discriminated Unions&lt;/strong&gt; allow you to create a type-safe state machine where the compiler ensures you only access data that actually exists in a given state.&lt;/p&gt;

&lt;p&gt;Here is a breakdown of why this pattern is the gold standard for state management.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Core Components
&lt;/h2&gt;

&lt;p&gt;To create a Discriminated Union, you need three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;The Members:&lt;/strong&gt; Individual object types representing different states.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Discriminant:&lt;/strong&gt; A common property with a &lt;strong&gt;literal type&lt;/strong&gt; (like &lt;code&gt;'idle'&lt;/code&gt;, &lt;code&gt;'loading'&lt;/code&gt;) that exists in every member.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Union:&lt;/strong&gt; A type that combines them using the &lt;code&gt;|&lt;/code&gt; operator.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Example Anatomy
&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;State&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;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;idle&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;loading&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;success&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;data&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;// 'data' only exists here&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;   &lt;span class="c1"&gt;// 'error' only exists here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. Why It Beats "Optional Property" Hell
&lt;/h2&gt;

&lt;p&gt;Without unions, developers often use one giant object with optional properties. This is dangerous because it allows for "impossible states."&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;The "Bad" Way (Optional Properties)&lt;/th&gt;
&lt;th&gt;The "Good" Way (Discriminated Unions)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;data?: string; error?: Error;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Data and Error are tied to specific statuses.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;You could accidentally have both &lt;code&gt;data&lt;/code&gt; and &lt;code&gt;error&lt;/code&gt; at the same time.&lt;/td&gt;
&lt;td&gt;The type system makes it impossible to have &lt;code&gt;data&lt;/code&gt; while in an &lt;code&gt;error&lt;/code&gt; state.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Requires constant null checks or "non-null assertions" (&lt;code&gt;!&lt;/code&gt;).&lt;/td&gt;
&lt;td&gt;TypeScript &lt;strong&gt;narrows&lt;/strong&gt; the type automatically.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  3. Type Narrowing in Action
&lt;/h2&gt;

&lt;p&gt;As you showed in your post's &lt;code&gt;switch&lt;/code&gt; statement, once you check the &lt;code&gt;status&lt;/code&gt; property, TypeScript "narrows" the object to that specific member of the union.&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;handleState&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;State&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;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;success&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="c1"&gt;// Inside this block, TypeScript knows 'data' exists.&lt;/span&gt;
    &lt;span class="c1"&gt;// You don't need to check if state.data is undefined.&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;state&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  4. Exhaustiveness Checking
&lt;/h2&gt;

&lt;p&gt;One of the best "pro tips" for state management is ensuring you've handled every possible state. You can use the &lt;code&gt;never&lt;/code&gt; type to catch unhandled cases at compile time:&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;handleState&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;State&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;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&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="s1"&gt;idle&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="s1"&gt;Waiting...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;loading&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="s1"&gt;Loading...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;success&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;state&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="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;// If you add a new state like 'processing' later, &lt;/span&gt;
      &lt;span class="c1"&gt;// TypeScript will throw an error here because 'processing'&lt;/span&gt;
      &lt;span class="c1"&gt;// cannot be assigned to 'never'.&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;state&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;_exhaustiveCheck&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;
  
  
  Summary of Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Safety:&lt;/strong&gt; Prevents accessing &lt;code&gt;data&lt;/code&gt; when the app is still &lt;code&gt;loading&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clarity:&lt;/strong&gt; The code serves as documentation for what data is available when.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintainability:&lt;/strong&gt; Adding a new state (e.g., &lt;code&gt;reconnecting&lt;/code&gt;) triggers compiler errors in every function that hasn't accounted for it yet.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Further reading and references
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://dev.to/shayy/20-typescript-tricks-every-developer-should-know-94c"&gt;https://dev.to/shayy/20-typescript-tricks-every-developer-should-know-94c&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/maryanmats/stop-using-booleans-everywhere-use-union-types-instead-k9m"&gt;https://dev.to/maryanmats/stop-using-booleans-everywhere-use-union-types-instead-k9m&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>typescript</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Learning CacheStorage</title>
      <dc:creator>Darren Hwang</dc:creator>
      <pubDate>Thu, 12 May 2022 19:03:42 +0000</pubDate>
      <link>https://dev.to/dhwang/learning-cachestorage-12c7</link>
      <guid>https://dev.to/dhwang/learning-cachestorage-12c7</guid>
      <description>&lt;p&gt;Just learned about CacheStorage, will try experiment with it. Seems promising&lt;/p&gt;

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