<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>TypeScript &#8211; Software, Fitness, and Gaming &#8211; Jesse Warden</title>
	<atom:link href="https://jessewarden.com/category/typescript/feed" rel="self" type="application/rss+xml" />
	<link>https://jessewarden.com</link>
	<description>Software &#124; Fitness &#124; Gaming</description>
	<lastBuildDate>Tue, 08 Apr 2025 01:06:41 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://jessewarden.com/wp-content/uploads/2016/08/cropped-Lambda2-32x32.png</url>
	<title>TypeScript &#8211; Software, Fitness, and Gaming &#8211; Jesse Warden</title>
	<link>https://jessewarden.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Read the Book Domain Modelling Made Functional</title>
		<link>https://jessewarden.com/2025/04/read-the-book-domain-modelling-made-functional.html</link>
		
		<dc:creator><![CDATA[JesterXL]]></dc:creator>
		<pubDate>Tue, 08 Apr 2025 01:04:27 +0000</pubDate>
				<category><![CDATA[TypeScript]]></category>
		<guid isPermaLink="false">https://jessewarden.com/?p=6826</guid>

					<description><![CDATA[Finally got around to reading Domain Modeling Made Functional, Tackle Software Complexity with Domain-Driven Design and F#, by Scott Wlaschin. Book basically covers: There&#8217;s other good content leveraging the above like balancing both exceptions vs. Result (e.g errors as values) &#38; Persistence concerns (e.g. database). My favorite, though, are his translations of OOP concepts to [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Finally got around to reading Domain Modeling Made Functional, Tackle Software Complexity with Domain-Driven Design and F#, by Scott Wlaschin. Book basically covers:</p>



<ol class="wp-block-list">
<li>Domain Driven Design</li>



<li>How to design with types</li>



<li>How to implement 1 and 2 in code &amp; refactor it.</li>
</ol>



<span id="more-6826"></span>



<p>There&#8217;s other good content leveraging the above like balancing both exceptions vs. Result (e.g errors as values) &amp; Persistence concerns (e.g. database).</p>



<p>My favorite, though, are his translations of OOP concepts to FP ones. This is a huge deal because most DDD (Domain Driven Design) content is heavily biased towards OOP, design patterns, and mutation, none of which is in the FP world. Additionally, despite Java&#8217;s &amp; C#&#8217;s massive improvements, OOP devs don&#8217;t worship types like the FP community does, and sum types &amp; records are perfect for modelling things in DDD style, with the compiler&#8217;s help. So having his translations from the OOP world to the FP one helped a lot. He even included Onion/Hexagonal architecture which I loved, omitting the parts that FP devs don&#8217;t need.</p>



<p>I&#8217;ve been meaning to read &#8220;The Blue Book&#8221; and &#8220;The Red Book&#8221;, famous DDD books, for a long time, but my patience of translating OOP things to my world is basically spent, so it was dope to get Scott&#8217;s explanation. The topics are vast so everything had external links if you wanted to learn more.</p>



<p>The biggest thing I took away was I am 90% on track. That was a HUGE sigh of relief; 90% of the book I&#8217;m either like &#8220;Yeah, I know this&#8221; or &#8220;whoa, cool, so I was doing it like you&#8217;re supposed too, rad&#8221;. That made me feel good; I don&#8217;t use F#, I use TypeScript, but it&#8217;s basically the same, just more verbose for unions, and doesn&#8217;t have those dope Monad helpers.</p>



<p>The other crazy thing was it was released in 2018. Excluding Continuous Delivery/TDD, everything in the book is just as relevant in today&#8217;s world in how I build software, and done the same way, so that was another self-esteem boost knowing the core techniques continue to be evergreen, and language agnostic (well&#8230; they _do_ assume your types don&#8217;t suck and even in 2025, there are popular languages who&#8217;s type systems aren&#8217;t that great).</p>



<p>One minor thing, it was hilarious to see DTO&#8217;s, Data Transfer Objects, embraced and encouraged. I haven&#8217;t used DTO&#8217;s or VO&#8217;s, ValueObjects, since my OOP days back in like 2011. I get why, and agree, no doubt, but&#8230; just funny in that:</p>



<ol class="wp-block-list">
<li>most FP devs I work with have no idea what those are</li>



<li>most OOP devs under 32 have zero idea what those are, either</li>
</ol>



<p>Really loved how both relational and NoSQL persistence were covered with raw SQL. Really helped solidify some feelings I&#8217;ve had and concerns, and squashed all anxiety after reading. I only have about 2 years of real Postegres and DynamoDB experience, but based on what I read, I was on the right track &#8220;thinking with an FP mindset&#8221; around persistence, and was missing a major core piece around how to save Unions/Sum Types/Variants, so the examples provided were awesome.</p>



<p>If you don&#8217;t know DDD and want to learn it, buy this book.</p>



<p>If you want to learn how to &#8220;design with types, first, not tests first&#8221;, buy this book.</p>



<p>If you know TypeScript, but aren&#8217;t really sure if the types are giving you value in your work, buy this book and see if it doesn&#8217;t give you a fresh perspective on how to approach your work.</p>



<p><a href="https://pragprog.com/titles/swdddf/domain-modeling-made-functional">https://pragprog.com/titles/swdddf/domain-modeling-made-functional</a></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Error Handling for fetch in TypeScript</title>
		<link>https://jessewarden.com/2025/02/error-handling-for-fetch-in-typescript.html</link>
		
		<dc:creator><![CDATA[JesterXL]]></dc:creator>
		<pubDate>Tue, 18 Feb 2025 15:10:29 +0000</pubDate>
				<category><![CDATA[TypeScript]]></category>
		<guid isPermaLink="false">https://jessewarden.com/?p=6798</guid>

					<description><![CDATA[Introduction The following post describes why and how you do error handling for fetch. Why Care? When you write code that does not handle errors, the code may break at runtime and when deployed to production. Getting PagerDuty calls at 3am are not fun, and are hard to debug because you&#8217;re sleepy. Doing error handling [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">Introduction</h2>



<p>The following post describes why and how you do error handling for <code>fetch</code>.</p>



<h2 class="wp-block-heading">Why Care?</h2>



<p>When you write code that does not handle errors, the code may break at runtime and when deployed to production. Getting PagerDuty calls at 3am are not fun, and are hard to debug because you&#8217;re sleepy. Doing error handling can both prevent those early morning PageDuty alerts, as well as ensure if they do occur, you have a better indication as to what went wrong, and if you need to act on it.</p>



<p>TypeScript can help you with types that make it more clear a piece of code can fail, and ensure you and other developers who build atop it months to years later also handle those errors. You just have to spend the time thinking about and writing the types.</p>



<span id="more-6798"></span>



<h2 class="wp-block-heading">Example of Code That Does Not Handle Errors</h2>



<p>The following code is commonly used in Node.js and the Browser:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-keyword">let</span> value = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">'https://some.server.com/api/data'</span>).then( <span class="hljs-function"><span class="hljs-params">r</span> =&gt;</span> r.json() )</code></span></pre>


<p>This code does not handle the following error conditions:<br>&#8211; if the URL is malformed<br>&#8211; if the fetch has a networking error<br>&#8211; if fetch gets a non-200 http status code response<br>&#8211; if the JSON sent back fails to parse<br>&#8211; if the JSON parses, but is in the incorrect type</p>



<p>You can possibly glean those errors from stack traces, but those aren&#8217;t always easy to read, can sometimes be red herring to the real problem sending you in the wrong direction, and sometimes can be ignored altogether. The above are a bit harder to ascertain at 3am with little sleep.</p>



<h2 class="wp-block-heading">Option 1: Add a catch</h2>



<p>The first step is to handle all errors unrelated to types. You do this either using a try/catch or a <code>.catch</code>. The above code mixes async/await style and Promise chain style. While you can do that, it is recommended to follow one or the other so the code is easier to read and debug.</p>



<p>If you&#8217;re choosing the async await style, it could be re-written like so:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">let</span> response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">'&lt;a href="https://some.server.com/api/data" target="_blank" rel="noreferrer noopener"&gt;https://some.server.com/api/data&lt;/a&gt;'</span>)
    <span class="hljs-keyword">let</span> json = response.json()
    ...
} <span class="hljs-keyword">catch</span>(error) {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"error:"</span>, error)
}</code></span></pre>


<p>If you&#8217;re using Promise chain style, it could look like so:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">fetch(<span class="hljs-string">'&lt;a href="https://some.server.com/api/data" target="_blank" rel="noreferrer noopener"&gt;https://some.server.com/api/data&lt;/a&gt;'</span>)
    .then( <span class="hljs-function"><span class="hljs-params">r</span> =&gt;</span> r.json() )
    .catch( <span class="hljs-function"><span class="hljs-params">error</span> =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"error:"</span>, error))</code></span></pre>


