<?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: Todsapon Boontap</title>
    <description>The latest articles on Forem by Todsapon Boontap (@atsyot).</description>
    <link>https://forem.com/atsyot</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%2F381148%2Fc271628f-4248-4486-9c40-6dcef5311981.jpeg</url>
      <title>Forem: Todsapon Boontap</title>
      <link>https://forem.com/atsyot</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/atsyot"/>
    <language>en</language>
    <item>
      <title>Enhancing Error Handling in TypeScript: Leveraging Functional Concepts for Better Practices</title>
      <dc:creator>Todsapon Boontap</dc:creator>
      <pubDate>Fri, 08 Dec 2023 22:49:26 +0000</pubDate>
      <link>https://forem.com/atsyot/enhancing-error-handling-in-typescript-leveraging-functional-concepts-for-better-practices-hib</link>
      <guid>https://forem.com/atsyot/enhancing-error-handling-in-typescript-leveraging-functional-concepts-for-better-practices-hib</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;As someone who tends to prioritize addressing errors upfront, encountering an error that can arise without our code being ready to catch it makes me feel uneasy. &lt;br&gt;
I believe error handling is a crucial topic that doesn't receive as much attention as it deserves, especially when working with JavaScript and TypeScript. There are gaps that need to be filled in these languages, and I personally believe we should put in more effort to handle errors effectively, rather than solely relying on native features.&lt;/p&gt;

&lt;p&gt;OK, today I'll begin with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The native approach in TypeScript/JavaScript.&lt;/li&gt;
&lt;li&gt;A common approach that is widely used and can be found in many projects.&lt;/li&gt;
&lt;li&gt;Then, a more preferable and better way.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Starting with what TypeScript/JavaScript natively provides for error handling: &lt;code&gt;try&lt;/code&gt; and &lt;code&gt;catch&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="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;op1&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;op2&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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;Here, all the operations within the try block are executed, and if an error occurs, the catch block is triggered.&lt;/p&gt;




&lt;h2&gt;
  
  
  A common approach
&lt;/h2&gt;

&lt;p&gt;Let's assume we're working on the backend side. We've structured our operations into different layers such as Validation and Database layers.&lt;/p&gt;

&lt;p&gt;For the purpose of this example, let's create two types of errors and simulate fake operations. One operation will result in a promise rejection or error, while the other will simulate success. This might look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ErrorFromDBLayer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ErrorFromValidationLayer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;field&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;reason&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;let&lt;/span&gt; &lt;span class="nx"&gt;validationOp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number only&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="nx"&gt;ErrorFromValidationLayer&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;dbOp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;({}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ErrorFromDBLayer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's imagine that we're working on a route handling function. This function will execute the previously created fake operations.&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;routeHandler&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;validationOp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;dbOp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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="c1"&gt;// handle errors }&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To me, this approach seems prevalent, present in about 3/4 of projects. It appears quite normal and understandable, right?&lt;/p&gt;

&lt;p&gt;However, let's take a step back, calm down, and analyze this pattern.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;One concern that comes to mind is within the try block: how can we distinguish which operation fails and what type of error it encounters? TypeScript lacks a native way to specify Promise reject function return types, leading all errors to fall into the catch block. In such cases, what is typically done is creating a function to handle all operation errors. Let's start by implementing the handleErrors function.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Firstly, I'll create a type guard utility function for error types.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// define type gard util funcs&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isErrorFromDBLayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;ErrorFromDBLayer&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;msg&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;t&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;isErrorFromValidationLayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;ErrorFromValidationLayer&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;errors&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;t&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;Then the handle errors function and update our catch block. Like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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="nf"&gt;handleErrorsByTypeNarrowing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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;handlerErrorByTypeNarrowing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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="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;e&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&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;let&lt;/span&gt; &lt;span class="nx"&gt;errObj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;object&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="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="k"&gt;case&lt;/span&gt; &lt;span class="nf"&gt;isErrorFromDBLayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;errObj&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;error msg from db layer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;errObj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nf"&gt;isErrorFromValidationLayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;errObj&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;error msg from validation layer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;errObj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;default&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;unknown error&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="k"&gt;else&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;unknown error&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As observed, this function effectively manages errors through type narrowing with a type guard function.&lt;/p&gt;

&lt;h3&gt;
  
  
  So what's wrong with this approach?
&lt;/h3&gt;