<h2 class="wp-block-heading">Option 2: Add the never Return Type</h2>



<p>If this code is in a function, you do not want the TypeScript types to lie to you. Given TypeScript is a gradually typed language, this means there are cases where it&#8217;s &#8220;mostly typed&#8221;, but not entirely 100% accurate. Take a look at the following function wrapping our fetch call:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-keyword">let</span> getData = ():<span class="hljs-function"><span class="hljs-params">SomeType</span> =&gt;</span> {
    <span class="hljs-keyword">let</span> response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">'&lt;a href="https://some.server.com/api/data" target="_blank" rel="noreferrer noopener"&gt;https://some.server.com/api/data&lt;/a&gt;'</span>)
    <span class="hljs-keyword">let</span> json = response.json()
    <span class="hljs-keyword">return</span> json <span class="hljs-keyword">as</span> SomeType
}</code></span></pre>


<p>The first of 2 issues here is your <code>fetch</code> call can fail with multiple issues, so if an error occurs, nothing is returned. The 2nd is the type casting <code>as</code> has no guarantee Instead, we should change our return type to accurately reflect that, changing from this:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-keyword">let</span> getData = ():<span class="hljs-function"><span class="hljs-params">SomeType</span> =&gt;</span> { ... }</code></span></pre>


<p>To this:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-keyword">let</span> getData = ():SomeType | <span class="hljs-function"><span class="hljs-params">never</span> =&gt;</span> { ... }</code></span></pre>


<p>The <code>never</code> indicates that the function will return your type _or_ never return. This forces all functions to handle that never; you or your fellow developers don&#8217;t have to remember this, TypeScript will tell you. In the case of using that function in an Angular router guard, a predicate function (a function that returns true or false), you can interpret that <code>never</code> as a false:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-keyword">let</span> canNavigate = <span class="hljs-keyword">async</span> ():<span class="hljs-function"><span class="hljs-params">boolean</span> =&gt;</span> {
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">let</span> result = <span class="hljs-keyword">await</span> getData()
        <span class="hljs-keyword">return</span> result.userIsAllowed === <span class="hljs-literal">true</span>
    } <span class="hljs-keyword">catch</span>(error) {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>
    }
}</code></span></pre>


<h2 class="wp-block-heading">Option 3: Add a Result Type</h2>



<p>The above is a good first step, however, it now forces someone _else_ to handle the errors. Given TypeScript is gradual, if someone _else_ is not handling errors, your exception risks being uncaught. The best thing to do is never intentionally throw errors, nor allow ones in your code to propagate, since JavaScript is so bad at exception handling, and TypeScript never&#8217;s aren&#8217;t perfect. Instead, you return a single type that indicates the possibility of failure. There 3 common ones used in TypeScript:</p>



<p>&#8211; <code>Promise</code> &#8211; native to all browsers, Node.js, and handles synchronous and asynchronous code; Errors are `unknown`<br>&#8211; <code>Observable</code> &#8211; typically used in Angular, but supported everywhere you import the <a href="https://rxjs.dev/">RxJS</a> library, and handles synchronous and asynchronous code; Errors are typically typed <code>Observable&lt;never&gt;</code><br>&#8211; <code>Result</code> or <code>Either</code> &#8211; a TypeScript discriminated union; handles synchronous, Errors are typically just strings</p>



<p>The less uncommon are typed FP libraries like <a href="https://effect.website/">Effect</a> or <a href="https://true-myth.js.org/">true-myth</a>.</p>



<p>Let&#8217;s use <code>Promise</code> for now since the <code>fetch</code> and the above code uses Promises already. We&#8217;ll change our <code>getData</code> function from:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-keyword">let</span> getData = ():<span class="hljs-function"><span class="hljs-params">SomeType</span> =&gt;</span> {...}</code></span></pre>


<p>To a type that more represents the possibility the function could succeed or fail:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-keyword">let</span> getData = ():<span class="hljs-built_in">Promise</span>&lt;SomeType&gt; =&gt; {...}</code></span></pre>


<p>While this doesn&#8217;t enforce someone adds a try/catch, there are some runtime enforcement&#8217;s and type helping TypeScript that will at least increase the chance the <code>Promise</code>&#8216;s error condition is handled.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>NOTE: I know it may be hard to divide &#8220;Promise is used for async&#8221; and &#8220;Promise is used to represent a function that can fail&#8221;. For now, just ignore the &#8220;Promises are required for async&#8221; part, and focus on Promise being a box that holds _either_ success or failure. The only way to _know_ typically if an either has success or failure is to open it, and those are done in type safe ways. JavaScript makes this confusing by newer versions of Node.js/Browser yelling at you for missing a catch in JavaScript, whereas TypeScript is more proactive via the compiler errors. </p>
</blockquote>



<p>Uncaught promises will eventually explode. Using an <code>Observable</code> at least ensures it won&#8217;t &#8220;break out&#8221; of the Observable itself, resulting in an unhandled runtime exception.</p>



<p>However, using a <code>Result</code> can be the best option because it ensures a developer cannot get a the value they want unless they handle the error condition, or intentionally choose to ignore it. TypeScript enforces this. We&#8217;ll come back to the asynchronous version in another post, so just pay attention to the examples below in how they enforce the type check:</p>


<pre class="wp-block-code"><span><code class="hljs language-xml">let getData = ():Result<span class="hljs-tag">&lt;<span class="hljs-name">SomeType</span>&gt;</span> =&gt; {...}</code></span></pre>


<p>This means to use that data, the developer must inspect the type. Inspecting a discriminant like this will ensure the user can only access value if it&#8217;s an Ok type, and the error property if it&#8217;s an Err type; the compiler is awesome like that:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-keyword">let</span> canNavigate = ():<span class="hljs-function"><span class="hljs-params">boolean</span> =&gt;</span> {
    <span class="hljs-keyword">let</span> result = getData()
    <span class="hljs-keyword">if</span>(result.type === <span class="hljs-string">'Ok'</span>) {
        <span class="hljs-keyword">return</span> result.value.userIsAllowed === <span class="hljs-literal">true</span>
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>
    }
}</code></span></pre>


<p>Notice they can&#8217;t just write <code>result.value</code> because that property only exists if the Union type is of <code>Ok</code>; an <code>Err</code> does not have a value type, so the code won&#8217;t compile unless you first check the type using an if or switch statement.</p>



<h2 class="wp-block-heading">Option 4: Check for an Ok Response</h2>



<p>The <code>fetch</code> function has the built in ability check if the response is an http status code of 200 through 299 range, meaning it&#8217;s safe to use <code>response.json()</code> (or blob, or text, etc). If you didn&#8217;t get a 200-299 status code, you can be confident you did not get JSON back you were expecting.</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">fetch(<span class="hljs-string">'&lt;a href="https://some.server.com/api/data" target="_blank" rel="noreferrer noopener"&gt;https://some.server.com/api/data&lt;/a&gt;'</span>)
.then( <span class="hljs-function"><span class="hljs-params">r</span> =&gt;</span> {
    <span class="hljs-keyword">if</span>(r.ok) {
        <span class="hljs-keyword">return</span> r.json()
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-comment">// we have an http error code</span>
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">Promise</span>.reject(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">`HTTP Error code: <span class="hljs-subst">${r.statusCode}</span>, reason: <span class="hljs-subst">${r.statusText}</span>`</span>))
    }
})</code></span></pre>


<p>Since much of parsing code isn&#8217;t setup to handle a response already in an non-200 state, there is no point in running that code, so you can choose to exit early, or throw an Error/return a rejected Promise so your <code>catch</code> will handle it early. Importantly, though, you have an opportunity to inspect what went wrong with the request, clearly indicating this is NOT a JSON parsing or type narrowing error, but a problem with the API response itself. This is important in that the type of error you get back can dictate how the developer&#8217;s code will respond. The only way it can do that is if you create a different type so the code can tell the difference in the errors returned.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Caveat: Some API&#8217;s will send back text or JSON error text in the body that you _do_ have to parse, but in a separate code path.</p>
</blockquote>



<h2 class="wp-block-heading">Option 5: Validate Your URL Beforehand</h2>



<p>If the URL you send to fetch is malformed, it&#8217;ll throw an exception before it even makes a network request. While you can rely on the <code>.catch</code> in the fetch promise chain to handle this, another option is to run it through JavaScript&#8217;s <a href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">URL class</a>. One horrible side-effect of that class constructor is if it notices the url is malformed, it&#8217;ll throw an exception.</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-keyword">const</span> getURL = (url:string):string | <span class="hljs-function"><span class="hljs-params">never</span> =&gt;</span> { ... }</code></span></pre>


<p>Notice since the new URL can possibly fail, we type it as &#8220;either a URL string, or it&#8217;ll never return because it exploded&#8221;. We can later use that to our advantage to distinguish between &#8220;the server had a problem&#8221; and &#8220;your JSON is messed up&#8221; and &#8220;your URL is malformed,  bruh&#8221;. You can replace with Promise/Observable/Result too. Example:</p>


<pre class="wp-block-code"><span><code class="hljs language-php"><span class="hljs-keyword">const</span> getURL = (url:string):Result&lt;string&gt; =&gt; {
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> urlValue = <span class="hljs-keyword">new</span> URL(url)
        <span class="hljs-keyword">return</span> Ok(urlvalue.href)
    } <span class="hljs-keyword">catch</span>(error) {
        <span class="hljs-keyword">return</span> Err(error.message)
    }
}</code></span></pre>


<h2 class="wp-block-heading">Option 6: Type Casting</h2>



<p>Type casting, meaning converting from 1 type to the next, is all on the developer. <a href="https://www.typescriptlang.org/docs/handbook/2/narrowing.html">Type narrowing</a> can be a ton of work that is error prone, order important, and may/may not be thorough enough. This is particularly dangerous in <code>JSON.parse</code> because the return type says it&#8217;s an <code>any</code>. However, it&#8217;s _actually_ <code>any | never</code>, and in the case of <code>response.json()</code>, it&#8217;s <code>Promise&lt;any&gt;</code> meaning someone else needs to handle the error scenario. You _can_ use <code>unknown</code> to ensure you, and your fellow developers, are forced to type narrow:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-keyword">const</span> result = <span class="hljs-built_in">JSON</span>.parse(someString)
<span class="hljs-keyword">if</span>(<span class="hljs-keyword">typeof</span> result !== <span class="hljs-string">'undefined'</span>
    &amp;&amp; <span class="hljs-keyword">typeof</span> result?.prop !== <span class="hljs-literal">null</span>
    &amp;&amp; <span class="hljs-keyword">typeof</span> result?.prop === <span class="hljs-string">'string'</span>
    &amp;&amp; ... {
        <span class="hljs-keyword">return</span> Ok(result <span class="hljs-keyword">as</span> YourType)
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">return</span> Err(<span class="hljs-string">'Failed to cast JSON.parse object to YourType.'</span>)
    }
)</code></span></pre>


<p>&#8230;but that&#8217;s a lot of no fun, dangerous work. Better to use a library that has already solved this problem like <a href="https://zod.dev/">Zod</a> or <a href="https://arktype.io/">ArkType</a>. It&#8217;ll ensure the types match up, and if not, give you an error response that _somewhat_ gives you a clue as to why the decoding went wrong, way more thorough and verbose than JSON.parse&#8217;s not so great runtime error messages.</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-keyword">const</span> json = <span class="hljs-built_in">JSON</span>.parse(someString)
<span class="hljs-keyword">const</span> { success, data, error } = YourType.safeParse(someObject)
<span class="hljs-keyword">if</span>(success) {
    <span class="hljs-keyword">return</span> Ok(data)
} <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">return</span> Err(error)
}</code></span></pre>


<h2 class="wp-block-heading">Conclusions</h2>