&lt;p&gt;Here are a few concerns that come to mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When dealing with more than just two operations, or when working with code we haven't authored ourselves, we're forced to delve into all interacting functions to identify error types. This can become quite cumbersome and time-consuming for developers, as it requires loading a lot of context beforehand to proceed.&lt;/li&gt;
&lt;li&gt;As developers, we aim to avoid writing code that requires readers or our future selves to invest a significant amount of time understanding context just to continue working. This can slow down development and productivity.&lt;/li&gt;
&lt;li&gt;Due to the lack of error typing support in Promises, manual detection of error types and handling becomes necessary. Consequently, when introducing more operations, the handle function needs frequent updates. This lack of scalability may lead to error-prone code.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  A Better approach
&lt;/h2&gt;

&lt;p&gt;In this approach, we'll explore a type commonly used in functional programming known as the Result type. This generic type can represent either an Ok or Error state. Let's gain a better understanding by examining its type definition.&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;Err&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;F&lt;/span&gt;&lt;span class="o"&gt;&amp;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;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fails&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;F&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;Ok&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;F&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Ok&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;Err&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;F&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 examine languages like Rust, where the type definition appears quite similar. Since TypeScript enums don't allow embedding associated values, we can implement a similar concept using discriminated unions.&lt;/p&gt;

&lt;p&gt;Let's proceed by updating our operation functions.&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;validationOp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="nx"&gt;ErrorFromValidationLayer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fails&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number only&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;let&lt;/span&gt; &lt;span class="nx"&gt;dbOp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="nx"&gt;ErrorFromDBLayer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;success&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As observed, our Promise now has a specified result type that supports Ok and Err types, effectively enhancing the Promise's capabilities.&lt;/p&gt;

&lt;p&gt;Now, let's examine our updated implementation of the routeHandler function.&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;routeHandler&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;validationOp_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;validationOp&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;validationOp_result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identifier&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fails&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;// log error, write an error response&lt;/span&gt;

    &lt;span class="c1"&gt;// also get benefit of auto intellisence, by discriminated union!&lt;/span&gt;
    &lt;span class="c1"&gt;// processValidation_result.reason.errors&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dbOp_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;dbOp&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;dbOp_result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identifier&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fails&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;// log error, write an error response&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// return success response, etc..&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this lengthy journey, let's reflect on what improvements we've achieved.&lt;/p&gt;

&lt;p&gt;The primary enhancement is the complete removal of the handleError function, rendering it unnecessary.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why?
&lt;/h4&gt;

&lt;p&gt;As evident in our updated routeHandler function:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We now possess the capability to catch errors early and exit the operation flow, granting more control.&lt;/li&gt;
&lt;li&gt;The power of discriminated types becomes apparent within the if true block, where our editor or IDE offers auto-completion for error types.&lt;/li&gt;
&lt;li&gt;Due to our knowledge of the error type, we can handle it with type safety, eliminating the need for implementing type narrowing functions to detect errors.&lt;/li&gt;
&lt;li&gt;With the Result type in place, we can seamlessly add numerous operations without the necessity to navigate to the function source for its definition. This scalability is achieved without requiring modifications.&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;Although TypeScript lacks direct support for error typing, we can draw upon concepts from functional programming to enhance our approach. The Result type, found in various functional languages, provides a foundation for further exploration. For instance, implementing traditional functional-style mapping functions to map Ok or Err values and enable type chaining is a potential avenue to explore, albeit beyond the scope of this discussion.&lt;/p&gt;

&lt;p&gt;I hope this idea proves helpful for better error handling!&lt;/p&gt;

&lt;p&gt;Enjoy the enhancements!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Solving zustand persisted store re-hydration merging state issue</title>
      <dc:creator>Todsapon Boontap</dc:creator>
      <pubDate>Sun, 19 Nov 2023 20:53:43 +0000</pubDate>
      <link>https://forem.com/atsyot/solving-zustand-persisted-store-re-hydtration-merging-state-issue-1abk</link>
      <guid>https://forem.com/atsyot/solving-zustand-persisted-store-re-hydtration-merging-state-issue-1abk</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;After spending about four months working in the Vue.js environment, I didn't choose it; the existing team was already using it. Now, I've transitioned back to React with Next.js.&lt;/p&gt;

&lt;p&gt;The reason I chose Next.js over just React or React with Vite is because I'm intrigued by the concept of server components—a relatively new paradigm in server-side rendering that caught my interest.&lt;/p&gt;