<p>As you can see, <code>fetch</code> has a lot of things that can go wrong, some can be ignored, and some can actually allow another code path such as retry to happen IF you know what went wrong in the fetching process. TypeScript can help enforce these paths are safe, and you can open up these paths safely now that know what possible things can go wrong in fetch. These are a malformed URL, a networking error, your JSON parsing fails, your JSON does not math your expected type(es), or the server returned an error response. Hopefully some of the above ensures you aren&#8217;t awoken in the middle of the night from your, or someone else&#8217;s code on your team.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Encoders and Decoders in TypeScript</title>
		<link>https://jessewarden.com/2025/02/encoders-and-decoders-in-typescript.html</link>
		
		<dc:creator><![CDATA[JesterXL]]></dc:creator>
		<pubDate>Tue, 18 Feb 2025 01:03:36 +0000</pubDate>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[TypeScript]]></category>
		<guid isPermaLink="false">https://jessewarden.com/?p=6792</guid>

					<description><![CDATA[Encoding Encoding in our context means converting a type to a string so we can save it to the web browser&#8217;s local storage. In soundly typed languages, this operation will never fail, but that is not true in JavaScript which TypeScript compiles to. Caveat: Much of the code below is psuedo types and code. Please [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h1 class="wp-block-heading">Encoding</h1>



<p>Encoding in our context means converting a type to a string so we can save it to the web browser&#8217;s <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage">local storage</a>. In soundly typed languages, this operation will never fail, but that is not true in JavaScript which TypeScript compiles to.</p>



<span id="more-6792"></span>



<p><strong>Caveat</strong>: Much of the code below is psuedo types and code. Please refer to the 3 &#8220;Final Full Examples&#8221; at the bottom of each section to see _actual_ working code you can use and safely reference.</p>



<p><strong>Caveat</strong>: You&#8217;ll see <code>if(error instanceof Error)</code> example code. The <code>instanceof</code> keyword isn&#8217;t great for runtime type checking in all cases, so in the future use <code>Error.isError(error)</code> where applicable. At the time of this writing, <a href="https://github.com/tc39/proposal-is-error">the proposal is Stage 3</a>, which means browsers intend to implement, and TypeScript will start to implement as well. </p>



<h2 class="wp-block-heading">JSON.stringify</h2>



<p>The <code>JSON.stringify</code> function can fail for a variety of reasons (circular references, BigInt), and has various nuances where some data types are converted to null, or are omitted entirely.</p>



<p>That means, encoding is not a safe operation. While not the true type, you can think of <code>JSON.stringify</code> having the following type:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">stringify = <span class="hljs-function">(<span class="hljs-params">data:uknown</span>) =&gt;</span> string | never</code></span></pre>


<p>That means, it can either return a <code>string</code>, or throw an exception and never return anything. Exceptions aren&#8217;t good, thus we must wrap the unsafe operation <code>JSON.stringify</code> and return some other type indicating either a success with the encoding data, or error indicating why the encoding failed.</p>



<h2 class="wp-block-heading">EncodeData Function Type</h2>



<p>In TypeScript, that means the return type must be defined as some type of <code>Result</code> such as a <code>Promise</code> or <code>Observable</code>, because the encoding operation can possibly fail. Below, the encoder&#8217;s type gets some data, and returns a string if it works, else an error if it fails.</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">type encodeData = <span class="hljs-function">(<span class="hljs-params">data:Record&lt;string, unknown&gt;</span>) =&gt;</span> <span class="hljs-built_in">Promise</span>&lt;string&gt;</code></span></pre>


<p>Success example:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">john = { <span class="hljs-attr">name</span>: <span class="hljs-string">'John'</span>, <span class="hljs-attr">age</span>: <span class="hljs-number">17</span> }
result = encodeData(john)
<span class="hljs-built_in">console</span>.log(result.data) <span class="hljs-comment">// '{ "name": "John", "age": 17 }'</span></code></span></pre>


<p>Failure example:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">azathoth = { <span class="hljs-attr">name</span>: <span class="hljs-string">'A̸̰̘͖̔z̵̛̝̀̊͠a̷͖̓̊ṱ̴̯̾̍̓̕ḣ̵̖̘̺̙̇ǫ̸̲̐̓̉t̴̢̜̊͊͂ẖ̵͆͊̾͛'</span>, <span class="hljs-attr">age</span>: BigInt(<span class="hljs-number">9007199254740991</span>) }
result = encodeData(azathoth)
<span class="hljs-built_in">console</span>.log(result.error) <span class="hljs-comment">// TypeError: Do not know how to serialize a BigInt</span></code></span></pre>


<h2 class="wp-block-heading">EncodeData Function Contents</h2>



<p>To ensure the types above remain correct, we can simply wrap the <code>JSON.stringify</code> above in a try/catch.</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-keyword">try</span> {
  <span class="hljs-keyword">const</span> encodedString = <span class="hljs-built_in">JSON</span>.stringify(data)
  <span class="hljs-keyword">return</span> Ok(string)
} <span class="hljs-keyword">catch</span>(error) {
  <span class="hljs-keyword">return</span> Err(error.message)
}</code></span></pre>


<h2 class="wp-block-heading">EncodeData Can Return a String</h2>



<p>If you&#8217;ve seen soundly typed languages like Elm, ReScript, Haskell, Scala, etc, you may have noticed many of their encoders do not fail. This is because the the types are sound, meaning it cannot go wrong going from one type to another. The developer is required to write the encoders by hand, however. There is no one size fits all function like <code>JSON.stringify</code> to magically convert your type to a string. This means our <code>encodeData</code> would have the changed type of:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">type encodeData = <span class="hljs-function">(<span class="hljs-params">data:Record&lt;string, unknown&gt;</span>) =&gt;</span> string</code></span></pre>


<p>JavaScript has many mechanisms to encode more complex data types. The <code>BigInt</code> above that broke our original encoding, you can monkey patch <code>BigInt.prototype.toJSON</code>. For <code>undefined</code> and <code>null</code>, you can choose to manually include the property with a null value, or choose to omit it entirely, treating <code>undefined</code> and <code>null</code> as a missing value.</p>



<p>However, the correctness is hard to get correct with a <code>Record&lt;string, unknown&gt;</code> without library support given JavaScript&#8217;s numerous data types, and various options in encoding the data. Often data is encoded to be _decoded_, which means the developer will encode knowing they&#8217;ll want to decode the data a specific way later.</p>



<h2 class="wp-block-heading">Narrowed Encoder</h2>



<p>This means, we&#8217;ll often narrow our types to be more specific than <code>Record</code>, and then create custom encoders and decoders for them which is easier to create, and easier to verify they are correct in most cases.</p>



<p>Say we have an enum we want to encode:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">enum CowRace {
  Cow = <span class="hljs-string">'cow'</span>,
  NotACow = <span class="hljs-string">'not a cow'</span>
}</code></span></pre>


<p>The type for the encoder would be:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">type encodeCowRace = <span class="hljs-function">(<span class="hljs-params">cowRace:CowRace</span>) =&gt;</span> string</code></span></pre>


<p>The function implementation would look something like the following:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-keyword">if</span>(cowRace === CowRace.Cow) {
  <span class="hljs-keyword">return</span> <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-string">'cow'</span>)
} <span class="hljs-keyword">else</span> {
  <span class="hljs-keyword">return</span> <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-string">'not a cow'</span>)
}</code></span></pre>


<p>Much easier to unit test, verify it is correct with compiler support, and decode back from local storage.</p>



<h2 class="wp-block-heading">Specific Encoders as Function Parameters</h2>



<p>Now that we&#8217;ve established using type narrowing results in easier to encode types, and that those types will have an associated encode function, let&#8217;s look at ways to use them in saving data.</p>



<p>Let&#8217;s encode our <code>CowRace</code> enum above to localstorage. We have 3 types, 2 of which we&#8217;ve already covered; our <code>CowRace</code> enum:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">enum CowRace {
  Cow = <span class="hljs-string">'cow'</span>,
  NotACow = <span class="hljs-string">'not a cow'</span>
}</code></span></pre>


<p>Our <code>encoderCowRace</code> function type:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">type encodeCowRace = <span class="hljs-function">(<span class="hljs-params">cowRace:CowRace</span>) =&gt;</span> string</code></span></pre>


<p>And our new type, the <code>saveCowRaceToLocalStorage</code> function:<br></p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">type saveCowRaceToLocalStorage =
  <span class="hljs-function">(<span class="hljs-params">cowRace:CowRace, encodeCowRace:EncodeCowRace</span>) =&gt;</span>
 string</code></span></pre>


<p>The type takes a <code>cowRace</code> enum, and your encoder function, and returns a string. The string is just whatever it was encoded too. The function may look something this:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">encodedString = encodeCowRace(cowRace)
localStorage.setItem(<span class="hljs-string">'cowrace'</span>, encodedString)
<span class="hljs-keyword">return</span> encodedString</code></span></pre>


<p>You&#8217;d invoke the <code>saveCowRaceToLocalStorage</code> like so:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">result = saveCowRaceToLocalStorage(CowRace.Cow, encodeCowRace)
<span class="hljs-comment">// result is "'cow'"</span></code></span></pre>


<h2 class="wp-block-heading">Generic Encoders as Function Parameters</h2>



<p>The above uses a specific type and associated encoder function. What if you want a way to save to local storage that supports any type? In that case, you use a generic type parameter. Just like functions accept parameters, types accept parameters as well.</p>



<p>Let&#8217;s change our <code>saveCowRaceToLocalStorage</code> in 3 steps: update to accept a generic type, add a second parameter to accept any encoder, and finally add a return type. The generic implies &#8220;You can pass any type you want&#8221; which also means the developer passing the type must also create an encoder for it and pass it to us.</p>



<h2 class="wp-block-heading">Step 1: Accept Generic Type Parameter</h2>



<p>The first step is to change the name and 1st parameter type so we can save _anything_ to localstorage:</p>