&lt;p&gt;Having been away from React for a while, I've noticed a significant expansion in the ecosystem of state management libraries. The last library I used for state management was Jotai. Now, there are new ones like xstate and zustand. After quickly exploring their documentation, I opted for zustand. It seems to offer an improved, less verbose version of Redux, which I believe early React developers are familiar with. Plus, I still appreciate the idea of time-traveling state.&lt;/p&gt;

&lt;p&gt;Alright, that's enough for the introduction. Let's get started!&lt;/p&gt;

&lt;h2&gt;
  
  
  Usecase and issues
&lt;/h2&gt;

&lt;p&gt;I've begun setting up a Zustand store following consultation with the official documentation. My goal is to partially persist certain parts of the state. Here's how I've set up the store:&lt;/p&gt;

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

&lt;p&gt;I employ the slices pattern to construct and divide the main store into smaller parts, adhering to this good practice. As evident, I utilize devtools to encapsulate all state slices, but intentionally choose to persist the UserSlice.&lt;/p&gt;

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

&lt;p&gt;This is how I've set up the app-user-slice. As you can see, I utilize the persist middleware from the &lt;/p&gt;

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

zustand/middleware


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

&lt;/div&gt;

&lt;p&gt;package to handle this process. The expected behavior should be that upon refreshing or reloading the website, all persisted state should properly rehydrate to the state shape without any complexity.&lt;/p&gt;

&lt;p&gt;However, a remarkable aspect that simplifies Zustand is its allowance to directly attach any side effect action to the state shape, exemplified by the login function.&lt;/p&gt;

&lt;p&gt;This feature enables us to access the store's associated function and use it within a component, like so:&lt;/p&gt;

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

&lt;p&gt;So, what are the issues here?&lt;/p&gt;

&lt;p&gt;Upon pre-validating this code, we've realized that this component relies on the login function derived from the store to function correctly. However, as previously mentioned, this slice is a persisted slice. The problem arises when I attempt to reload the page for the first time. After the complete reload, the error surfaced, stating that the login function is undefined, although the other state properties exist.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdufxewtidtlrhrk3o4o2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdufxewtidtlrhrk3o4o2.png" alt="Image description"&gt;&lt;/a&gt;&lt;br&gt;
as you can see, login function is disappear!&lt;/p&gt;

&lt;p&gt;This error drive me an hours try figure this out, Here is what I understand and how to solve.&lt;/p&gt;

&lt;p&gt;Initially, I questioned whether I set up the store and the slice correctly or if there was any relation to SSR hydration. Upon investigation, I discovered that the issue wasn't related to the hydration flow. Rather, it stemmed from the process of persisting the state shape to localStorage (the default setting), which involves serializing the current state behind the scenes. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;The critical point here is the process of serialization.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As we're aware, JSON.stringify supports only primitive types and not object or reference types. However, because Zustand allows attaching side-effect functionalities directly to the store's state shape, this limitation becomes apparent. This is precisely why the login function disappears.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solving &amp;amp; Solution
&lt;/h2&gt;

&lt;p&gt;I consulted the official documentation and examined the persist middleware API specification. I came across the "merge" option, which stated:&lt;/p&gt;

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

&lt;p&gt;The default implementation explain it all and this is what I need.&lt;br&gt;
So I add it to my slice like this:&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Why it work
&lt;/h2&gt;

&lt;p&gt;Because every time the page reloads, the hydration is triggered, Zustand recreates the store with the initial state, which includes side-effect functions. Then, it loads the persisted state and attempts to merge them. So, we override the default behavior of the merge function and reassign the sideEffectFunction to currentState.sideEffectFunction (initial state).&lt;/p&gt;

&lt;p&gt;This is the normal and simplest approach because I only have one function in this slice. However, when dealing with more functions or nested state, a more advanced approach, such as using deepMerge or Immer, can be employed to assist in merging the state shape.&lt;/p&gt;

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

&lt;p&gt;For me, this kind of issue seems basic, something I didn't expect with such a popular library. But when it arises, it's a good reminder to revisit fundamental concepts, such as serialization and its limitations.&lt;/p&gt;