<pre class="wp-block-code"><span><code class="hljs language-xml">type saveAnythingLocalStorage<span class="hljs-tag">&lt;<span class="hljs-name">Type</span>&gt;</span> = (data:Type) ...</code></span></pre>


<p>That means now you can pass the <code>cowRace</code> parameter like before, but also the <code>CowEnum</code> type. Notice the <code>cowRace</code> is lowercase to be our enum value, and the <code>CowRace</code> type is uppercase to visually indicate a type:</p>


<pre class="wp-block-code"><span><code class="hljs language-xml">saveAnythingLocalStorage<span class="hljs-tag">&lt;<span class="hljs-name">CowRace</span>&gt;</span>(cowRace)</code></span></pre>


<p>This also supports our <code>Record&lt;string, unknown&gt;</code>:</p>


<pre class="wp-block-code"><span><code class="hljs">saveAnythingLocalStorage&lt;Record&lt;string, unknown&gt;&gt;(john)</code></span></pre>


<h2 class="wp-block-heading">Step 2: Accept Generic Encoder</h2>



<p>The type parameter is a type. The generic encoder is also a type, more specifically a function type. We have to narrow down what the encoder actually returns, though. We&#8217;ll stick to <code>string</code> for now since most encoders will be converting their types to strings for use as JSON strings, in our case saving to local storage so we can read out later.</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">type saveAnythingLocalStorage&lt;Type&gt; =
  (
    data:Type,
    <span class="hljs-attr">encoder</span>:<span class="hljs-function">(<span class="hljs-params">convertMe:Type</span>) =&gt;</span> string
  ) ...</code></span></pre>


<p>The function reads &#8220;Call <code>saveAnythingLocalStorage</code> and pass it the <code>data</code> you want to save, and the <code>encoder</code> function which converts it to a string.&#8221;</p>



<p>Using our existing <code>CowRace</code> encoder above, <code>encodeCowRace</code>, we can call that new function like so:</p>


<pre class="wp-block-code"><span><code class="hljs language-xml">saveAnythingLocalStorage<span class="hljs-tag">&lt;<span class="hljs-name">CowRace</span>&gt;</span>(cowRace, encodeCowRace)</code></span></pre>


<p>We _could_ also do a generic one for <code>JSON.stringify</code> and our <code>Record&lt;string, unknown&gt;</code>, but that&#8217;s &nbsp;not safe given TypeScript thinks <code>JSON.stringify</code> has a return value of <code>string</code>, but we know it&#8217;s actually <code>string | never</code>. However, I&#8217;ve put here anyway so you know how to do it. TypeScript _is_ a gradually typed language after all, so good to get something work first, then make the types strong as you refactor.</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">saveAnythingLocalStorage&lt;Record&lt;string, unknown&gt;&gt;(john, <span class="hljs-built_in">JSON</span>.stringify)</code></span></pre>


<h2 class="wp-block-heading">Step 3: Return Value</h2>



<p>The last step is the return value. Since all of the encoders we&#8217;ve written cannot fail, we&#8217;ll simply return their value, a <code>string</code> which is their encoded representation.</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">type saveAnythingLocalStorage&lt;Type&gt; =
  (
    data:Type,
    <span class="hljs-attr">encoder</span>:<span class="hljs-function">(<span class="hljs-params">convertMe:Type</span>) =&gt;</span> string
  ) =&gt; string</code></span></pre>


<p>The function implementation, regardless of inputs, looks like:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-keyword">const</span> encodedString = encoder(data)
<span class="hljs-keyword">return</span> encodedString</code></span></pre>


<h2 class="wp-block-heading">Final Non-Failing Encoder Result</h2>



<p>Putting it all together, our final happy path code looks like:</p>


<pre class="wp-block-code"><span><code class="hljs language-php">enum CowRace {
  Cow = <span class="hljs-string">'cow'</span>,
  NotACow = <span class="hljs-string">'not a cow'</span>
}

type Encoder = (convertMe:Type) =&gt; string

<span class="hljs-keyword">const</span> encodeCowRace = (cowRace:CowRace):string =&gt; {
  <span class="hljs-keyword">if</span>(cowRace === CowRace.Cow) {
    <span class="hljs-keyword">return</span> JSON.stringify(<span class="hljs-string">'cow'</span>)
  } <span class="hljs-keyword">else</span> {
   <span class="hljs-keyword">return</span> JSON.stringify(<span class="hljs-string">'not a cow'</span>)
  }
}

<span class="hljs-keyword">const</span> saveAnythingLocalStorage = &lt;Type,&gt;(data:Type, encoder:Encoder):string =&gt; {
  <span class="hljs-keyword">const</span> encodedString = encoder(data)
  <span class="hljs-keyword">return</span> encodedString
}

<span class="hljs-keyword">const</span> cowRace = CowRace.Cow
<span class="hljs-keyword">const</span> result = saveAnythingLocalStorage&lt;CowRace&gt;(cowRace, encodeCowRace)
<span class="hljs-comment">// result is: 'cow'</span></code></span></pre>


<h2 class="wp-block-heading">Generic Encoders That Can Fail</h2>



<p>In soundly typed languages, encoders cannot fail. In TypeScript, using <code>JSON.stringify</code> under the hood means they can fail. To make our <code>encodeCowRace</code> safer and the types more accurate, we can change the return value to some type of <code>Either</code>; a type indicating something can fail. The most common in TypeScript regardless of Browser or Node.js server is <code>Promise</code>, and for Angular an <code>Observable</code>. However, both don&#8217;t treat errors as values as well, so we&#8217;ll just make our own for now.</p>



<p>If it works, return the encoded string. If it fails, return the `Error` explaining why it failed:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">type EncodeResult = string | <span class="hljs-built_in">Error</span></code></span></pre>


<p>We&#8217;ll change the encoder that returns a string:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">type EncodeCowRace = <span class="hljs-function">(<span class="hljs-params">cowRace:CowRace</span>) =&gt;</span> string</code></span></pre>


<p>To instead return our Result type:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">type EncodeCowRace = <span class="hljs-function">(<span class="hljs-params">cowRace:CowRace</span>) =&gt;</span> EncodeResult</code></span></pre>


<p>That means, the first part of our <code>encodeCowRace</code> function implementation is just wrapped with a try/catch:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-keyword">try</span> {
  <span class="hljs-keyword">if</span>(cowRace === CowRace.Cow) {
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-string">'cow'</span>)
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-string">'not a cow'</span>)
  }
...</code></span></pre>


<p>The 2nd part, <code>error</code> is typed as <code>unknown</code>, so if it&#8217;s an <code>Error</code>, we&#8217;ll return that, else make a new <code>Error</code> and attempt to convert whatever to error was to a readable string inside it:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-keyword">catch</span>(error:unknown) {
  <span class="hljs-keyword">if</span>(error <span class="hljs-keyword">instanceof</span> <span class="hljs-built_in">Error</span>) {
    <span class="hljs-keyword">return</span> error
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">`unknown encoding error: <span class="hljs-subst">${<span class="hljs-built_in">String</span>(error)}</span>`</span>)
  }
}</code></span></pre>


<p>That means our <code>saveAnythingLocalStorage</code> no longer &#8220;always succeeds&#8221;. So we&#8217;ll change it&#8217;s return type from a <code>string</code>&#8230;:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">type saveAnythingLocalStorage&lt;Type&gt; =
  (
    data:Type,
    <span class="hljs-attr">encoder</span>:<span class="hljs-function">(<span class="hljs-params">convertMe:Type</span>) =&gt;</span> string
  ) =&gt; string</code></span></pre>