&lt;p&gt;In terms of Zustand's intention to allow us to include functionality in the state, it's somewhat predictable that this type of issue might surface. I wonder why Zustand doesn't handle this case for us. Just imagine having to override the merge option behavior for every persisting slice—this could impact the Developer Experience (DX), right?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As users, we encounter limitations and then resolve them. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Hopefully, this insight is helpful.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/em&gt;:&lt;br&gt;
&lt;a href="https://docs.pmnd.rs/zustand/integrations/persisting-store-data#merge" rel="noopener noreferrer"&gt;persist middleware merge option&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>nextjs</category>
      <category>zustand</category>
      <category>learning</category>
    </item>
    <item>
      <title>Dark mode เหมาะกับสายตา Dev อย่างเราจริงๆ หรอ, นอกจากความชอบ มีผลเสียอะไรไหม</title>
      <dc:creator>Todsapon Boontap</dc:creator>
      <pubDate>Mon, 19 Apr 2021 07:42:25 +0000</pubDate>
      <link>https://forem.com/atsyot/dark-mode-dev-4jb0</link>
      <guid>https://forem.com/atsyot/dark-mode-dev-4jb0</guid>
      <description>&lt;p&gt;เดี๋ยวนี้ หันไปทางไหน Developer 99% ใช้ dark theme ในการ coding กันหมดเลย แต่ด้วยส่วนตัวผมเอง ตอนนี้เปลี่ยนมาใช้ light theme มาสักพักใหญ่และ (เกิน 6 เดือน) เปลี่ยนโดยที่ไม่ได้มีความรู้อะไร นอกจากความรู้สึกว่า มองๆ code ไปมันสบายตาดีกว่านี่หว่า&lt;/p&gt;

&lt;p&gt;วันนี้ รู้สึกสงสัยเพิ่ม ฮ่าๆ ว่าทำไมว้า เลยไปหาคำตอบมา ซึ่งพอไปอ่าน รู้สึกมีประโยชน์ดีครับ&lt;/p&gt;




&lt;h2&gt;
  
  
  Dark mode เป็นสิ่งที่เพิ่งมีมาหรอ
&lt;/h2&gt;

&lt;p&gt;ย้อนไป ในยุคสมัยก่อน computer ใช้จอ CRT ซึ่งแสดงตัวหนังสือเป็นสีเขียว และพื้นหลังเป็นสีดำ หรือ มีเครื่องที่เอาใว้ใช้ทำ words procesing ก็แสดงตัวหนังสือสีขาว บนพิ้นหลังสีดำ &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LCyd4dPD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kf8f7pe279htmcmp8ukv.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LCyd4dPD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kf8f7pe279htmcmp8ukv.jpeg" alt="Alt Text" width="800" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;มาเปลี่ยนกลายเป็น พื้นหลังสีขาว ตัวหนังสือสีดำ ก็ช่วงยุค 80s โดยบริษัทส่ง fax ในยุคนั้น ซึ่งทำมาจากเหตุผลว่าอยากเลียนแบบให้เหมือนอยู่บนกระดาษ แล้วเราเขียนหรืออ่านหนังสือ &lt;br&gt;
   ตรงนี้เราจะเห็นว่า dark mode ไม่ใช่ innovation อะไรใหม่เลย&lt;/p&gt;

&lt;h2&gt;
  
  
  ทำไมเราควรใช้
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;ในเชิงพลังงาน คือการใช้ dark mode มีส่วนช่วยลดการใช้พลังงาน กล่าวคือเวลาที่เราใช้ dark mode พื้นหลังเป็นสี ดำ หรือ เทา ทำให้ตัว pixel dot บนหน้าจอของเราไม่ได้เปิดทำงานทุก dot ที่มี ต่างจาก light mode ที่ส่วนนี้ทำงานเต็มที่เพื่อแสดงพื้นหละงสีขาวให้เรา&lt;/li&gt;
&lt;li&gt;ในเชิงของสุขภาพ คือ light mode จะปล่อย blue light ออกมาเยอะมากกว่า ซึ่ง blue light ก็ไม่ใช่อะไรใหม่ แต่ว่ามันมีอยู่ตามธรรมชาติอยู่แล้ว เช่นแสงแดดเป็นต้น ผลของมันก็คือ การสร้าง melatonin บนผิวเราจะเพิ่มขึ้น พูดง่ายๆ คือ พวกขี้แมลงวัน ที่ขึ้นบนหน้าเรา เมื่อเราเริ่มอายุเพิ่มขึ้นเรื่อยๆ นั้นเอง
และ blue light ยังมีผลต่อ hormone ที่เกี่ยวข้องกับการนอนหลับด้วยครับ&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  แล้วทำไมไม่ควรใช้
&lt;/h2&gt;

&lt;p&gt;มนุษย์เรา สายตาเรานั้นมองได้ชัด ในเวลากลางวัน มากกว่า กลางคืน เพราะสายตาเราถูกพัฒนามา ให้ เห็นสีดำ บน สีขาว โดยไม่สนว่า สีดำจะเป็นอะไร สิ่งของ หรือตัวหนังสือ ถ้า พื้นหลังเป็นสีขาว เราโอเคหมด&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;จากที่กล่าวไปข้างต้นว่า เวลาเราใช้ dark mode นั้น เจ้า pixel dot ไม่จำเป็นต้องทำงานเต็มที่ (ไม่ได้ activate pixel dot ทั้งหมดที่มี) ทำให้เวลาเราอ่านเขียน ด้วย dark mode รูม่านตาเราจะขยายมากขึ้นเพื่อรับแสง (ก็แสงมันน้อย) และ การกระพริบตาของเราก็จะน้อยลงตาม ซึ่งเป็นการปรับตัวของกล้ามเนื้อตา เพื่อช่วยให้เรา focus สิ่งที่มองได้ง่ายขึ้น&lt;br&gt;
แน่นอนว่าเมื่อเรากระพริบตาน้อยลง ส่งผลให้ตาแห้ง ซึ่งเป็นสาเหตุของความเสียหายของกระจกตา ซึ่งถ้าปล่อยผ่านไป มีผลให้สายตาของเรา แย่ลงในที่สุดครับ&lt;br&gt;
ในทางกลับกัน Light mode สายตาเราไม่ต้องพยายามมากนักที่จะ focus ครับ&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;สายตาเราถ้าเราอยู่กับ dark mode (แสงน้อย) เป็นเวลานานมากพอ เขาจะเกิดการปรับตัวให้ชินครับ ทีนี้ สมมุติเราต้องขับรถกลางวันเราจะรู้สึกว่าตาเรามันไม่ค่อยสู้แสง รู้สึกว่าใช้พลังเยอะในการเพ่ง ซึ่งเป็นส่วนของสาเหตุ ของการง่ายขณะขับรถครับ&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;สรุปก็คือ รู้ใว้ว่า สายตาเราประกอบไปด้วยกล้ามเนื้อด้วย ถ้าเกิดเรารู้สึกว่า ต้องพยายามในการ focus เราอย่าไปฝืนมันครับ&lt;/p&gt;




&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4Pw-ZAlc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0ummhjwpblmt3w8y7yx8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4Pw-ZAlc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0ummhjwpblmt3w8y7yx8.png" alt="Alt Text" width="603" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ส่วนตัวตอนนี้ ผมใช้ light theme ครับ เริ่มจาก เวลา coding ตัว code editor ของเรามืดเพราะเรา set dark theme ใช่ไหมครับ แต่เวลาที่เราทำงานอยู่ เราก็ต้องเปิด Website หรือ อื่นๆ ไปด้วย แล้วต้องสลับไปมา ขาวๆ ดำๆ พอนานผมรู้สึกไม่สบายตา ก็เลยลองเปลี่ยนครับ &lt;br&gt;
   ตัวผมเองไม่ได้แคร์เรื่อง blue light มากเพราะมันยังไม่มีข้อมูลยืนยันว่ามีผลเสียต่อสายตา (ถ้ามีบอกด้วยยย) นอกจากผิวเรา ก็ทากันแดดกันได้ครับ หรือการนอน ก็ไม่ใช้คอมสัก 1 ชมก่อนนอน ก็แก้ได้ แต่ผมต้องอยู่หน้าคอม หลายชั่วโมงต่อวัน light theme สำหรับ code editor จึงเหมาะกับผมมากกว่าครับ&lt;/p&gt;




&lt;p&gt;สุดท้าย จะเห็นว่าจริงๆ ก็มีทั้งข้อดี ข้อเสีย ของทั้งสอง mode ซึ่งก็ขึ้นอยู่กับวิธีใช้งานของเรา และว่าเราให้น้ำหนักที่อะไร ครับ&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Builder: design pattern</title>
      <dc:creator>Todsapon Boontap</dc:creator>
      <pubDate>Thu, 07 May 2020 04:59:28 +0000</pubDate>
      <link>https://forem.com/atsyot/builder-design-pattern-422d</link>
      <guid>https://forem.com/atsyot/builder-design-pattern-422d</guid>
      <description>&lt;p&gt;สวัสดีครับ Devs ถ้าเรื่อง code quality สำคัญสำหรับคุณ เราคือเพื่อนกัน&lt;/p&gt;