<p>To the <code>EncodeResult</code> instead:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">type saveAnythingLocalStorage&lt;Type&gt; =
  (
    data:Type,
    <span class="hljs-attr">encoder</span>:<span class="hljs-function">(<span class="hljs-params">convertMe:Type</span>) =&gt;</span> string
  ) =&gt; EncodeResult</code></span></pre>


<p>Now the types are correct, the function is safe, and the developer can pass in any types they want to safely encode.</p>



<h2 class="wp-block-heading">Final Can-Fail Decoding</h2>



<p>Our final encoding example where the encoding can fail below:</p>


<pre class="wp-block-code"><span><code class="hljs language-php">enum CowRace {
  Cow = <span class="hljs-string">'cow'</span>,
  NotACow = <span class="hljs-string">'not a cow'</span>
}

type EncodeResult = string | Error

type EncoderCanFail = &lt;Type&gt;(convertMe:Type) =&gt; EncodeResult

<span class="hljs-keyword">const</span> encodeCowRace = &lt;CowRace,&gt;(cowRace:CowRace):EncodeResult =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">if</span>(cowRace === CowRace.Cow) {
      <span class="hljs-keyword">return</span> JSON.stringify(<span class="hljs-string">'cow'</span>)
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">return</span> JSON.stringify(<span class="hljs-string">'not a cow'</span>)
    }
  } <span class="hljs-keyword">catch</span>(error:unknown) {
    <span class="hljs-keyword">if</span>(error <span class="hljs-keyword">instanceof</span> Error) {
      <span class="hljs-keyword">return</span> error
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Error(`unknown encoding error: ${String(error)}`)
    }
  }
}

<span class="hljs-keyword">const</span> saveAnythingLocalStorage2 = &lt;Type,&gt;(data:Type, encoder:EncoderCanFail ):EncodeResult =&gt; {
  <span class="hljs-keyword">const</span> encodedStringOrError = encoder(data)
  <span class="hljs-keyword">return</span> encodedStringOrError
}

<span class="hljs-comment">// success is string</span>
<span class="hljs-keyword">const</span> cowRace = CowRace.Cow
<span class="hljs-keyword">const</span> result = saveAnythingLocalStorage2&lt;CowRace&gt;(cowRace, encodeCowRace)
<span class="hljs-comment">// result is: 'cow'</span>

<span class="hljs-comment">// failure is Error</span>
type CowIsCool = { race: CowRace, age: BigInt }
<span class="hljs-keyword">const</span> encodeCowIsCool = &lt;CowIsCool,&gt;(cool:CowIsCool):EncodeResult =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">return</span> JSON.stringify(cool)
  } <span class="hljs-keyword">catch</span>(error) {
    <span class="hljs-keyword">if</span>(error <span class="hljs-keyword">instanceof</span> Error) {
      <span class="hljs-keyword">return</span> error
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Error(`unknown encoding error: ${String(error)}`)
    }
  }
}
<span class="hljs-keyword">const</span> failCow = { race: CowRace.Cow, age: BigInt(<span class="hljs-number">9007199254740991</span>) }
<span class="hljs-keyword">const</span> resultBad = saveAnythingLocalStorage2&lt;CowIsCool&gt;(failCow, encodeCowIsCool)
console.log(<span class="hljs-string">"resultBad:"</span>, resultBad)
<span class="hljs-comment">// BigInt value can't be serialized in JSON </span></code></span></pre>


<h2 class="wp-block-heading">Decoding</h2>



<p>Decoding works the same way as encoding, just in reverse. We give our decoder function a type we want to decode to, and a decoder to parse the string to our type.</p>



<p>Why not simply use <code>JSON.parse</code> and then cast the parsed result using <code>as</code>? A few reasons this is incorrect and dangerous:</p>



<ul class="wp-block-list">
<li><code>JSON.parse</code> can also throw an error</li>



<li>You can get an <code>unknown</code> return value, so you&#8217;ll have to <a href="https://www.typescriptlang.org/docs/handbook/2/narrowing.html">type narrow</a> to your type. (We&#8217;ll avoid doing type narrowing in this post and assume you&#8217;ll use something like <a href="https://zod.dev/">Zod</a> or <a href="https://arktype.io/">ArkType</a> heavily in your decoders).</li>



<li><code>as</code> turns TypeScript type checking off, which is unsafe</li>
</ul>



<p>Let&#8217;s first create a specific decoder, then we&#8217;ll make it generic just like we did for our encoder.</p>



<h2 class="wp-block-heading">Specific Decoder</h2>



<p>Our enum before is <code>CowRace</code>, so our decoder needs to convert a <code>string</code> to a <code>CowRace</code>. However, what if someone passes a string that is not <code>"cow"</code> or <code>"not a cow"</code> such as <code>"bunny"</code> or empty string? We have 2 choices. We can either assume anything that&#8217;s not <code>"cow"</code> is <code>CowRace.NotACow</code>, OR we can return an error.</p>



<p>It may be tempting to just use a default, but this makes it much harder to debug later when many downstream components and UI&#8217;s are getting default data, and you didn&#8217;t expect it to. We want to <a href="https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/">parse, don&#8217;t validate</a>; meaning we want to parse our data, and if it doesn&#8217;t look correct, it should fail vs. &#8220;make an assumption that bites us later which it turns out the data is invalid and we have to backtrack to figure out where our code went wrong&#8221;.</p>



<p>So let&#8217;s type it correctly as a <code>Result</code>: either we got our data and it&#8217;s good, or we got something that is not an encoded <code>CowRace</code> enum.</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">type DecodeResult = CowRace | <span class="hljs-built_in">Error</span></code></span></pre>


<p>Next up is to pass in our decoder, which takes a JSON string, parses it, and attempts to convert it to a <code>CowRace</code> enum.</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">type CowRaceDecoder = <span class="hljs-function">(<span class="hljs-params">jsonString:string</span>) =&gt;</span> DecodeResult</code></span></pre>


<p>The internals look something like this:<br></p>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-keyword">const</span> result = <span class="hljs-built_in">JSON</span>.parse(jsonString)
<span class="hljs-keyword">if</span>(result === <span class="hljs-string">'cow'</span>) {
  <span class="hljs-keyword">return</span> CowRace.Cow
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(result === <span class="hljs-string">'not a cow'</span>) {
  <span class="hljs-keyword">return</span> CowRace.NotACow
} <span class="hljs-keyword">else</span> {
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">`Cannot decode <span class="hljs-subst">${result}</span> to a CowRace.`</span>)}
}</code></span></pre>


<p>That&#8217;s the happy path assuming <code>JSON.parse</code> works. If that fails, such as when <code>localStorage.getItem</code> returns a <code>null</code> value or a malformed JSON string, then we&#8217;ll need to handle that unhappy path as well:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">} <span class="hljs-keyword">catch</span>(error:unknown) {
  <span class="hljs-keyword">if</span>(error <span class="hljs-keyword">instanceof</span> <span class="hljs-built_in">Error</span>) {
    <span class="hljs-keyword">return</span> error
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">`Unknown decoding error: <span class="hljs-subst">${<span class="hljs-built_in">String</span>(error)}</span>`</span>)
  }
}</code></span></pre>


<p>Finally, our <code>CowRace</code> function to read out of local storage and decode it in a type safe way looks something like:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">type readCowRaceFromLocalStorage = <span class="hljs-function">(<span class="hljs-params">decoder:CowRaceDecoder</span>) =&gt;</span> DecodeResult</code></span></pre>


<p>The internals look something like this:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-keyword">const</span> readString:string | <span class="hljs-literal">null</span> = localStorage.getItem(<span class="hljs-string">'cowrace'</span>)
<span class="hljs-keyword">if</span>(readString !== <span class="hljs-literal">null</span>) {
  <span class="hljs-keyword">const</span> decodeResult = decoder(readString)
  <span class="hljs-keyword">return</span> decodeResult
} <span class="hljs-keyword">else</span> {
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'No CowRace encoded data found in localstorage.'</span>)
}</code></span></pre>