&lt;p&gt;Devs เรา code กันมา เราน่าจะเคยเห็น class ที่การจะสร้าง instance ของพี่เขาเนี่ย&lt;br&gt;
require parameters เยอะ หรือบาง instance ต้องการการ set runtime config ค่าบางอย่างเพื่อให้เขาทำงานได้ถูกต้อง ดังตัวอย่างง่ายๆ ดังนี้&lt;/p&gt;

&lt;p&gt;ClassGenerator มีหน้าที่ gen code ออกมาเป็น string ธรรมดา&lt;br&gt;
ตาม config ที่มีให้อย่างในภาพด้านล่าง&lt;/p&gt;

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

&lt;p&gt;จากภาพสังเกตุว่าใน function generate จะมีส่วนที่มีปัญหานิดหน่อย &lt;/p&gt;

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

&lt;p&gt;ตรงนี้&lt;br&gt;
ถ้าเกิดเรา config parentClass แต่ไม่กำหนด isExtends เป็น true&lt;br&gt;
code ที่ gen ออกมาก็จะไม่ มีสวน extends parent class ให้&lt;br&gt;
ซึ่งมันไม่ใช่ expected behavior&lt;/p&gt;

&lt;p&gt;ซึ่งสิ่งที่อาจเป็นปัญหาคร่าวๆ คือ &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Devs คนอื่นๆ หรือ ตัวเราเอง ที่ใช้ object นี้ต่ออาจลืม setter บางตัวได้&lt;/li&gt;
&lt;li&gt;จากข้อบน อาจทำให้เกิด unexpected behavior ได้&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ทีนี้ Builder pattern จะมาช่วยเราอย่างไรบ้าง&lt;br&gt;
โดยตาม theory บอกว่า &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;builder ทำให้การสร้าง หรือ config object ที่มีความ complex ให้ง่ายขึ้น&lt;/li&gt;
&lt;li&gt;builder นั้นเป็นอิสระจาก object อื่นๆด้วยครับ&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Welcome Builder class&lt;/p&gt;

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

&lt;p&gt;สังเกตุว่า class Builder นี้ใน function parentClass&lt;br&gt;
ผมกำหนดให้ไปเลยว่า ถ้ามีการ set parent class เข้ามา &lt;br&gt;
isExtends ต้องเป็น true เลย ซึ่งก็จะลดความซับซ้อน ไปได้ครับ&lt;/p&gt;

&lt;p&gt;ซึ่งวิธีการเรียกใช้เป็นแบบนี้&lt;/p&gt;

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

&lt;p&gt;code เราที่เขียน เพื่อนมาดูต่อก็ จะรู้สึก มีอารยธรรมครับผม&lt;/p&gt;

&lt;p&gt;ทีนี้ด้วยความเป็นคนจุกจิก ไม่พอใจอะไรง่ายๆ ครับ ฮ่าๆ&lt;br&gt;
แล้วมันทำได้ดีกว่านี้อีกไหม ?&lt;br&gt;
คำตอบคือได้ครับ &lt;/p&gt;

&lt;p&gt;ขอแนะนำให้รู้จักสิ่งที่เรียกว่า Fluent Builder pattern&lt;br&gt;
ซึ่งเขาก็คือ builder แหละ แต่เป็น style ที่สวยขึ้น useful มากกว่า&lt;br&gt;
โดยเราจะ implement ได้ดังนี้&lt;/p&gt;

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

&lt;p&gt;ซึ่งสิ่งที่เราเพิ่มเติมขึ้น เราให้ class builder นี้ return ตัวเขาเองออกมาทุกครั้ง&lt;br&gt;
เพื่อให้เราสามารถ ทำ chain configuration ได้ เพื่อให้เราเรียกใช้ แบบนี้ได้ครับ&lt;/p&gt;

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

&lt;p&gt;เป็นงัยบ้างครับ สำหรับ Builder pattern&lt;br&gt;
ทีนี้เรารู้จักการเขียนแบบนี้ Team เรา หรือเราจาก อนาคต&lt;br&gt;
จะรู้สึกว่าได้ร่วมงานกับ คนที่มีอารธรรมที่สูงส่งกว่าครับ&lt;/p&gt;

&lt;p&gt;หวังว่าเพื่อนๆ Devs จะลองเอาไป implement กันดูนะครับ&lt;/p&gt;

</description>
      <category>design</category>
      <category>codequality</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