<h2 class="wp-block-heading">Generic Decoder</h2>



<p>The above is specific to decoding our <code>CowRace</code> enum, but what if we wanted our local storage decoder to be generic? There are 3 things to make dynamic:</p>



<ol class="wp-block-list">
<li>the key of where we read in local storage</li>



<li>the decoder type has to be generic</li>



<li>the decoder function needs to be updated</li>
</ol>



<p>Let&#8217;s handle those 3 in order. The latter 2 should look familiar from the encoder exercise. We&#8217;ll cover the types first, then we&#8217;ll work on function implementation.</p>



<h2 class="wp-block-heading">Step 1: Key</h2>



<p>The key is just a string, so that type includes it as the first parameter:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">type readAnythingFromLocalStorage = <span class="hljs-function">(<span class="hljs-params">key:string</span>) =&gt;</span> ...</code></span></pre>


<p>That&#8217;d make your internals something like:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-keyword">const</span> readString:string | <span class="hljs-literal">null</span> = localStorage.getItem(key)</code></span></pre>


<h2 class="wp-block-heading">Step 2: Decoder</h2>



<p>Next up is to pass our decoder. However, the return result is _too_ specific:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">type DecodeResult = CowRace | <span class="hljs-built_in">Error</span></code></span></pre>


<p>We need that result to return _any_ type. So let&#8217;s change that first via a type parameter:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-comment">// incorrect</span>
type DecodeResult&lt;Type&gt; = Type | <span class="hljs-built_in">Error</span></code></span></pre>


<p>HOWEVER, that may look right, but sadly, TypeScript unions &#8220;don&#8217;t know the difference&#8221; between the types they&#8217;re unifying if they&#8217;re objects like this. What&#8217;s to say you&#8217;re not passing an <code>Error</code>? Then what is <code>type DecodeResult&lt;Error&gt; = Error | Error</code> saying exactly? Yeah, I don&#8217;t know either.</p>



<p>So let&#8217;s whip out a <a href="https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions">discriminant</a> so TypeScript knows the difference between _our_ generic type, and an Error.</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-comment">// not quite correct</span>
type DecodeResult&lt;Type&gt; = { <span class="hljs-attr">tag</span>: <span class="hljs-string">'success'</span>, <span class="hljs-attr">value</span>: Type } | { <span class="hljs-attr">tag</span>: <span class="hljs-string">'error'</span>, <span class="hljs-attr">error</span>: <span class="hljs-built_in">Error</span> }</code></span></pre>


<p>However, TypeScript may give you a compiler error when attempting to create those types like &#8220;Type &#8216;{ tag: &#8220;success&#8221;; value: CowRace; }&#8217; is not assignable to type &#8216;DecodeResult&lt;Type&gt;&#8217;.&#8221; or &#8220;can&#8217;t assign Type &#8216;{ tag: &#8220;success&#8221;; value: CowRace; }&#8217; is not assignable to type &#8216;DecodeResult&lt;Type&gt;&#8217;. to an Error&#8221;. TypeScript needs help, even with discriminants (our <code>tag</code>) to identify a type. You can&#8217;t always just make a local variable and type it. Creating the types through functions really helps TypeScript:</p>


<pre class="wp-block-code"><span><code class="hljs language-php"><span class="hljs-comment">// almost correct</span>
type DecodeResult&lt;Type&gt; = { tag: <span class="hljs-string">'success'</span>, value: Type } | { tag: <span class="hljs-string">'error'</span>, error: Error }

<span class="hljs-keyword">const</span> DecodeSuccess = &lt;Type,&gt;(value:Type):{ tag: <span class="hljs-string">'success'</span>, value: Type } =&gt;
    ({ tag: <span class="hljs-string">'success'</span>, value })

<span class="hljs-keyword">const</span> DecodeError = (error:Error):{ tag: <span class="hljs-string">'error'</span>, error: Error } =&gt;
    ({ tag: <span class="hljs-string">'error'</span>, error })</code></span></pre>


<p>&#8230; however, those copy pasta&#8217;d anonymous types everywhere are hard to read. You can DRY types just like you can DRY code. You do this by naming your types, just like how you name your variables:</p>


<pre class="wp-block-code"><span><code class="hljs language-php"><span class="hljs-comment">// correct</span>
type DecodeResult&lt;Type&gt; = DecodeSuccess&lt;Type&gt; | DecodeError
type DecodeSuccess&lt;Type&gt; = { tag: <span class="hljs-string">'success'</span>, value: Type }
type DecodeError = { tag: <span class="hljs-string">'error'</span>, error: Error }

<span class="hljs-keyword">const</span> DecodeSuccess = &lt;Type,&gt;(value:Type):DecodeSuccess&lt;Type&gt; =&gt;
    ({ tag: <span class="hljs-string">'success'</span>, value })

<span class="hljs-keyword">const</span> DecodeError = (error:Error):DecodeError =&gt;
    ({ tag: <span class="hljs-string">'error'</span>, error })</code></span></pre>


<p>We&#8217;ll now make the decoder more generic by including that new generic type parameter:</p>


<pre class="wp-block-code"><span><code class="hljs language-xml">type Decoder<span class="hljs-tag">&lt;<span class="hljs-name">Type</span>&gt;</span> = (jsonString:string) =&gt; DecodeResult<span class="hljs-tag">&lt;<span class="hljs-name">Type</span>&gt;</span></code></span></pre>


<p>You can read that as &#8220;If I give you a JSON string, you&#8217;ll either give me the type I&#8217;m expecting back, or an Error&#8221;.</p>



<h2 class="wp-block-heading">Step 3: Return Value</h2>



<p>Now that we&#8217;ve got our return type setup, and the decoder type is now generic as well, let&#8217;s do the same for the read from local storage function&#8217;s 2nd parameter:</p>


<pre class="wp-block-code"><span><code class="hljs language-xml">type readAnythingFromLocalStorage = <span class="hljs-tag">&lt;<span class="hljs-name">Type,</span>&gt;</span>(
key:string,
decoder:Decoder<span class="hljs-tag">&lt;<span class="hljs-name">Type,</span>&gt;</span>
) =&gt; ...</code></span></pre>


<p>And the return value:</p>


<pre class="wp-block-code"><span><code class="hljs language-xml">type readAnythingFromLocalStorage = <span class="hljs-tag">&lt;<span class="hljs-name">Type,</span>&gt;</span>(
key:string,
decoder:Decoder<span class="hljs-tag">&lt;<span class="hljs-name">Type,</span>&gt;</span>
):DecodeResult<span class="hljs-tag">&lt;<span class="hljs-name">Type</span>&gt;</span></code></span></pre>


<h2 class="wp-block-heading">Step 4: Function Implementations</h2>



<p>We have 2 functions to write: our CowRace decoder, and our generic &#8220;read anything from localStorage&#8221;.</p>



<p>While our CowRace decoder is returning a specific type, it&#8217;s still using the generic type we defined above. The signature looks something like:</p>


<pre class="wp-block-code"><span><code class="hljs language-php"><span class="hljs-keyword">const</span> decodeCowRace = (json:string):DecodeResult&lt;CowRace&gt; =&gt; {</code></span></pre>


<p>Notice how we specify <code>CowRace</code> in the <code>DecodeResult</code>&#8216;s first type parameter; it can be generic, so we&#8217;re like &#8220;Cool, we&#8217;ll return a <code>DecodeResult</code> with a <code>CowRace</code> inside it&#8221;.</p>



<p>Now let&#8217;s parse our Enum from a string. Since we cannot gurentee the string read from an external localStorage is _our_ only 2 available enum strings, and  <code>JSON.parse</code> are both dangerous operations, we&#8217;ll wrap in a try/catch:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> value = <span class="hljs-built_in">JSON</span>.parse(json)
    <span class="hljs-keyword">if</span>(value === <span class="hljs-string">'cow'</span>) {
        <span class="hljs-keyword">return</span> DecodeSuccess&lt;CowRace&gt;(CowRace.Cow)
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(value === <span class="hljs-string">'not a cow'</span>) {
        <span class="hljs-keyword">return</span> DecodeSuccess&lt;CowRace&gt;(CowRace.NotACow)
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">return</span> DecodeError(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">`Cannot decode <span class="hljs-subst">${result}</span> to a CowRace.`</span>))
    }
}</code></span></pre>


<p>That&#8217;ll handle getting our data,but if the <code>JSON.parse</code> throws, let&#8217;s handle the error:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">} <span class="hljs-keyword">catch</span>(error) {
  <span class="hljs-keyword">if</span>(error <span class="hljs-keyword">instanceof</span> <span class="hljs-built_in">Error</span>) {
    <span class="hljs-keyword">return</span> DecodeError(error)
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">return</span> DecodeError(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">`Unknown decode error: <span class="hljs-subst">${<span class="hljs-built_in">String</span>(error)}</span>`</span>))
  }
}</code></span></pre>


<p>That handles our decoder. Now let&#8217;s create the generic <code>readAnythingFromLocalStorage</code>:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-keyword">const</span> encodedString = localStorage.getItem(key)
<span class="hljs-keyword">if</span>(encodedString !== <span class="hljs-literal">null</span>) {
  <span class="hljs-keyword">return</span> decoder(encodedString)
} <span class="hljs-keyword">else</span> {
  <span class="hljs-keyword">return</span> DecodeError(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Failed to find key in local storage'</span>))</code></span></pre>


<h2 class="wp-block-heading">Final Decoder Code</h2>



<p>The final code for decoder is as follows:</p>


<pre class="wp-block-code"><span><code class="hljs language-php">enum CowRace {
  Cow = <span class="hljs-string">'cow'</span>,
  NotACow = <span class="hljs-string">'not a cow'</span>
}

type DecodeResult&lt;Type&gt; = DecodeSuccess&lt;Type&gt; | DecodeError
type DecodeSuccess&lt;Type&gt; = { tag: <span class="hljs-string">'success'</span>, value: Type }
type DecodeError = { tag: <span class="hljs-string">'error'</span>, error: Error }

<span class="hljs-keyword">const</span> DecodeSuccess = &lt;Type,&gt;(value:Type):DecodeSuccess&lt;Type&gt; =&gt;
    ({ tag: <span class="hljs-string">'success'</span>, value })

<span class="hljs-keyword">const</span> DecodeError = (error:Error):DecodeError =&gt;
    ({ tag: <span class="hljs-string">'error'</span>, error })

type Decoder&lt;Type&gt; = (jsonString:string) =&gt; DecodeResult&lt;Type&gt;

<span class="hljs-keyword">const</span> decodeCowRace = (json:string):DecodeResult&lt;CowRace&gt; =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> value = JSON.parse(json)
    <span class="hljs-keyword">if</span>(value === <span class="hljs-string">'cow'</span>) {
        <span class="hljs-keyword">return</span> DecodeSuccess&lt;CowRace&gt;(CowRace.Cow)
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(value === <span class="hljs-string">'not a cow'</span>) {
        <span class="hljs-keyword">return</span> DecodeSuccess&lt;CowRace&gt;(CowRace.NotACow)
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">return</span> DecodeError(<span class="hljs-keyword">new</span> Error(`Cannot decode ${result} to a CowRace.`))
    }
  } <span class="hljs-keyword">catch</span>(error) {
    <span class="hljs-keyword">if</span>(error <span class="hljs-keyword">instanceof</span> Error) {
      <span class="hljs-keyword">return</span> DecodeError(error)
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">return</span> DecodeError(<span class="hljs-keyword">new</span> Error(`Unknown decode error: ${String(error)}`))
    }
  }
}

<span class="hljs-keyword">const</span> readAnythingFromLocalStorage = &lt;Type,&gt;(
key:string,
decoder:Decoder&lt;Type&gt;
):DecodeResult&lt;Type&gt; =&gt; {
  <span class="hljs-keyword">const</span> encodedString = localStorage.getItem(key)
  <span class="hljs-keyword">if</span>(encodedString !== <span class="hljs-keyword">null</span>) {
    <span class="hljs-keyword">return</span> decoder(encodedString)
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">return</span> DecodeError(<span class="hljs-keyword">new</span> Error(<span class="hljs-string">'Failed to find key in local storage'</span>))
  }
}

localStorage.setItem(<span class="hljs-string">'cowrace'</span>, JSON.stringify(<span class="hljs-string">'cow'</span>))
<span class="hljs-keyword">const</span> result = readAnythingFromLocalStorage&lt;CowRace&gt;(<span class="hljs-string">'cowrace'</span>,decodeCowRace)
<span class="hljs-comment">// 'Cow'</span>

localStorage.clear()
<span class="hljs-keyword">const</span> result2 = readAnythingFromLocalStorage&lt;CowRace&gt;(<span class="hljs-string">'cowrace'</span>,decodeCowRace)
<span class="hljs-comment">// Error: Failed to find key in local storage</span></code></span></pre>


<h2 class="wp-block-heading">Note: &lt; Type, &gt; vs &lt; Type &gt;</h2>



<p>You have noticed in some of the types above, we used a <code>&lt;Type,&gt;</code> instead of a <code>&lt;Type&gt;</code>. For old school JavaScript functions, you can use the type parameters like so:</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span>&lt;<span class="hljs-title">Type</span>&gt; <span class="hljs-title">nameOfFunction</span>(<span class="hljs-params">...</span>)</span></code></span></pre>


<p>However, for Arrow functions, that syntax currently <a href="https://stackoverflow.com/questions/32308370/what-is-the-syntax-for-typescript-arrow-functions-with-generics">fails to parse in TypeScript</a>.<br></p>


<pre class="wp-block-code"><span><code class="hljs language-php">type func&lt;Type&gt; = () =&gt; void <span class="hljs-comment">// works</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> &lt;<span class="hljs-title">Type</span>&gt;<span class="hljs-params">()</span> </span>{} <span class="hljs-comment">// works</span>
func = &lt;Type&gt;() =&gt; undefined <span class="hljs-comment">// fails</span>
func = &lt;Type,&gt;() =&gt; undefined <span class="hljs-comment">// works</span></code></span></pre>


<p><br>The parameters and return value are fine, it&#8217;s just the initial type parameter part in the front. There are a few options such as having the 1st parameter extends unknown, but mixing inheritance and type parameters doesn&#8217;t make much since and is a lot more to read. Types are hard enough to read, and TypeScript types are quite verbose, so anything you can do to shrink it helps.</p>



<h2 class="wp-block-heading">Conclusions</h2>



<p>As you can see, encoding and decoding in TypeScript can make your code safer, reduce the amount of <a href="https://www.typescriptlang.org/docs/handbook/2/narrowing.html">type narrowing</a> you need to do, especially if you use <a href="https://zod.dev/">Zod</a>. For consumers of your code and api&#8217;s, it gives them the flexibility of utilizing your API&#8217;s while providing their own types which includes their own encoders and decoders in a type-safe way. TypeScript can safely ensure all erros are typesafe, and those error scenarios are handled in developers who use your code.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
