<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Ohans Emmanuel's Blog]]></title><description><![CDATA[Technical articles, simply explained.]]></description><link>https://blog.ohansemmanuel.com/</link><image><url>https://blog.ohansemmanuel.com/favicon.png</url><title>Ohans Emmanuel&apos;s Blog</title><link>https://blog.ohansemmanuel.com/</link></image><generator>Ghost 5.2</generator><lastBuildDate>Fri, 10 Apr 2026 20:28:32 GMT</lastBuildDate><atom:link href="https://blog.ohansemmanuel.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Before Writing Code with AI]]></title><description><![CDATA[<p>How we write code has fundamentally changed with AI in the mix &#x2026; blah blah blah.</p><p>We&#x2019;ve all heard that.</p><p>So, I&#x2019;ll jump straight to the point.</p><p>There are lots of ways to code with AI, and a lot of new things to learn, but this</p>]]></description><link>https://blog.ohansemmanuel.com/before-writing-code-with-ai/</link><guid isPermaLink="false">6969cfa17ed2f005e4cd0d69</guid><category><![CDATA[AI]]></category><dc:creator><![CDATA[Ohans Emmanuel]]></dc:creator><pubDate>Fri, 16 Jan 2026 05:43:04 GMT</pubDate><media:content url="https://blog.ohansemmanuel.com/content/images/2026/01/before_writing_code_with_ai-1.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.ohansemmanuel.com/content/images/2026/01/before_writing_code_with_ai-1.jpg" alt="Before Writing Code with AI"><p>How we write code has fundamentally changed with AI in the mix &#x2026; blah blah blah.</p><p>We&#x2019;ve all heard that.</p><p>So, I&#x2019;ll jump straight to the point.</p><p>There are lots of ways to code with AI, and a lot of new things to learn, but this article will focus on the one thing I think is the most important foundation.</p><h2 id="sharp-or-dull">Sharp or dull</h2><p>You&#x2019;ve likely heard a variation of the following story before.</p><p>A general goes to war with his army, but all his army&#x2019;s got are spears.</p><p>A salesman approaches the general with a machine gun, but the general is quick to dismiss him.</p><blockquote>I don&#x2019;t have time to talk to a salesman ...</blockquote><p>Well, that&#x2019;s seemingly silly.</p><p>The machine gun would have won him the war.</p><p>This analogy is apt when it comes to coding with AI. A lot of us (from my experience) plunge into writing code with LLMs without talking to the salesman.</p><p>Why not?</p><h2 id="the-salesman">The Salesman</h2><p>I&#x2019;ve got nothing to sell you in this short write-up.</p><p>Analogous to the salesman is spending time to configure your AI agent to output code like YOU.</p><p>I didn&#x2019;t say to get an LLM to work better.</p><p>No.</p><p>To output code like you.</p><p>That&#x2019;s the point.</p><p>You can&#x2019;t expect a probabilistic machine trained on the code written by millions of developers to magically understand how YOU write code.</p><p>There&#x2019;s a very low probability of that happening, the more opinionated you are about writing quality software.</p><p>So, your taste, your idiosyncrasies matter (I like to order module imports by length, for example - weird, I know, but that&#x2019;s me.)</p><p>Otherwise, you spend more time tweaking and editing the output code. A bigger waste of time.</p><h2 id="cursor-claude-code-gemini-%E2%80%A6-doesn%E2%80%99t-matter">Cursor, Claude Code, Gemini &#x2026; Doesn&#x2019;t matter</h2><p>We won&#x2019;t get into a battle of what&#x2019;s best.</p><p>I personally don&#x2019;t care. I have my favourites, but it doesn&#x2019;t matter right now.</p><p>I find myself using different tools at different times.</p><p>What matters?</p><p>Configuring them to work like you.</p><h2 id="here%E2%80%99s-what-to-do">Here&#x2019;s what to do</h2><p>Every tool&#x2019;s got its flavour of some document you can pass to the agent to tailor it to you.</p><p>The biggest mistake you can make is treat this as optional.</p><p>It shouldn&#x2019;t be an optional requirement.</p><p>At least not if you&#x2019;re serious about your time - LLMs outputting code I spend hours reviewing and could have been faster doing on my own, is the equivalent of &#x201C;you name it&#x201D;.</p><p>I call it a massive waste of time.</p><p>Now, find that document: <code>AGENTS.md</code>, <code>CLAUDE.md</code>, <code>Cursor rules</code>, whatever it is, and spend time working on it.</p><p>Remember spears and machine guns?</p><p>Don&#x2019;t squint at the idea of spending time to work on something that&#x2019;ll drastically improve your productivity.</p><h3 id="how-to-actually-write-your-instructions">How to actually write your instructions</h3><p>First things first, I&#x2019;ll assume at some point you&#x2019;ve written code you&#x2019;re proud of, or that signifies how you want or like to write code/structure programs.</p><p>Or perhaps you have strong opinions on how programs should be structured.</p><p>Pick an advanced thinking model. Damn the cost at this point.</p><p>Use any dictation app if you wanna be faster (you can also write), and tell it to do the following:</p><pre><code class="language-md">Please take a look at the attached file(s). It represents the implementation of a complex [XXX] feature. 

Your task is to codify the principles under which [XXX] has been built into a short document, less than 200 lines, that any AI agent can follow to replicate the method here. 

Now, this is extremely important because not following these principles is a waste of time. I want an AI agent, especially when asked to develop [XXX], to follow these principles to the T. 

So, here are some examples of the rules that have been followed in the attached directory.I&apos;m going to tell you how I approached things, your task is to codify them. You take the information I give you now, you look through the code files, and you codify the principles.


# [Comment: Now I dictate some of the principles. To bias the model into looking at specific things]


The first, and perhaps the most important thing [....]

Now another point is [...]

Now beyond that [...]

Take the principles I&apos;ve explained, look through the implementation in the file(s) one more time, and codify these principles in a .md file that should live in [XXX]. Please be detailed, but remember to be concise. These are rules that should be codified accurately without vague or unnecessary sentences.
</code></pre><p>This prompt is not overly optimised. You could come up with something better; this is just what I dictated this morning (about a week ago, before publishing this) to write some new rules.</p><p>The most important thing is to codify your way of working.</p><h3 id="keep-sharpening">Keep sharpening</h3><p>For me <code>[XXX]</code> in the prompt was about frontend engineering. It&#x2019;s the branch of software engineering where I have the strongest opinions and also where I think AI falls short or is too bland for my taste.</p><p>After creating the first document, if you&#x2019;ve used an advanced thinking model as prescribed, you should have a great starting point.</p><p>Treat it as that. A good starting point.</p><p>Give it a good read, edit, and be content with it.</p><p>It should represent you. Your taste.</p><p>Now every time you ask an AI agent to do <code>[XXX]</code>, when its done, you review the code (as I hope you do, regardless)</p><p>During review, you&#x2019;ll find things missed.</p><p>This is where you correct by updating the doc.</p><p>I typically write a prompt like <code>Why did you do YYY instead of as described in the doc JJJ</code>, this gives you insight into what wasn&#x2019;t clear and a precise pointer for what to update.</p><p>It&#x2019;s common for different models to act in different ways.</p><p>The best-case scenario is your doc works perfectly for all models.</p><p>However, if it doesn&#x2019;t, instead of creating different docs, I personally prefer keeping things simple.</p><p>One doc, shared across different tools, even if it means adding more info to the doc so it&#x2019;s clearer to a less capable model I frequently use.</p><h3 id="whatever-your-guidelines-make-sure-it-includes-%E2%80%A6">Whatever your guidelines make sure it includes &#x2026;</h3><pre><code class="language-md">When in doubt, ask questions and await my approval before continuing. Do not assume. Simply ask me, and I&apos;ll confirm what to do
</code></pre><p>The above will save you a lot of time and cost in wasted tokens.</p><h2 id="conclusion">Conclusion</h2><p>Before doing what I wrote here, I&#x2019;d initially copied some general instructions from other engineers on Twitter&#x2026; this is a mistake.</p><p>You can use those as a starting point, but don&#x2019;t make the mistake of not making it yours.</p><p>There&#x2019;ll be a lot to learn/unlearn in the next few years as software engineers.</p><p>Regardless, the foundation of it will be getting AI to output software like YOU.</p><p>Until it all becomes pointless, if we ever get there.</p>]]></content:encoded></item><item><title><![CDATA[The Typescript feature we all wanted]]></title><description><![CDATA[<hr><p><strong>TLDR: </strong>Inferred Type Predicates are now available in Typescript 5.5 Beta. In this article, I&#x2019;ll explain the much-awaited feature and how it&#x2019;ll help you write more type-safe code.</p><hr><h2 id="introduction">Introduction</h2><p>For the most part, Typescript&#x2019;s control flow analysis does an impressive job of monitoring</p>]]></description><link>https://blog.ohansemmanuel.com/the-typescript-feature-we-all-wanted/</link><guid isPermaLink="false">663db5cf7ed2f005e4cd0bb5</guid><dc:creator><![CDATA[Ohans Emmanuel]]></dc:creator><pubDate>Fri, 10 May 2024 06:00:54 GMT</pubDate><media:content url="https://blog.ohansemmanuel.com/content/images/2024/05/blog-ts-loved-feature-1.png" medium="image"/><content:encoded><![CDATA[<hr><img src="https://blog.ohansemmanuel.com/content/images/2024/05/blog-ts-loved-feature-1.png" alt="The Typescript feature we all wanted"><p><strong>TLDR: </strong>Inferred Type Predicates are now available in Typescript 5.5 Beta. In this article, I&#x2019;ll explain the much-awaited feature and how it&#x2019;ll help you write more type-safe code.</p><hr><h2 id="introduction">Introduction</h2><p>For the most part, Typescript&#x2019;s control flow analysis does an impressive job of monitoring how the type of a variable changes as it moves through your code.</p><p>For example, consider the trivial function that receives either a <code>string</code> or <code>number</code> below:</p><pre><code class="language-html">function echo (val: string| number) {
  if (typeof val === &apos;string&apos;) {
    console.log(`STRING: ${val}`)
    return 
  }

  console.log(val)
}
</code></pre><p>In this basic example, Typescript can conveniently tell that the variable on line 3 is of type <code>string</code> and the variable on line 7 of type <code>number</code>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2024/05/CleanShot-2024-05-10-at-06.37.36.png" class="kg-image" alt="The Typescript feature we all wanted" loading="lazy"><figcaption>Correct string type inference</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2024/05/CleanShot-2024-05-10-at-06.37.44.png" class="kg-image" alt="The Typescript feature we all wanted" loading="lazy"><figcaption>Correct number type inference</figcaption></figure><p>The reason this is important is that Typescript can spot bugs based on this inferred type, i.e., if we used a wrong string method in line 7, we&#x2019;d get an error.</p><figure class="kg-card kg-image-card"><img src="https://blog.ohansemmanuel.com/content/images/2024/05/CleanShot-2024-05-10-at-06.39.18.png" class="kg-image" alt="The Typescript feature we all wanted" loading="lazy"></figure><p>This is wonderful.</p><p>Let&#x2019;s now consider a case where Typescript has historically failed.</p><h2 id="what-the-ts">What the TS??</h2><p>Consider this perfectly written code:</p><pre><code class="language-html">const values = [&quot;Hello&quot;, &quot;World&quot;, null, undefined, &quot;Hooray&quot;]

const filteredValues = values.filter((v) =&gt; typeof v === &apos;string&apos;)
</code></pre><p>From a developer&#x2019;s perspective, this reads nicely.</p><ul><li>We have a list of values containing either strings or <code>null</code> and <code>undefined</code></li><li>We <code>filter</code> out <code>null</code> and <code>undefined</code> so we can work with the remaining string values</li></ul><p>However, Typescript is confused.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2024/05/CleanShot-2024-05-10-at-07.41.41.png" class="kg-image" alt="The Typescript feature we all wanted" loading="lazy"><figcaption>Typescript error: &apos;v&apos; is possibly &apos;null&apos; or &apos;undefined&apos;</figcaption></figure><p>The inferred type of <code>filteredValues</code> is still <code>(string | null | undefined)[]</code> even though we explicitly filtered <code>null</code> and <code>undefined</code></p><p>If you&#x2019;ve battled this behaviour in the past, you&#x2019;re not alone.</p><p>While there are workarounds to this, from a Typescript control flow analysis perspective, this is code that should simply work out of the box.</p><h2 id="inferred-type-predicates">Inferred Type Predicates</h2><p>When I spoke about workarounds, one way to fix this would be to write a type predicate.</p><p>A type predicate is really just a function that returns a boolean and is used to narrow down types.</p><p>OK, back to the subject of discussion. Why should we need workarounds in the first place?</p><p>Well, there&#x2019;s good news. From Typescript 5.5 Beta, Typescript will now infer type predicates out of the box!</p><p>In practical terms, this means the code we wrote earlier now works out of the box. No workarounds, no hacks, no nothing.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2024/05/CleanShot-2024-05-10-at-07.44.14.png" class="kg-image" alt="The Typescript feature we all wanted" loading="lazy"><figcaption>Inferred type predicates at work &#x1F389;</figcaption></figure><p>No errors!</p><h2 id="conclusion">Conclusion</h2><p>This seemingly simple change has been seven years in the making. Yes, seven &#x2014; well, since the <a href="https://github.com/microsoft/TypeScript/issues/16069">initial issue</a> was open.</p><p>Typescript 5.5 beta ships with plenty of changes, but none excites me as much as the humble inferred type predicates. Every so often, it&#x2019;s the simple things that make a difference. At least in my case.</p><hr><p>=============== SHAMELESS PLUG &#x2B07;&#xFE0F; &#xA0; ===============</p><p>I&#x2019;m launching a passion project soon and would appreciate your genuine feedback and support. Please hit &#xAB;notify me&#xBB; here: <a href="https://www.producthunt.com/products/bestregards">https://www.producthunt.com/products/bestregards</a></p><p>=============== &#xA0; =============== &#xA0; ===============</p>]]></content:encoded></item><item><title><![CDATA[How to Build a Notion-style editor with AI-powered autocompletion]]></title><description><![CDATA[<p><strong>TLDR</strong>: In this article, we will explore a high-level design of <a href="https://potion.bestregards.me/" rel="noopener">Potion</a> &#x2014;a Notion-style email builder with AI-powered autocompletion. Also, I just launched <a href="https://www.producthunt.com/posts/potion-4" rel="noopener">Potion on Producthunt</a>.</p><p>Let&#x2019;s get started.</p><h4 id="requirements">Requirements</h4><p>Rich-text editors come in all shapes and forms e. g., editors in apps like Substack, Medium and</p>]]></description><link>https://blog.ohansemmanuel.com/how-to-build-a-notion-style-editor-with-ai-powered-autocompletion/</link><guid isPermaLink="false">660e0bad7ed2f005e4cd0b6a</guid><dc:creator><![CDATA[Ohans Emmanuel]]></dc:creator><pubDate>Thu, 04 Apr 2024 02:16:17 GMT</pubDate><media:content url="https://blog.ohansemmanuel.com/content/images/2024/04/ph_feature_image_5@3x-1.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.ohansemmanuel.com/content/images/2024/04/ph_feature_image_5@3x-1.png" alt="How to Build a Notion-style editor with AI-powered autocompletion"><p><strong>TLDR</strong>: In this article, we will explore a high-level design of <a href="https://potion.bestregards.me/" rel="noopener">Potion</a> &#x2014;a Notion-style email builder with AI-powered autocompletion. Also, I just launched <a href="https://www.producthunt.com/posts/potion-4" rel="noopener">Potion on Producthunt</a>.</p><p>Let&#x2019;s get started.</p><h4 id="requirements">Requirements</h4><p>Rich-text editors come in all shapes and forms e. g., editors in apps like Substack, Medium and even Notion.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2024/04/image.png" class="kg-image" alt="How to Build a Notion-style editor with AI-powered autocompletion" loading="lazy" width="1049" height="618" srcset="https://blog.ohansemmanuel.com/content/images/size/w600/2024/04/image.png 600w, https://blog.ohansemmanuel.com/content/images/size/w1000/2024/04/image.png 1000w, https://blog.ohansemmanuel.com/content/images/2024/04/image.png 1049w" sizes="(min-width: 720px) 720px"><figcaption>Modern rich editors</figcaption></figure><p>But let&#x2019;s take these one step further and create an editor not simply capable of text inputs, but includes interactive nodes and exports to valid email HTML.</p><ul><li>The editor must be capable of supporting text (without any character limits)</li><li>The editor must support rich content such as images</li><li>The editor must support email template nodes such as buttons and dividers</li><li>The editor must be able to support layout columns e.g., a 2-column or 3-column layout</li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-images-1.medium.com/max/800/1*ZCROIMELbvCuAlvPpzSfWg.png" class="kg-image" alt="How to Build a Notion-style editor with AI-powered autocompletion" loading="lazy"><figcaption>A summary of the requirements</figcaption></figure><p>Here&#x2019;s an example of Potion with the Netflix email template:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-images-1.medium.com/max/800/1*l1peYMEGV0YG23ViQZkcsw.gif" class="kg-image" alt="How to Build a Notion-style editor with AI-powered autocompletion" loading="lazy"><figcaption>Editing a Netflix template with&#xA0;Potion</figcaption></figure><h4 id="high-level-design">High-level design</h4><p>I&#x2019;ll skip the overall system design (databases, API gateway, edge middleware etc.) and focus solely on what goes on in the clients:</p><h4 id="1-the-potion-editor"><strong>1. The Potion Editor</strong></h4><p>The main components of our editor include a global state and associating view and model.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2024/04/image-2.png" class="kg-image" alt="How to Build a Notion-style editor with AI-powered autocompletion" loading="lazy" width="1690" height="1190" srcset="https://blog.ohansemmanuel.com/content/images/size/w600/2024/04/image-2.png 600w, https://blog.ohansemmanuel.com/content/images/size/w1000/2024/04/image-2.png 1000w, https://blog.ohansemmanuel.com/content/images/size/w1600/2024/04/image-2.png 1600w, https://blog.ohansemmanuel.com/content/images/2024/04/image-2.png 1690w" sizes="(min-width: 720px) 720px"><figcaption>The Potion editor</figcaption></figure><p>To parse the editor state and render valid email output, the entire content of the editor is powered by a very strict schema (model) that enforces what&#x2019;s possible and visible to a user (the view).</p><p>For a rich extensible headless editor, I chose Tiptap.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2024/04/image-3.png" class="kg-image" alt="How to Build a Notion-style editor with AI-powered autocompletion" loading="lazy" width="1400" height="735" srcset="https://blog.ohansemmanuel.com/content/images/size/w600/2024/04/image-3.png 600w, https://blog.ohansemmanuel.com/content/images/size/w1000/2024/04/image-3.png 1000w, https://blog.ohansemmanuel.com/content/images/2024/04/image-3.png 1400w" sizes="(min-width: 720px) 720px"><figcaption>Leveraging Tiptap</figcaption></figure><h4 id="2-the-potion-renderer"><strong>2. The Potion Renderer</strong></h4><p>When a user previews the editor content, they must get the same visual representation but an HTML output. To be specific, XHTML 1.0 (the markup supported in most email clients)</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-images-1.medium.com/max/800/1*4GIMb7eHzIQXmC5yxHUJ0A.gif" class="kg-image" alt="How to Build a Notion-style editor with AI-powered autocompletion" loading="lazy"><figcaption>The Potion&#xA0;renderer</figcaption></figure><p>The potion renderer takes an object representation of the editor state, walks the entire tree and maps each view item to corresponding email-compatible markup while keeping the same styles and output.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2024/04/image-4.png" class="kg-image" alt="How to Build a Notion-style editor with AI-powered autocompletion" loading="lazy" width="1727" height="873" srcset="https://blog.ohansemmanuel.com/content/images/size/w600/2024/04/image-4.png 600w, https://blog.ohansemmanuel.com/content/images/size/w1000/2024/04/image-4.png 1000w, https://blog.ohansemmanuel.com/content/images/size/w1600/2024/04/image-4.png 1600w, https://blog.ohansemmanuel.com/content/images/2024/04/image-4.png 1727w" sizes="(min-width: 720px) 720px"><figcaption>From Potion renderer to email markup</figcaption></figure><p>If you strip away the custom code for walking the tree and other business logic, the email-compatible components are provided by react-email.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2024/04/image-5.png" class="kg-image" alt="How to Build a Notion-style editor with AI-powered autocompletion" loading="lazy" width="988" height="642" srcset="https://blog.ohansemmanuel.com/content/images/size/w600/2024/04/image-5.png 600w, https://blog.ohansemmanuel.com/content/images/2024/04/image-5.png 988w" sizes="(min-width: 720px) 720px"><figcaption>Leveraging React email for email markup render</figcaption></figure><p>In a nutshell, the potion renderer could be summarised as <strong>state (object) + theme (object) = React Fragment nodes [] </strong>eventually rendered to HTML by react-email.</p><p>When you bring it all together, you have a combination of two main components.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2024/04/image-6.png" class="kg-image" alt="How to Build a Notion-style editor with AI-powered autocompletion" loading="lazy" width="1329" height="345" srcset="https://blog.ohansemmanuel.com/content/images/size/w600/2024/04/image-6.png 600w, https://blog.ohansemmanuel.com/content/images/size/w1000/2024/04/image-6.png 1000w, https://blog.ohansemmanuel.com/content/images/2024/04/image-6.png 1329w" sizes="(min-width: 720px) 720px"><figcaption>The major components of Potion</figcaption></figure><blockquote><strong>Shameless plug: Support </strong><a href="https://www.producthunt.com/posts/potion-4" rel="noopener">Potion on Producthunt</a></blockquote><h4 id="worthy-mentions">Worthy mentions</h4><p>If we zoom into one of the editor components (view), our design must include custom nodes such as buttons, images, layout grids etc.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-images-1.medium.com/max/800/1*YTE5knTos3XbGPv8j-EgyQ.gif" class="kg-image" alt="How to Build a Notion-style editor with AI-powered autocompletion" loading="lazy"><figcaption>Custom Potion&#xA0;nodes</figcaption></figure><p>These aren&#x2019;t supported natively by Tiptap. However, the underlying editor used by Tiptap, prosemirror, supports plugins&#x200A;&#x2014;&#x200A;there&#x2019;s also an associated API within Tiptap.</p><p>This means we could define custom schemas and associating nodes.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2024/04/image-7.png" class="kg-image" alt="How to Build a Notion-style editor with AI-powered autocompletion" loading="lazy" width="1400" height="861" srcset="https://blog.ohansemmanuel.com/content/images/size/w600/2024/04/image-7.png 600w, https://blog.ohansemmanuel.com/content/images/size/w1000/2024/04/image-7.png 1000w, https://blog.ohansemmanuel.com/content/images/2024/04/image-7.png 1400w" sizes="(min-width: 720px) 720px"><figcaption>Custom nodes</figcaption></figure><h4 id="what-about-ai-autocompletion">What about AI autocompletion?</h4><p>AI&#x2019;s all the rage these days, and for good reasons.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-images-1.medium.com/max/800/1*NUZjnp-lLctlZO4hwmGx8g.gif" class="kg-image" alt="How to Build a Notion-style editor with AI-powered autocompletion" loading="lazy"><figcaption>AI autocompletion in&#xA0;Potion</figcaption></figure><p>Except you&#x2019;re fine-tuning an open-source model or creating a custom model, creating streaming UI interfaces isn&#x2019;t a complex feat.</p><p>The easiest way to build this as a React developer is to use the <strong>Vercel AI SDK</strong>.</p><p>This is how Potion works under the hood as well.</p><figure class="kg-card kg-image-card kg-width-full kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2024/04/ph_feature_image_1@3x-1.png" class="kg-image" alt="How to Build a Notion-style editor with AI-powered autocompletion" loading="lazy" width="2000" height="1197" srcset="https://blog.ohansemmanuel.com/content/images/size/w600/2024/04/ph_feature_image_1@3x-1.png 600w, https://blog.ohansemmanuel.com/content/images/size/w1000/2024/04/ph_feature_image_1@3x-1.png 1000w, https://blog.ohansemmanuel.com/content/images/size/w1600/2024/04/ph_feature_image_1@3x-1.png 1600w, https://blog.ohansemmanuel.com/content/images/size/w2400/2024/04/ph_feature_image_1@3x-1.png 2400w"><figcaption><a href="https://potion.bestregards.me/">https://potion.bestregards.me/</a></figcaption></figure><h4 id="conclusion"><strong>Conclusion</strong></h4><p>There are many interesting technical challenges with building such an editor. I could write a series of blog posts on this. Ultimately, this was fun to build! </p><p><strong>P.S.: </strong>Check out <a href="https://www.producthunt.com/posts/potion-4" rel="noopener">Potion on Producthunt</a>.</p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[Astro View Transitions by Examples]]></title><description><![CDATA[<p>View Transitions unlocks native browser transition effects between pages for Astro 3.0 using new platform APIs. The good part? Astro is the first major web framework to mainstream View Transitions, and this article will get you started with the new API.<br></p><h2 id="getting-started">Getting started &#xA0;</h2><p>Let&#x2019;s explore Astro</p>]]></description><link>https://blog.ohansemmanuel.com/astro-view-transitions-2/</link><guid isPermaLink="false">653404d543e529f14fb26563</guid><dc:creator><![CDATA[Ohans Emmanuel]]></dc:creator><pubDate>Sun, 22 Oct 2023 14:44:59 GMT</pubDate><media:content url="https://blog.ohansemmanuel.com/content/images/2023/10/a.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.ohansemmanuel.com/content/images/2023/10/a.png" alt="Astro View Transitions by Examples"><p>View Transitions unlocks native browser transition effects between pages for Astro 3.0 using new platform APIs. The good part? Astro is the first major web framework to mainstream View Transitions, and this article will get you started with the new API.<br></p><h2 id="getting-started">Getting started &#xA0;</h2><p>Let&#x2019;s explore Astro view transitions with a starter project. Run the following command:<br></p><pre><code class="language-bash">git clone https://github.com/understanding-astro/astro-view-transitions-starter.git &amp;&amp; cd astro-view-transitions-starter &amp;&amp; npm install &amp;&amp; npm start</code></pre><p><br>This will:</p><ol><li>Clone a starter Astro project</li><li>Install all relevant dependencies</li><li>Start the application</li></ol><p><br>Open the local port (localhost:4321) and view the starter application. Make sure to transition between the home page (with the card list) and the main article page, as shown below:<br></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://paper-attachments.dropboxusercontent.com/s_A370C11DD96CE3152AFCEC6F072FABA36221DB09B7DD60A3E5FAF03720283DC3_1696139841475_CleanShot+2023-10-01+at+07.48.54.gif" class="kg-image" alt="Astro View Transitions by Examples" loading="lazy"><figcaption>Transitioning between the home (list) and main article page.&#xA0;</figcaption></figure><p><br>What we have here is a standard navigation between pages in Astro. Let&#x2019;s now add some view transitions.</p><h2 id="two-ways-to-automatically-include-view-transitions">Two ways to automatically include view transitions</h2><p>Astro view transitions kick in when navigating from one page to another. When you click a link on the origin page, Astro intercepts the click, prevents the default routing behaviour, and provides the transition effect from the origin page to the destination page.</p><p><br>To enable view transitions in Astro, we import and render the ViewTransitions component in the head of an Astro page.</p><p><br><code>ViewTransitions</code> is responsible for adding a client script to your origin page that intercepts clicks to other pages and there are two ways to leverage <code>ViewTransitions</code>.</p><h3 id="option-1-lone-page-view-transitions"><br>Option 1: Lone page view transitions</h3><p>If you need to add view transitions to a single page or between specific pages, go ahead and render ViewTransitions in the head of the specific pages:<br></p><pre><code class="language-astro">
// some-page.astro 
---
import { ViewTransitions } from &quot;astro:transitions&quot;;
---

&lt;head&gt;
  &lt;ViewTransitions /&gt;
&lt;/head&gt;

// ... </code></pre><p><br>Note that you must add <code>ViewTransitions</code> to the <code>&lt;head&gt;</code> of the origin and destination pages.</p><h3 id="option-2-full-site-view-transitions"><br>Option 2: Full-site view transitions</h3><p>To add view transitions to every page in your application, render <code>ViewTransitions</code> within the <code>&lt;head&gt;</code> element rendered in a shared Layout component (or similar).<br>With our starter project, we may go to <code>layouts/Main.astro</code> and render the <code>ViewTransitions</code> component within <code>&lt;head&gt;</code>, as shown below:</p><pre><code class="language-astro">
---
// import the view transitions component
import { ViewTransitions } from &quot;astro:transitions&quot;;
---

&lt;html lang=&quot;en&quot;&gt;
  &lt;head&gt;
    ...
    &lt;!-- Render the view transitions component --&gt;
    &lt;ViewTransitions /&gt;
  &lt;/head&gt;
  ...
&lt;/html&gt;</code></pre><p><br>Adding ViewTransitions as discussed in standalone pages or shared layout components will capture clicks to other pages and provide a default fade transition, as shown below. Refresh the page if you don&#x2019;t notice the transition right away:<br></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://paper-attachments.dropboxusercontent.com/s_A370C11DD96CE3152AFCEC6F072FABA36221DB09B7DD60A3E5FAF03720283DC3_1696139724965_CleanShot+2023-10-01+at+07.53.50.gif" class="kg-image" alt="Astro View Transitions by Examples" loading="lazy"><figcaption>The default fade transition between the home (list) page and single article view</figcaption></figure><p><br>This is quite a subtle transition. Let&#x2019;s look at how to customize the transition to make it a bit flashier.</p><h2 id="customizing-astro-view-transitions">Customizing Astro view transitions</h2><p>Based on the type of DOM element and its location in the DOM, Astro automatically infers corresponding elements in the origin and destination page.<br>For finer control, we may leverage transition directives:</p><ul><li><code>transition:name</code></li><li><code>transition:animate</code></li><li><code>transition:persist</code></li></ul><h3 id="transitionname"><br>transition:name</h3><p>With <code>transition:name</code> we can specifically associate a pair of DOM elements from the origin and destination page.</p><p><br>To do this, we provide the same <code>transition:name</code> attribute to the origin and destination page elements. This name must be unique across each page.<br>Let&#x2019;s give this a try. In our demo application, we can associate the media elements on the home and overview pages:<br></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2023/09/CleanShot-2023-09-20-at-11.41.46@2x.png" class="kg-image" alt="Astro View Transitions by Examples" loading="lazy"><figcaption>The media elements on the home and overview pages.</figcaption></figure><p><br>To associate these elements, we can give them the same <code>transition:name</code> attribute value. In <code>pages/blog/[...path].astro</code>, find the destination element and provide the <code>transition:name</code> as shown below:</p><pre><code class="language-astro">
&lt;!-- &#x1F4C2; pages/blog/[...path].astro --&gt;
---
const { path } = Astro.params;
--- 

&lt;Main&gt;
  &lt;div
    class=&quot;bg-white/80 min-w-[100%] min-h-[40vh] rounded-lg transition-all&quot;
    transition:name=`media-image-${path}`
  &gt;
...
&lt;/div&gt;
...
&lt;/Main&gt;</code></pre><p><br>The path variable refers to the blog path e.g., <code>some-blog</code> or <code>another-blog</code>. Now, do the same for the origin page.</p><p><br>Pass a <code>mediaTransitionName</code> prop to Card. This will be translated to a <code>media-image-${path}</code> attribute on the media element on the origin page:</p><pre><code class="language-astro">
&lt;!-- &#x1F4C2; pages/index.astro --&gt;

&lt;Card
    to={`/blog/${path`}
    mediaTransitionName={`media-image-${path}`}
/&gt;</code></pre><p><br>By doing this, we now have both elements associated and animated accordingly:<br></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://paper-attachments.dropboxusercontent.com/s_A370C11DD96CE3152AFCEC6F072FABA36221DB09B7DD60A3E5FAF03720283DC3_1696140069482_CleanShot+2023-10-01+at+08.00.27.gif" class="kg-image" alt="Astro View Transitions by Examples" loading="lazy"><figcaption>Associating the media elements via transition:name</figcaption></figure><hr><p>&#x1F44B;&#x1F3FD; Want to delve deeper into Astro, give my latest book a Read: <a href="https://github.com/understanding-astro/understanding-astro-book">Understanding Astro: Everything You Need to know</a>. </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2023/10/image.png" class="kg-image" alt="Astro View Transitions by Examples" loading="lazy" width="1270" height="1086" srcset="https://blog.ohansemmanuel.com/content/images/size/w600/2023/10/image.png 600w, https://blog.ohansemmanuel.com/content/images/size/w1000/2023/10/image.png 1000w, https://blog.ohansemmanuel.com/content/images/2023/10/image.png 1270w" sizes="(min-width: 720px) 720px"><figcaption>The Ultimate Guide to Astro. <a href="https://github.com/understanding-astro/understanding-astro-book">Read for free on Github</a></figcaption></figure><hr><h3 id="transitionanimate"><br>transition:animate</h3><p><code>ViewTransitions</code> always applies a default fade animation, but we can assertively customize the behaviour of specific transitions by adding the <code>transition:animate</code> directive to individual elements.<br>There are four default animations supported via transition:animate:<br></p><ul><li><code>fade</code> (default): An opinionated crossfade animation where the old content fades out and the new content fades in</li><li><code>initial</code>: Uses the browser&#x2019;s default styling</li><li><code>slide</code>: The old content slides left, new slides right. This is reversed on backward navigation</li><li><code>none</code>: Disables the browser&#x2019;s default animations</li></ul><p><br>Let&#x2019;s use <code>slide</code> on our content elements:<br></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2023/09/CleanShot-2023-09-21-at-06.41.58@2x.png" class="kg-image" alt="Astro View Transitions by Examples" loading="lazy"><figcaption>The content elements on the home and blog pages.</figcaption></figure><p><br>The origin page is represented by <code>pages/index.astro</code>. Go ahead and add the <code>transition:animate</code> directive to the content stripes by passing a <code>contentTransitionAnimate</code> prop, as shown below:</p><pre><code class="language-astro">
// &#x1F4C2; pages/index.astro 

&lt;Card
   to={`/blog/${path}`}
   contentTransitionAnimate=&quot;slide&quot;
 /&gt;</code></pre><p><br>Now, add <code>transition:animate</code> to the destination content parent element as shown below:</p><pre><code class="language-astro">
// &#x1F4C2; pages/blog/[...path].astro

&lt;article class=&quot;flex gap-3 flex-col mt-10&quot; transition:animate=&quot;slide&quot;&gt;
 // ...
&lt;/article&gt;</code></pre><p><br>We should now have the content slide animation on both forward and backwards navigation:<br></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://paper-attachments.dropboxusercontent.com/s_A370C11DD96CE3152AFCEC6F072FABA36221DB09B7DD60A3E5FAF03720283DC3_1696140829324_CleanShot+2023-10-01+at+08.13.35.gif" class="kg-image" alt="Astro View Transitions by Examples" loading="lazy"><figcaption>The content slide transitions</figcaption></figure><h3 id="custom-animations">Custom animations</h3><p>Aside from the default animations, we may also configure custom animations via <code>transition:animate</code>. We can customize an existing default animation, such as fade or slide, or create a new one.<br></p><p>Consider the example below that changes the duration of the slide animation on the media content:<br></p><pre><code class="language-astro">
// &#x1F4C2; pages/blog/[...path].astro
---
import { slide } from &quot;astro:transitions&quot;;
---

&lt;article
    class=&quot;flex gap-3 flex-col mt-10&quot;
    // &#x1F440; look here
    transition:animate={slide({ duration: &quot;0.4s&quot; })}
  &gt;
 ...
&lt;/article&gt;</code></pre><p><br>Now, the content slide animation will come in later, as shown below:<br><br></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://paper-attachments.dropboxusercontent.com/s_A370C11DD96CE3152AFCEC6F072FABA36221DB09B7DD60A3E5FAF03720283DC3_1696140968103_CleanShot+2023-10-01+at+08.14.53.gif" class="kg-image" alt="Astro View Transitions by Examples" loading="lazy"><figcaption>The delayed slide transition</figcaption></figure><pre><code class="language-astro">
// &#x1F4C2; layouts/Main.astro

&lt;body&gt;
// ...
    &lt;style is:global&gt;
      @keyframes bounce {
        from,
        20%,
        53%,
        to {
          animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
          transform: translate3d(0, 0, 0);
        }

        40%,
        43% {
          animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
          transform: translate3d(0, -30px, 0) scaleY(1.1);
        }

        70% {
          animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
          transform: translate3d(0, -15px, 0) scaleY(1.05);
        }

        80% {
          transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
          transform: translate3d(0, 0, 0) scaleY(0.95);
        }

        90% {
          transform: translate3d(0, -4px, 0) scaleY(1.02);
        }
      }
    &lt;/style&gt;
&lt;/body&gt;</code></pre><p><br>How about creating a custom animation altogether? Let&#x2019;s make a <a href="https://github.com/animate-css/animate.css/blob/main/source/attention_seekers/bounce.css" rel="noreferrer nofollow noopener">bounce animation</a>.</p><p><br>Go to the <code>layouts/Main.astro</code> component and add a global style with the keyframes for the bounce animation, as shown below:</p><pre><code class="language-astro">
export interface TransitionAnimation {
  name: string;
  delay?: number | string;
  duration?: number | string;
  easing?: string;
  fillMode?: string;
  direction?: string;
}

export interface TransitionAnimationPair {
  old: TransitionAnimation | TransitionAnimation[];
  new: TransitionAnimation | TransitionAnimation[];
}

export interface TransitionDirectionalAnimations {
  forwards: TransitionAnimationPair;
  backwards: TransitionAnimationPair;
}</code></pre><p><br>To create a valid animation, we must follow a strict interface similar to the following:<br></p><pre><code class="language-astro">
// &#x1F4C2; pages/blog/[...path].astro
---
//...
const bounceAnimation = {
  old: {
    name: &quot;bounce&quot;, // same name as the keyframe in layouts/Main
    duration: &quot;0.7s&quot;,
    easing: &quot;linear&quot;,
    fillMode: &quot;forwards&quot;,
  },
  new: {
    name: &quot;bounce&quot;,
    duration: &quot;0.7s&quot;,
    easing: &quot;linear&quot;,
    fillMode: &quot;backwards&quot;,
  },
};

const bounce = {
  forwards: bounceAnimation,
  backwards: bounceAnimation,
};
---</code></pre><p><br>The above describes the expected behaviour when navigating between old and new pages. It includes forward and backwards movement directions.</p><p><br>Let&#x2019;s put this into practice. </p><p>Go to the <code>pages/blog/[...path].astro</code> page and create the actual bounce custom animation in the frontmatter, as shown below:</p><pre><code class="language-astro">
// &#x1F4C2; pages/blog/[...path].astro
---
//...
const bounceAnimation = {
  old: {
    name: &quot;bounce&quot;, // same name as the keyframe in layouts/Main
    duration: &quot;0.7s&quot;,
    easing: &quot;linear&quot;,
    fillMode: &quot;forwards&quot;,
  },
  new: {
    name: &quot;bounce&quot;,
    duration: &quot;0.7s&quot;,
    easing: &quot;linear&quot;,
    fillMode: &quot;backwards&quot;,
  },
};

const bounce = {
  forwards: bounceAnimation,
  backwards: bounceAnimation,
};
---</code></pre><p><br>Finally, use this animation for the destination page content as shown below:</p><pre><code class="language-astro">
// &#x1F4C2; pages/blog/[...path].astro
&lt;article class=&quot;flex gap-3 flex-col mt-10&quot; transition:animate={bounce}&gt;
//...
&lt;/article&gt;</code></pre><p><br>Now, navigate from the home page to the blog destination page and watch the content bounce:<br><br></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://paper-attachments.dropboxusercontent.com/s_A370C11DD96CE3152AFCEC6F072FABA36221DB09B7DD60A3E5FAF03720283DC3_1696141062038_CleanShot+2023-10-01+at+08.17.15.gif" class="kg-image" alt="Astro View Transitions by Examples" loading="lazy"><figcaption>The custom bounce transition</figcaption></figure><h3 id="transitionpersist"><br>transition:persist</h3><p>Upon navigation, we can use the <code>transition:persist</code> directive to persist components and HTML elements across the origin and destination pages.<br></p><p>For example, if we had a video playing on the origin page, we could persist the state of the video component, if it&#x2019;s rendered in the destination page by doing the following:</p><pre><code class="language-astro">
&lt;video transition:persist&gt;
...
&lt;/video&gt;</code></pre><p><br>This is the same with Astro Islands.</p><p><br>Let&#x2019;s put this into practice. For this, I&#x2019;ve prepared a basic counter component built in React, called <code>ReactCounter</code>. In the demo home page, go ahead and render the <code>ReactCounter</code> island as shown below:</p><pre><code class="language-astro">
// &#x1F4C2; pages/index.astro
---
import { ReactCounter } from &quot;../components/ReactCounter&quot;;
---


&lt;Card
  to={`/blog/${path}`}
  mediaTransitionName={`media-image-${path}`}
  contentTransitionAnimate=&quot;slide&quot;&gt;
  {/** &#x1F440; Render counter island */}

  &lt;ReactCounter client:load /&gt;
&lt;/Card&gt;</code></pre><p><br>This will render the counter within the card media element:<br></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://paper-attachments.dropboxusercontent.com/s_A370C11DD96CE3152AFCEC6F072FABA36221DB09B7DD60A3E5FAF03720283DC3_1695314288830_CleanShot+2023-09-21+at+18.37.132x.png" class="kg-image" alt="Astro View Transitions by Examples" loading="lazy"><figcaption>The Counter island within the card media element</figcaption></figure><p><br>Note that clicking the media element updates the counter. To navigate to the blog page, you must click outside the media element, which in our case is the card content.</p><p><br>Go ahead and also render the counter in the blog destination page as shown below:</p><pre><code class="language-astro">
// &#x1F4C2; pages/blog/[...path].astro
---
import { ReactCounter } from &quot;../../components/ReactCounter&quot;;
--- 

&lt;Main&gt;
  &lt;div
    class=&quot;bg-white/80 min-w-[100%] h-[40vh] rounded-lg transition-all&quot;
    transition:name=`media-image-${path}`
  &gt;
    &lt;ReactCounter client:load /&gt;
  &lt;/div&gt;
  // ...
&lt;/Main&gt;</code></pre><p><br>This will render the counter in the main blog media element:<br></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://paper-attachments.dropboxusercontent.com/s_A370C11DD96CE3152AFCEC6F072FABA36221DB09B7DD60A3E5FAF03720283DC3_1695314357641_CleanShot+2023-09-21+at+18.38.492x.png" class="kg-image" alt="Astro View Transitions by Examples" loading="lazy"><figcaption>The Counter Island in the main blog media element</figcaption></figure><p><br>By default, if we transition between the home page and the blog page, the state of the counter is always reset as shown below:<br><br></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://paper-attachments.dropboxusercontent.com/s_A370C11DD96CE3152AFCEC6F072FABA36221DB09B7DD60A3E5FAF03720283DC3_1696141191050_CleanShot+2023-10-01+at+08.19.33.gif" class="kg-image" alt="Astro View Transitions by Examples" loading="lazy"><figcaption>The default counter transition behaviour</figcaption></figure><p><br>However, we can change that by adding <code>transition:persist</code> to the rendered <code>ReactCounter</code> islands and give the counters a unique <code>transition:name</code> to make sure the multiple islands on the home page are treated as distinct elements:</p><p></p><pre><code class="language-astro">
// &#x1F4C2; index.astro
&lt;ReactCounter
  client:load
  transition:persist
  transition:name={`counter-${path}`}
/&gt;

// &#x1F4C2; pages/blog/[...path].astro

&lt;ReactCounter
  client:load
  transition:persist
  transition:name={`counter-${path}`}
/&gt;</code></pre><p><br><br>Now, navigate from the home page to the blog and back. Note that the counter&#x2019;s state remains preserved:<br><br></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://paper-attachments.dropboxusercontent.com/s_A370C11DD96CE3152AFCEC6F072FABA36221DB09B7DD60A3E5FAF03720283DC3_1696141359489_CleanShot+2023-10-01+at+08.22.21.gif" class="kg-image" alt="Astro View Transitions by Examples" loading="lazy"><figcaption>The preserved counter state</figcaption></figure><h2 id="handling-fallback-for-unsupported-browsers">Handling fallback for unsupported browsers</h2><p><code>&lt;ViewTransitions /&gt;</code> works best for browsers that support view transitions, i.e., Chromium browsers. However, it also ships support for other browsers.<br>For example, at the time of writing, Firefox doesn&#x2019;t support view transitions, but here&#x2019;s our demo in Firefox:<br></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://paper-attachments.dropboxusercontent.com/s_A370C11DD96CE3152AFCEC6F072FABA36221DB09B7DD60A3E5FAF03720283DC3_1696141465440_CleanShot+2023-10-01+at+08.23.36.gif" class="kg-image" alt="Astro View Transitions by Examples" loading="lazy"><figcaption>View transitions in Firefox</figcaption></figure><p><br>Astro still tries to provide a comparable transition experience. If you wish to disable support in other browsers, pass none to the fallback prop, as shown below:<br></p><pre><code class="language-astro">&lt;ViewTransitions fallback=&quot;none&quot; /&gt;</code></pre><p><br>This will trigger a full page reload in non-supporting browsers. To disable a full-page reload and disable animations upon navigation, pass the swap prop value as shown below:</p><pre><code class="language-astro">&lt;ViewTransitions fallback=&quot;swap&quot; /&gt;</code></pre><p><br></p><h2 id="conclusion">Conclusion</h2><p>View transitions bring a native feel to the web, and with Astro being the first major web framework to support them, you can start building arguably better transitions today.</p><p><br>Visit the <a href="https://github.com/understanding-astro/astro-middleware-examples" rel="noreferrer nofollow noopener">Astro view transitions examples</a> GitHub repo for all the examples discussed here.</p><hr><p>Want to understand Astro beyond the surface level? Check out <a href="https://www.ohans.me/ua-udemy-coupon">Astro 101: Build Blazing Fast Frontends</a> for a solid foundation where you&apos;ll:</p><ul><li>Build a personal site with Astro </li><li>Build your own component islands implementation from scratch</li><li>Plus, a 500+ page (free) ebook: The Ultimate Guide to Astro</li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2023/09/Astro-101---Splash.gif" class="kg-image" alt="Astro View Transitions by Examples" loading="lazy"><figcaption>Learn the secret(s) of Astro</figcaption></figure><p><a href="https://www.ohans.me/ua-udemy-coupon">Check it out.</a></p>]]></content:encoded></item><item><title><![CDATA[Working with Astro's middleware]]></title><description><![CDATA[A middleware takes the front-row seat in the application server. Allowing us to intercept the user request before it is completed. Read more...]]></description><link>https://blog.ohansemmanuel.com/working-with-astros-middleware/</link><guid isPermaLink="false">64ed7dd843e529f14fb26516</guid><category><![CDATA[Astro]]></category><dc:creator><![CDATA[Ohans Emmanuel]]></dc:creator><pubDate>Thu, 28 Sep 2023 04:40:58 GMT</pubDate><media:content url="https://blog.ohansemmanuel.com/content/images/2023/10/b.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.ohansemmanuel.com/content/images/2023/10/b.png" alt="Working with Astro&apos;s middleware"><p>I like to think of middleware as middle agents.</p><p>A simplified flow of data from a user&#x2019;s browser to our application servers comprises a request-response cycle.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2023/08/DraggedImage.png" class="kg-image" alt="Working with Astro&apos;s middleware" loading="lazy"><figcaption>A basic request-response cycle</figcaption></figure><p>A middleware takes the front-row seat in the application server. Allowing us to intercept the user request before it is completed.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2023/08/DraggedImage-1.png" class="kg-image" alt="Working with Astro&apos;s middleware" loading="lazy"><figcaption>The middleware intercepts the client request</figcaption></figure><p>With a middleware, we can modify the client request via headers or cookies.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2023/08/DraggedImage-2.png" class="kg-image" alt="Working with Astro&apos;s middleware" loading="lazy"><figcaption>A middleware can modify the client request</figcaption></figure><p>We can also forward the request as is, but inject a new behaviour e.g., log the requests.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2023/08/DraggedImage-3.png" class="kg-image" alt="Working with Astro&apos;s middleware" loading="lazy"><figcaption>A middleware can inject new behaviour</figcaption></figure><p>Middleware is also capable of just returning a response to the client before hitting any reaching actors in the web server e.g., handling request validations.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2023/08/DraggedImage-4.png" class="kg-image" alt="Working with Astro&apos;s middleware" loading="lazy"><figcaption>A middleware can respond directly to a client request</figcaption></figure><p>As we explore middleware, note that middleware is invoked every time a page or endpoint is about to be rendered.</p><p>The best way to get acquainted with Astro middleware is to try examples. Let&#x2019;s get started with a basic one.</p><h2 id="hello-world-middleware">Hello world, middleware</h2><p>What would a basic Astro middleware look like?</p><p>Start a new Astro project with the following command:</p><pre><code class="language-js">npm create astro@latest -- --yes --template=minimal --skip-houston hello-world
</code></pre><p>This will use <code>hello-world</code> as the project directory and use the <code>minimal</code> Astro starter template.</p><p>Run the project:</p><pre><code class="language-js">cd hello-world &amp;&amp; npm run dev
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2023/08/CleanShot-2023-08-27-at-07.54.58.png" class="kg-image" alt="Working with Astro&apos;s middleware" loading="lazy"><figcaption>The new running application</figcaption></figure><p>To use a middleware, create a new file in <code>src/middleware.js|ts</code>. Alternatively, we may use a <code>middleware</code> directory e.g.: <code>src/middleware/index.js|ts</code>.</p><p>Let&#x2019;s stick to the simpler <code>src/middleware.ts</code> file.</p><p>In this file, make sure to export an <code>onRequest</code> function that points to the middleware.</p><pre><code class="language-js">import type { MiddlewareHandler } from &quot;astro&quot;;

const middleware = () =&gt; {};

// Export onRequest. This should NOT be a default export
export const onRequest = middleware;
</code></pre><p>The middleware is incomplete.</p><p>Technically speaking, a middleware will be invoked with two arguments and must return a <a href="https://developer.mozilla.org/en-US/docs/Web/API/Response">Response</a> object.</p><p>Here&#x2019;s a typed implementation:</p><pre><code class="language-js">import type { MiddlewareResponseHandler } from &quot;astro&quot;;
import { defineMiddleware } from &quot;astro/middleware&quot;;

const middleware: MiddlewareResponseHandler = (context, next) =&gt; {
};

export const onRequest = middleware;
</code></pre><p>The context object is identical to the <a href="https://docs.astro.build/en/reference/api-reference/#endpoint-context">endpoint context</a> which mirrors many of the global Astro properties. The <code>next</code> function returns a standard <code>Response</code> when invoked.</p><p>Now, complete the basic middleware as shown below:</p><pre><code class="language-js">const middleware: MiddlewareResponseHandler = (context, next) =&gt; {
    console.log(&quot;HELLO WORLD!&quot;);

	return next();
};
</code></pre><p>In this example, the middleware receives a request, logs <code>HELLO WORLD!</code> and returns an unmodified response that continues its standard lifecycle.</p><p>Now, go to the project&#x2019;s index page and add a simple log as shown below:</p><pre><code class="language-js">---
console.log(&quot;Rendering the index page&quot;);
---
</code></pre><p>If you inspect the local server logs, every time the index page is rendered, the middleware log will be fired first. Hit refresh to see multiple logs.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2023/08/CleanShot-2023-08-27-at-07.58.40.png" class="kg-image" alt="Working with Astro&apos;s middleware" loading="lazy"><figcaption>The Hello world middleware logs</figcaption></figure><p>A terser version of the same middleware could also be written as shown below:</p><pre><code class="language-js">{/* &#x1F4C2; src/middleware.ts */}

import { defineMiddleware } from &quot;astro/middleware&quot;;

/**
* Inline the middleware function by leveraging &quot;defineMiddleware&quot;
*/
export const onRequest = defineMiddleware((context, next) =&gt; {
  console.log(&quot;HELLO WORLD!&quot;);

  return next();
});
</code></pre><h3 id="middleware-responses">Middleware responses</h3><p>Our current Hello World middleware simply intercepts the user&#x2019;s request, logs a message on the server and forwards the response by calling <code>next()</code>.</p><p>In this example, we&#x2019;re not modifying the user response.</p><p><code>next()</code> returns a standard response that continues its lifecycle as usual i.e., eventually renders the requested page or endpoint.</p><p>Consider the following example that intercepts the user request and immediately returns a response to the user.</p><pre><code class="language-js">{/* &#x1F4C2; src/middleware.ts */}

import { defineMiddleware } from &quot;astro/middleware&quot;;

export const onRequest = defineMiddleware((context, next) =&gt; {
  console.log(&quot;MIDDLEWARE&quot;);

  return new Response(
    JSON.stringify({
      message: &quot;Hello world&quot;,
    }),
    {
      status: 200,
    }
  );
});
</code></pre><p>Now, if you visit the <code>index</code> page (or any valid route), you will no longer receive the requested resource, but the <code>JSON</code> response specified in the middleware.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2023/08/CleanShot-2023-08-02-at-07.33.50.png" class="kg-image" alt="Working with Astro&apos;s middleware" loading="lazy"><figcaption>Intercepted request and middleware response</figcaption></figure><h2 id="why-middleware">Why Middleware?</h2><p>Middleware shines when you need centralised logic for your application.</p><p>Remember, every request to pages and API endpoints in your project will be intercepted by middleware.</p><p>Invariably, middleware acts as the first touchpoint to your application, making it suitable for<strong> centralised logic</strong> such as:</p><ul><li>Authentication</li><li>A/B testing</li><li>Server-side validations</li><li>Request logging</li></ul><p>And much more.</p><p>Instead of duplicating logic across different pages and endpoints, centralise shared application logic or behaviour with middleware.</p><h2 id="practical-astro-middleware-examples">Practical Astro Middleware Examples</h2><p>Beyond Hello World, let&#x2019;s take a plunge into practical middleware use cases. We&#x2019;ll start simple and increasingly progress in complexity.</p><h3 id="redirects">Redirects</h3><p>Use <code>Response.redirect</code> or <code>context.redirect</code> to respond with a redirect within a middleware.</p><p>The API is shown below:</p><pre><code class="language-js">// Response.redirect
return Response.redirect(url: URL | string, status?: number)

// For SSR: 
return context.redirect(path: string, status?: number)
</code></pre><p>The main difference between both APIs is that <code>context.redirect</code> accepts relative string paths e.g., <code>/redirected</code> while <code>Response.redirect</code> accepts either a URL object or the full redirect URL path e.g., <code>https://www.example.com/redirected</code> not <code>/redirected</code>.</p><p>Let&#x2019;s consider a practical example.</p><p>Start a new Astro project with the command below:</p><pre><code class="language-bash">npm create astro@latest -- --yes --skip-houston --template=basics redirect
</code></pre><p>Create a new <code>src/pages/redirected.astro</code> page.</p><p>Copy the entire content of <code>src/pages/index.astro</code> into this new page and change the <code>&lt;h1&gt;</code> element to the following:</p><pre><code class="language-js">&lt;h1&gt;You&apos;ve been &lt;span class=&quot;text-gradient&quot;&gt;Redirected&lt;/span&gt;&lt;/h1&gt;
</code></pre><p>Now handle the redirect in the middleware as shown below:</p><pre><code class="language-js">// src/middleware.ts

import { defineMiddleware } from &quot;astro/middleware&quot;;

const INDEX_PATH = &quot;/&quot;;

export const onRequest = defineMiddleware((context, next) =&gt; {
  /**
   * The middleware runs every time a page or endpoint is about to be rendered.
   * Only redirect if this is the home page
   */
  if (context.url.pathname === INDEX_PATH) {
    /**
     * Construct a full URL by passing `context.url` as the base URL
     */
    return Response.redirect(new URL(&quot;/redirected&quot;, context.url), 302);

    /**
     * You may also redirect using `context.redirect` as shown below:
     * =========================================
     * return context.redirect(&quot;/redirected&quot;, 302);
     * =========================================
     * Note that this only works in SSR mode
     */
  }

  return next();
});

</code></pre><h3 id="how-to-use-astrolocals">How to use Astro.locals</h3><p>For the entire lifecycle of a request i.e., from when it&#x2019;s received by the middleware to when the eventual response is sent to the user, we may persist data in the locals object to be used in Astro pages, API endpoints or other middleware.</p><p>Consider the example below:</p><pre><code class="language-js">// src/middleware.ts

import { defineMiddleware } from &quot;astro/middleware&quot;;

export const onRequest = defineMiddleware((context, next) =&gt; {
  // add a string value to the locals object
  context.locals.stringValue = &quot;Hello Middleware&quot;;

  // add a method to the locals object
  context.locals.functionValue = () =&gt; &quot;This is a function return value&quot;;

  return next();
}); 
</code></pre><p>We&#x2019;ve added two values to the <code>locals</code> object, and may access this on an Astro page as shown below:</p><pre><code class="language-js">// src/pages/index.astro
---
const data = Astro.locals;

console.log({ data });
/** 
 * Log &#x2B07;&#xFE0F;
 * {
  data: {
    stringValue: &apos;Hello Middleware&apos;,
    functionValue: [Function (anonymous)]
  }
}
 */

console.log({ res: data.functionValue() });

/**
 * Log &#x2B07;&#xFE0F;
 * { res: &apos;This is a function return value&apos; }
 */
---
</code></pre><p>For full typescript support, update the <code>src/env.d.ts</code> file to include the specific <code>locals</code> properties as shown below:</p><pre><code class="language-ts">// Before 
/// &lt;reference types=&quot;astro/client&quot; /&gt;
</code></pre><pre><code class="language-ts">// After 

/// &lt;reference types=&quot;astro/client&quot; /&gt;
declare namespace App {
  interface Locals {
    stringValue: string;
    functionValue: () =&gt; string;
  }
}
</code></pre><h3 id="how-to-use-multiple-astro-middleware">How to use multiple Astro middleware</h3><p>In our previous examples, we only considered an application with one middleware. In reality, we often use a series of middleware, where the user&#x2019;s request is passed from one middleware to the next until a response is finally sent back to the user.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2023/08/DraggedImage-5.png" class="kg-image" alt="Working with Astro&apos;s middleware" loading="lazy"><figcaption>Using multiple middleware in as Astro project</figcaption></figure><p>Create a new Astro project and create the following two middleware:</p><pre><code class="language-js">// src/middleware/auth.ts 

import { defineMiddleware } from &quot;astro/middleware&quot;;

export const auth = defineMiddleware((context, next) =&gt; {
  console.log(&quot;In auth middleware&quot;);
  return next();
});
</code></pre><pre><code class="language-js">// src/middleware/validate.ts

import { defineMiddleware } from &quot;astro/middleware&quot;;

export const validate = defineMiddleware((context, next) =&gt; {
  console.log(&quot;In validate middleware&quot;);
  return next();
});
</code></pre><p>Note that these are created in a <code>middleware</code> directory.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2023/08/DraggedImage-6.png" class="kg-image" alt="Working with Astro&apos;s middleware" loading="lazy"><figcaption>The directory structure for multiple middleware</figcaption></figure><p>Instead of creating a directory, we could inline the different middleware in a single <code>src/middleware.ts</code> file. However, having a directory is arguably neater when working with multiple middleware.</p><p>Now create an <code>src/middleware/index.ts</code> file to compose the multiple middleware as shown below:</p><pre><code class="language-js">// src/middleware/index.ts

// sequence will accept middleware functions and will execute them in the order they are passed
import { sequence } from &quot;astro/middleware&quot;;

// import the middleware 
import { auth } from &quot;./auth&quot;;
import { validate } from &quot;./validate&quot;;

// export onRequest. Invoke &quot;sequence&quot; with the middleware
export const onRequest = sequence(validate, auth);
</code></pre><p>With this, look in the server logs, and you&#x2019;ll find logs from the validate and auth middleware.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2023/08/CleanShot-2023-08-26-at-08.02.05@2x.png" class="kg-image" alt="Working with Astro&apos;s middleware" loading="lazy"><figcaption>The multiple middleware logs</figcaption></figure><p>Please note that the order in which you pass the middleware to <code>sequence</code> matters. For example, consider the change below:</p><pre><code class="language-js">// Before 
export const onRequest = sequence(validate, auth);

// After
export const onRequest = sequence(auth, validate);
</code></pre><p>This will result in a different execution order as seen in the logs:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2023/08/CleanShot-2023-08-26-at-08.03.10@2x.png" class="kg-image" alt="Working with Astro&apos;s middleware" loading="lazy"><figcaption>The reordered multiple middleware logs</figcaption></figure><p></p><hr><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2023/09/Astro-101---Splash.gif" class="kg-image" alt="Working with Astro&apos;s middleware" loading="lazy" width="1920" height="1080" srcset="https://blog.ohansemmanuel.com/content/images/size/w600/2023/09/Astro-101---Splash.gif 600w, https://blog.ohansemmanuel.com/content/images/size/w1000/2023/09/Astro-101---Splash.gif 1000w, https://blog.ohansemmanuel.com/content/images/size/w1600/2023/09/Astro-101---Splash.gif 1600w, https://blog.ohansemmanuel.com/content/images/2023/09/Astro-101---Splash.gif 1920w" sizes="(min-width: 720px) 720px"><figcaption><a href="https://www.ohans.me/ua-udemy-coupon">Learn the secret(s) of Astro</a>&#xA0;</figcaption></figure><p></p><hr><h3 id="basic-authentication">Basic Authentication</h3><p><a href="https://en.wikipedia.org/wiki/Basic_access_authentication">Basic authentication</a> provides a way for a browser to provide a username and password when making a request.</p><p>Consider the basic auth example with Astro middleware shown below:</p><pre><code class="language-js">import { defineMiddleware } from &quot;astro/middleware&quot;;

export const onRequest = defineMiddleware((context, next) =&gt; {
  // If present, this will have the form: &quot;Basic &lt;credential&gt;&quot;
  const basicAuth = context.request.headers.get(&quot;authorization&quot;);

  if (basicAuth) {
    // get auth value from string &quot;Basic authValue&quot;
    const authValue = basicAuth.split(&quot; &quot;)[1];

    // decode the Base64 encoded string via atob (https://developer.mozilla.org/en-US/docs/Web/API/atob)
    const [user, pwd] = atob(authValue).split(&quot;:&quot;);

    if (user === &quot;admin&quot; &amp;&amp; pwd === &quot;admin&quot;) {
      // forward request 
      return next();
    }
  }

  return new Response(&quot;Auth required&quot;, {
    status: 401,
    headers: {
      &quot;WWW-authenticate&quot;: &apos;Basic realm=&quot;Secure Area&quot;&apos;,
    },
  });
});
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2023/08/CleanShot-2023-08-02-at-08.22.45.png" class="kg-image" alt="Working with Astro&apos;s middleware" loading="lazy"><figcaption>Basic authentication interface.</figcaption></figure><p>The crux of the solution is if the browser requests with an authorisation header, we respond with the following:</p><pre><code class="language-js">return new Response(&quot;Auth required&quot;, {
    status: 401,
    headers: {
      &quot;WWW-authenticate&quot;: &apos;Basic realm=&quot;Secure Area&quot;&apos;,
    },
  });
</code></pre><p>The browser receives this response and understands to request basic authorisation by showing the username and password prompt.</p><p>Once a user enters the username and password, it&#x2019;s sent to the server as a header. We then parse the request accordingly in the middleware as shown below:</p><pre><code class="language-js">const basicAuth = context.request.headers.get(&quot;authorization&quot;);

  if (basicAuth) {
    // Get auth value from string &quot;Basic authValue&quot;
    const authValue = basicAuth.split(&quot; &quot;)[1];

    // Decode the Base64 encoded string via atob (https://developer.mozilla.org/en-US/docs/Web/API/atob)
    const [user, pwd] = atob(authValue).split(&quot;:&quot;);
	
    // Our username and password must be &quot;admin&quot; to be valid
    if (user === &quot;admin&quot; &amp;&amp; pwd === &quot;admin&quot;) {
      // forward request 
      return next();
    }
  }
</code></pre><p>NB: By default, basic auth remains cached until the browser is closed i.e., the user remains logged in till they close the browser.</p><h3 id="jwt-authentication-via-astro-middleware">JWT Authentication via Astro middleware</h3><p><a href="https://jwt.io/introduction">JSON Web Tokens</a> are a common way to communicate authentication claims between server and client.</p><p>Let&#x2019;s see an implementation in an Astro middleware.</p><p>First, create a new project by running the following command:</p><pre><code class="language-bash">npm create astro@latest -- --yes --skip-houston --template=basics jwt-auth
</code></pre><p>Now, create a new <code>src/constants.ts</code> file with the following content:</p><pre><code class="language-ts">// The key of the JWT cookie value
export const TOKEN = &quot;token&quot;;

// The following pages do NOT need auth to be accessed
export const PUBLIC_ROUTES = [&quot;/&quot;, &quot;/api/login&quot;, &quot;/api/logout&quot;];

</code></pre><p>Create an endpoint route for <code>/api/login</code> to log in a user.</p><pre><code class="language-ts">// src/pages/api/login.ts 

import { nanoid } from &quot;nanoid&quot;;
import { SignJWT } from &quot;jose&quot;;
import type { APIRoute } from &quot;astro&quot;;
import { TOKEN } from &quot;../../constant&quot;;

// The token secret. Note the environment variable &quot;JWT_SECRET_KEY
// @see https://docs.astro.build/en/guides/environment-variables/
const secret = new TextEncoder().encode(import.meta.env.JWT_SECRET_KEY);

export const post: APIRoute = async (ctx) =&gt; {
  try {
    // Create the token 
    // @see https://github.com/panva/jose
    const token = await new SignJWT({})
      .setProtectedHeader({ alg: &quot;HS256&quot; })
      .setJti(nanoid())
      .setIssuedAt()
      .setExpirationTime(&quot;2h&quot;)
      .sign(secret);

    // set JWT as a cookie 
    ctx.cookies.set(TOKEN, token, {
      httpOnly: true,
      path: &quot;/&quot;,
      maxAge: 60 * 60 * 2, // 2 hours in seconds
    });
	
    // return a successful response
    return new Response(
      JSON.stringify({
        message: &quot;You&apos;re logged in!&quot;,
      }),
      {
        status: 200,
      }
    );
  } catch (error) {
    console.debug(error);

    return new Response(
      JSON.stringify({
        message: &quot;Login failed&quot;,
      }),
      {
        status: 500,
      }
    );
  }
};
</code></pre><p>If a user&#x2019;s logged in, they should be able to visit any protected routes in the application.</p><p>Let&#x2019;s handle the auth validation via a middleware. Create one in <code>src/middleware.ts</code> and consider the annotated content below:</p><pre><code class="language-js">// src/middleware.ts

import { errors, jwtVerify } from &quot;jose&quot;;
import { defineMiddleware } from &quot;astro/middleware&quot;;
import { TOKEN, PUBLIC_ROUTES } from &quot;./constant&quot;;

// The JWT secret 
const secret = new TextEncoder().encode(import.meta.env.JWT_SECRET_KEY);

/**
* Verify if a client token. 
*/
const verifyAuth = async (token?: string) =&gt; {
  if (!token) {
    return {
      status: &quot;unauthorized&quot;,
      msg: &quot;Please pass a request token&quot;,
    } as const;
  }

  try {
    const jwtVerifyResult = await jwtVerify(token, secret);

    return {
      status: &quot;authorized&quot;,
      payload: jwtVerifyResult.payload,
      msg: &quot;successfully verified auth token&quot;,
    } as const;
  } catch (err) {
    if (err instanceof errors.JOSEError) {
      return { status: &quot;error&quot;, msg: err.message } as const;
    }

    console.debug(err);
    return { status: &quot;error&quot;, msg: &quot;could not validate auth token&quot; } as const;
  }
};

export const onRequest = defineMiddleware(async (context, next) =&gt; {
  // Ignore auth validation for public routes
  if (PUBLIC_ROUTES.includes(context.url.pathname)) {
   // Respond as usual 
    return next();
  }
 
  // Get the token from cookies 
  const token = context.cookies.get(TOKEN).value;
  // Verify the token 
  const validationResult = await verifyAuth(token);

  console.log(validationResult);

  // Handle the validation result 
  switch (validationResult.status) {
    case &quot;authorized&quot;:
      // Respond as usual if the user is authorised 
      return next();

    case &quot;error&quot;:
    case &quot;unauthorized&quot;:
      // If an API endpoint, return a JSON response
      if (context.url.pathname.startsWith(&quot;/api/&quot;)) {
        return new Response(JSON.stringify({ message: validationResult.msg }), {
          status: 401,
        });
      }
      // Otherwise, this is a standard page. Redirect to the root page for the user to login
      else {
        return Response.redirect(new URL(&quot;/&quot;, context.url));
      }

    default:
      return Response.redirect(new URL(&quot;/&quot;, context.url));
  }
});
</code></pre><p></p><h3 id="feature-flags-with-astro-middleware">Feature flags with Astro middleware</h3><p>Feature flags enable easy rollback of a new feature or code. They can also be used to control access to certain parts of your application, all without the need for redeployment.</p><p><br>Handling feature flags via Astro middleware is straightforward depending on the service where your feature flags are hosted. </p><p><br>Assume we want to redirect users to a special marketing page if the feature flag astro-middleware-demo is toggled on. Regardless of your feature flag service, your implementation will look something like the following:</p><pre><code class="language-ts">

// &#x1F4C2; src/middleware.ts
import { defineMiddleware } from &quot;astro:middleware&quot;;

/** Import the Feature Flag client - make sure this is server compatible e.g., node js client **/
import { FeatureFlagger } from &quot;some-feature-node-library&quot;;

const HOME_PAGE_PATH = &quot;/&quot;;

/**
 * Depending on your service&apos;s API: 
 * Create the feature flag client and pass your project API key
 * Add FEATURE_FLAG_API_KEY to &quot;src/.env&quot;
 */
const client = new FeatureFlagger(import.meta.env.FEATURE_FLAG_API_KEY);

/** &#x1F440; The middleware function **/
export const onRequest = defineMiddleware(async (context, next) =&gt; {
  /**
   * Early return. We will only check the feature flag for requests
   * to the homepage
   */
  if (context.url.pathname !== HOME_PAGE_PATH) {
    return next();
  }

  try {
    /**
     * Retrieve the feature toggle for your feature flag
     * In this case, &quot;astro-middleware-demo&quot;
     */
    const isEnabled = await client.isFeatureEnabled(
      &quot;astro-middleware-demo&quot;,
    );

    if (isEnabled) {
      console.log(&quot;Feature is ENABLED!&quot;);

      /**
       * When the feature flag is toggled on, redirect users who access the homepage,
       * to the &quot;/astro-middleware-demo&quot; page
       */
      return Response.redirect(new URL(&quot;/astro-middleware-demo&quot;, context.url), 302);

      /**
       * Otherwise, handle the request as usual
       */
      return next();
    }

    /**
     * Feature flag NOT enabled? Handle the request as usual
     */

    console.log(&quot;Feature is DISABLED!&quot;);
    return next();
  } catch (error) {
    console.error(&quot;Failed to load feature flag from some-feature-node-library&quot;);

    /**
     * Handle the request as usual
     */
    return next();
  }
});</code></pre><h2 id="conclusion">Conclusion</h2><p>Astro is now closer to parity with other mainstream frameworks like NextJS, thanks to its middleware support. Middleware in web client frameworks (regardless of which) helps centralize logic within your application and aims to solve the same problem. </p><p><br>It&apos;s important to note that Astro only supports a single global middleware.ts file, and <a href="https://vercel.com/docs/frameworks/astro#edge-middleware" rel="noreferrer nofollow noopener">does not allow for route-specific middleware</a>. However, if you&apos;re deploying Astro on Vercel, you can take advantage of the matcher configuration available to NextJS middleware.</p><pre><code class="language-ts">
// src/middleware.ts 

export const config = {
  // Only run the middleware on the marketing route
 matcher: &apos;/marketing&apos;
}

// write Middleware as usual </code></pre><p>Visit the <a href="https://github.com/understanding-astro/astro-middleware-examples">Github repo</a> for all the examples discussed here and more. See <a href="https://github.com/understanding-astro/astro-middleware-examples">Astro middleware examples</a>.</p><p></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2023/09/Astro-101---Splash.gif" class="kg-image" alt="Working with Astro&apos;s middleware" loading="lazy" width="1920" height="1080" srcset="https://blog.ohansemmanuel.com/content/images/size/w600/2023/09/Astro-101---Splash.gif 600w, https://blog.ohansemmanuel.com/content/images/size/w1000/2023/09/Astro-101---Splash.gif 1000w, https://blog.ohansemmanuel.com/content/images/size/w1600/2023/09/Astro-101---Splash.gif 1600w, https://blog.ohansemmanuel.com/content/images/2023/09/Astro-101---Splash.gif 1920w" sizes="(min-width: 720px) 720px"><figcaption><a href="https://www.ohans.me/ua-udemy-coupon">Learn the secret(s) of Astro</a>&#xA0;</figcaption></figure>]]></content:encoded></item><item><title><![CDATA[The new Instantiation Expression in Typescript]]></title><description><![CDATA[With instantiation expressions, we can now take functions and constructors and feed them type arguments directly.]]></description><link>https://blog.ohansemmanuel.com/the-new-instantiation-expression-in-typescript/</link><guid isPermaLink="false">62e4648b43e529f14fb263ae</guid><category><![CDATA[typescript]]></category><dc:creator><![CDATA[Ohans Emmanuel]]></dc:creator><pubDate>Fri, 29 Jul 2022 22:52:36 GMT</pubDate><media:content url="https://blog.ohansemmanuel.com/content/images/2022/07/blog-7-1.png" medium="image"/><content:encoded><![CDATA[<h2 id="tldr">TLDR</h2><img src="https://blog.ohansemmanuel.com/content/images/2022/07/blog-7-1.png" alt="The new Instantiation Expression in Typescript"><p>Instantiation expressions provide the ability to specify type arguments for generic functions or generic constructors without actually calling them.</p><h2 id="introduction">Introduction</h2><p>You&#x2019;re building a banking application and need to write a certain <code>withdrawMoney</code> function that takes the <code>currency</code> and <code>amount</code> to be withdrawn as parameters.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-30-at-00.29.09.png" class="kg-image" alt="The new Instantiation Expression in Typescript" loading="lazy"><figcaption>The withdrawMoney function</figcaption></figure><p>Here&#x2019;s a contrived implementation anyone could arguably come up with:</p><pre><code class="language-ts">const withdrawMoney = (currency: string, amount: number) =&gt; {
  // do something 
  return { currency, amount }
}
</code></pre><p>Assuming you want the type of the <code>currency</code> and <code>amount</code> to be ultimately flexible, you may go ahead and represent the arguments with generics as follows:</p><pre><code class="language-ts">const withdrawMoney = &lt;C, A&gt;(currency: C, amount: A) =&gt; {
  // do something 
  return { currency, amount }
}
</code></pre><p>Looking good! Heck, you may win an award at work for such fine code.</p><h2 id="the-specificity-problem">The Specificity Problem</h2><p>When you have generic functions as in the example above, more often than not, you may find yourself in a situation where you need a stricter or more specific version of the same function.</p><p>For example, <code>withdrawMoney</code> is your generic handler for withdrawing any sum of money in any currency.</p><p>However, you may find yourself requiring a more specific version that handles tipping creators one dollar (creators deserve more).</p><p>To make a more specific function, there used to be only two options.</p><h3 id="1-wrap-the-generic-function-in-a-new-function">1. Wrap the generic function in a new function</h3><p>This is straightforward.</p><p>You&#x2019;d write a new function <code>tipCreator</code> that composes the <code>withdrawMoney</code> function as follows:</p><pre><code class="language-ts">const tipCreator = (currency: &apos;USD&apos;, amount: 1) =&gt; {
  return withdrawMoney(currency, amount)
}
</code></pre><p>And of course, the return value of <code>tipCreator</code> is correctly typed, leveraging the type of value passed to <code>withdrawMoney</code>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-29-at-16.39.51.png" class="kg-image" alt="The new Instantiation Expression in Typescript" loading="lazy"><figcaption>The tipCreator function correctly typed</figcaption></figure><p>See <a href="https://www.typescriptlang.org/play?#code/MYewdgzgLgBA7gSygCwCYCcCGcCy4CmAnjALwwA8AwgDQwCCAfABTACu66+YwhAXDDRiYAtiFZgo-OgEpSDGAG8AsACgYMAPQaYqEDAghh+FAjABzGKvWco7MIphsOXHrRFiJMAL6qfK1aCQsFAIAA6UnJhQIOikMCzsnNx8MADkAKoAygAiqW6i4pIwAIyyJPLKajA2dvBIaFi4BIQJzsn5HlDSvkA">Typescript playground</a>.</p><p>This isn&#x2019;t a bad solution.</p><p>However, it seems a bit too much. All we want is to restrict the generic type for the <code>withdrawMoney</code> function. Do we really need to re-invoke <code>withdrawMoney</code> to get the appropriate type here?</p><p>Perhaps not!</p><p>Ideally, we would prefer a solution that allows you to alias<code>withdrawMoney</code> while replacing all the generics in its signature.</p><h3 id="2-use-an-explicit-type-for-an-alias">2. Use an explicit type for an alias</h3><p>An alternative solution would be to use an explicit type for a new function, as seen below:</p><pre><code class="language-ts">const tipCreator: (currency: &apos;USD&apos;, amount: 1) =&gt; 
{ currency: &apos;USD&apos;, amount: 1 } = withdrawMoney
</code></pre><p>If you remove the type information, here&#x2019;s what this is stripped down to:</p><pre><code class="language-ts">const tipCreator = withdrawMoney
</code></pre><p>Essentially, an alias.</p><p>The difference is the extra explicit type.</p><p>This solution works, but writing explicit types as seen above can get unwieldy rapidly.</p><p>An ideal solution would be something close to the stripped down version above, without the fuss of a full on explicit type.</p><p>See <a href="https://www.typescriptlang.org/play?#code/MYewdgzgLgBA7gSygCwCYCcCGcCy4CmAnjALwwA8AwgDQwCCAfABTACu66+YwhAXDDRiYAtiFZgo-OgEpSDGAG8AsACgYMAPQaYqEDAghh+FAjABzGKvWco7MIphsOXHrRFiJMAL6qfK1aCQsFAIAA6UnJhQIOj8LOyc3HwwAOQAqgDKACIpbqLikjAAjLIk8gqOCS7J6dm5QvkS-EXepPBIaFi4BIRAA">Typescript playground</a>.</p><h2 id="introducing-instantiation-expressions">Introducing instantiation expressions</h2><p>Now that you&#x2019;re familiar with the problem, here&#x2019;s the solution with instantiation expressions:</p><pre><code class="language-ts">// look here &#x1F447;
const tipCreator = withdrawMoney&lt;&apos;USD&apos;, 1&gt;
</code></pre><p>For completeness, here&#x2019;s the definition for <code>withdrawMoney</code>:</p><pre><code class="language-ts">const withdrawMoney = &lt;C, A&gt;(currency: C, amount: A) =&gt; {
  // do something 
  return { currency, amount }
}
</code></pre><p>How easy!</p><p>With instantiation expressions, we can now take functions and constructors and feed them type arguments directly.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-30-at-00.35.09.png" class="kg-image" alt="The new Instantiation Expression in Typescript" loading="lazy"><figcaption>Feeding types to functions and constructors directly</figcaption></figure><p>Remember that this also works for constructors e.g., <code>Array</code>, <code>Map</code> or <code>Set</code> e.g.,</p><pre><code class="language-ts">type Currency = &apos;USD&apos; | &apos;EUR&apos; | &apos;GBP&apos;

// use instantiation expressions 
const CurrencyMap = Map&lt;string, Currency&gt;;

// use the new alias 
const currencyMap = new CurrencyMap() 
// currencyMap will have type Map&lt;string, Currency&gt;
</code></pre><p>There you go!</p><h2 id="how-do-i-know-when-you-use-instantiation-expressions">How do I know when you use instantiation expressions?</h2><figure class="kg-card kg-image-card"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-29-at-16.19.13.png" class="kg-image" alt="The new Instantiation Expression in Typescript" loading="lazy"></figure><p>Next time you find yourself needing a specifically typed version of a function or constructor, it may be a great time to consider instantiation expressions.</p><h2 id="further-resources">Further resources</h2><ol><li>The Instantiation expressions <a href="https://github.com/microsoft/TypeScript/pull/47607">implementation</a></li><li><a href="https://www.ohansemmanuel.com/cheatsheet/top-7-stack-overflowed-typescript-questions">The Seven Most Stackoverflowed TS Questions</a> (PDF)</li><li><a href="https://www.ohansemmanuel.com/books/how-to-build-strongly-typed-polymorphic-react-components">Build Strongly Typed Polymorphic React Components</a> (PDF)</li></ol>]]></content:encoded></item><item><title><![CDATA[Avoid Object wrapper Types in Typescript—Here's why]]></title><description><![CDATA[<p>Consider the following contrived Typescript example:</p><pre><code class="language-ts">// 1
const hello = (v: String) =&gt; {
  console.log(v)
}

// 2
const hello = (v: string) =&gt; {
  console.log(v)
}
</code></pre><p>Note the difference in the type of the <code>v</code> parameter: <code>string</code> vs <code>String</code>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-15-at-09.41.26@2x.png" class="kg-image" alt="The primitive vs object wrapper type" loading="lazy"><figcaption>The primitive vs object wrapper type</figcaption></figure><h2 id="don%E2%80%99t-use-the-object-wrapper-types">Don&#x2019;t use the Object wrapper</h2>]]></description><link>https://blog.ohansemmanuel.com/avoid-object-wrapper-types-in-typescript-here-is-why/</link><guid isPermaLink="false">62d11f4143e529f14fb26360</guid><category><![CDATA[typescript]]></category><dc:creator><![CDATA[Ohans Emmanuel]]></dc:creator><pubDate>Fri, 15 Jul 2022 08:10:28 GMT</pubDate><media:content url="https://blog.ohansemmanuel.com/content/images/2022/07/blog-7.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.ohansemmanuel.com/content/images/2022/07/blog-7.png" alt="Avoid Object wrapper Types in Typescript&#x2014;Here&apos;s why"><p>Consider the following contrived Typescript example:</p><pre><code class="language-ts">// 1
const hello = (v: String) =&gt; {
  console.log(v)
}

// 2
const hello = (v: string) =&gt; {
  console.log(v)
}
</code></pre><p>Note the difference in the type of the <code>v</code> parameter: <code>string</code> vs <code>String</code>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-15-at-09.41.26@2x.png" class="kg-image" alt="Avoid Object wrapper Types in Typescript&#x2014;Here&apos;s why" loading="lazy"><figcaption>The primitive vs object wrapper type</figcaption></figure><h2 id="don%E2%80%99t-use-the-object-wrapper-types">Don&#x2019;t use the Object wrapper types</h2><p>As a general rule, you should rarely use the object wrapper types: <code>Number</code>, <code>String</code>, <code>Boolean</code>, <code>Symbol</code> or <code>Object</code></p><p>This is explicitly stated in the Typescript docs.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-15-at-08.42.37@2x.png" class="kg-image" alt="Avoid Object wrapper Types in Typescript&#x2014;Here&apos;s why" loading="lazy"><figcaption>https://www.typescriptlang.org/docs/handbook/declaration-files/do-s-and-don-ts.html#use-union-types</figcaption></figure><p>The question this article aims to answer is: why shouldn&#x2019;t you?</p><p>It&#x2019;s not enough to follow an instruction without understand why it is advised.</p><p>Let&#x2019;s explore this.</p><h2 id="the-fundamental-difference">The fundamental difference</h2><p>My initial thoughts when I first stumbled on this rule was that it had to do with the type system within Typescript.</p><p>However, that&#x2019;s not the case.</p><p>The real reason lies in Javascript itself.</p><p>Standard Javascript has seven primitive values types: <code>string</code>, <code>boolean</code>, <code>null</code>, <code>undefined</code>, <code>symbol</code> and <code>bigint</code>.</p><p>As you may be aware of, all Javascript primitives are rightly represented in the Typescript type system.</p><p>So, all of these are valid:</p><pre><code class="language-ts">// string
const hello = (v: string) =&gt; {
  console.log(v)
}

// boolean 
const hello = (v: boolean) =&gt; {
  console.log(v)
}

// null
const hello = (v: null) =&gt; {
  console.log(v)
}

// undefined
const hello = (v: undefined) =&gt; {
  console.log(v)
}

// symbol
const hello = (v: symbol) =&gt; {
  console.log(v)
}

// bigint
const hello = (v: bigint) =&gt; {
  console.log(v)
}
</code></pre><p>On the other hand, standard Javascript also has associating objects for most primitives, e.g., <code>String</code> to represent and manipulate a sequence of characters.</p><pre><code class="language-ts">// primitive 
const string = &quot;Hello world&quot; 

// String object 
const string2 = new String(&quot;Hello world&quot;)
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-15-at-08.55.03@2x.png" class="kg-image" alt="Avoid Object wrapper Types in Typescript&#x2014;Here&apos;s why" loading="lazy"><figcaption>Creating a string with the primitive and String constructor</figcaption></figure><p>Understanding why you shouldn&#x2019;t use these object wrapper types in Typescript really boils down to you understanding the difference in standard Javascript.</p><p>For example, primitives technically do <strong>not have methods</strong> and are <strong>immutable</strong>.</p><p>However, you can call a method on a string primitive as shown below:</p><pre><code class="language-ts">&quot;Hello world&quot;.toLowerCase()
</code></pre><p>If primitives do not have methods, then this should NOT be possible.</p><p>That is partly true.</p><p>Yes, the primitive has no methods, but Javascript defines a <code>String</code> object type that does.</p><p>So, what really happens internally is Javascript converts between the primitive and object.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-15-at-09.53.16@2x.png" class="kg-image" alt="Avoid Object wrapper Types in Typescript&#x2014;Here&apos;s why" loading="lazy"><figcaption>Javascript freely converts between the primitive and Object</figcaption></figure><p>When you call <code>toLowerCase</code> on the primitive, Javascript wraps it in a <code>String</code> object, calls the <code>toLowerCase</code> method, and then throws the object away.</p><p>An interesting consequence that explains is when you assign a property to a primitive.</p><p>For example:</p><pre><code class="language-ts">const greeting = &quot;Hola Mundo&quot; // primitive
greeting.language = &quot;Spanish&quot; // assign a property to the primitive
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-15-at-09.03.13@2x.png" class="kg-image" alt="Avoid Object wrapper Types in Typescript&#x2014;Here&apos;s why" loading="lazy"><figcaption>Assigning a property to a primitive</figcaption></figure><p>You&#x2019;ll get no errors when you do this.</p><p>Everything seems fine, but is it?</p><p>Attempt to access the language property afterwards, e.g.:</p><pre><code class="language-ts">greeting.language
// undefined
</code></pre><p>And you get <code>undefined</code>.</p><p>Where did the <code>language</code> property go?</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-15-at-09.04.08@2x.png" class="kg-image" alt="Avoid Object wrapper Types in Typescript&#x2014;Here&apos;s why" loading="lazy"><figcaption>Undefined property upon retrieval</figcaption></figure><p>You already know the explanation here.</p><p>When you set the <code>language</code> property, the primitive was converted to the <code>String</code> object and the <code>language</code> property set on that object. Then, the object (and its language property) discarded!</p><h2 id="why-do-these-object-wrapper-types-exist">Why do these object wrapper types exist?</h2><p>I have explained so far with the <code>String</code> object wrapper type, but you have object wrapper types for other primitive as well (apart from <code>null</code> and <code>undefined</code>)</p><p>These wrapper types mostly exist for convenience. They provide methods on the primitive values and, perhaps more importantly (in real usage), they provide static methods such as <code>String.fromCharCode</code>.</p><p>Other than these, there&#x2019;s typically no reason you&#x2019;d want to use these, EVEN IN YOUR JAVASCRIPT CODE.</p><h2 id="the-typescript-distinction">The Typescript distinction</h2><p>Just as the object wrapper types differ from their primitives, Typescript models this distinction, i.e., providing types for both primitives and their object wrappers.</p><p>Essentially, you have the <code>string</code> and <code>String</code> type, <code>number</code> and <code>Number</code> type, etc.</p><p>If you&#x2019;re coming from some other programming language such as <code>Java</code> you may find yourself using the Object wrapper types e.g.:</p><pre><code class="language-ts">const hello = (v: String) =&gt; {
  console.log(v)
}
</code></pre><p>This seems to work fine:</p><pre><code class="language-ts">const hello = (v: String) =&gt; {
  console.log(v)
}

hello(&quot;world!&quot;)

hello(new String(&quot;world!&quot;))
</code></pre><p>But you&#x2019;ll run into issues when you start to use Javascript methods that expect a primitive <code>string</code> e.g.,</p><pre><code class="language-ts">const isValidHello = (v: String) =&gt; {
  console.log([&quot;Hello&quot;].includes(v)) // error here
}
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-15-at-09.18.05@2x.png" class="kg-image" alt="Avoid Object wrapper Types in Typescript&#x2014;Here&apos;s why" loading="lazy"><figcaption>Error: the includes method expects a string primitive</figcaption></figure><p><a href="https://www.typescriptlang.org/play?#code/MYewdgzgLgBAFgUwDZJDAvDAFANwFwwDKUATgJZgDmAlBgHwwDeAUDDKJCEggHSqW5qzAL7NmiFCCwAiAO4gSSACYBCaUPHJUWMAllFSFAXIXK11DR2gwyEAGoBDJGSUAJLWky4CxclVroDCxsVly8-FgA2tLuktIAujwUwEgArkoIEIJCwkA">Typescript playground</a></p><p><code>string</code> is assignable to <code>String</code>, but <code>String</code> is not assignable to <code>string</code> &#x1F92F;</p><p>Save yourself the hassle and stick to the primitive types.</p><p>Most type declarations that ship with Typescript use it, you should do the same.</p><h2 id="conclusion">Conclusion</h2><p>Typescript is a superset of Javascript. It&#x2019;s only fair that it models the underlying Javascript system.</p><p>The object wrapper types exist for completeness and in the rare case that you actually need them. Otherwise, don&#x2019;t use them. Stick to the primitive types.</p><p>Cheers &#x1F389;</p><p></p><h2 id="fancy-some-free-typescript-resources"><strong>Fancy some free Typescript resources?</strong></h2><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/7-ts-2.png" class="kg-image" alt="Avoid Object wrapper Types in Typescript&#x2014;Here&apos;s why" loading="lazy"><figcaption><a href="https://www.ohansemmanuel.com/cheatsheet/top-7-stack-overflowed-typescript-questions">Download the cheatsheet</a></figcaption></figure><p>Get the <a href="https://www.ohansemmanuel.com/cheatsheet/top-7-stack-overflowed-typescript-questions">PDF or ePub</a> in exchange you sign up for my newsletter. No spam, ever. Unsubscribe at any time.</p><h2></h2><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/image-4.png" class="kg-image" alt="Avoid Object wrapper Types in Typescript&#x2014;Here&apos;s why" loading="lazy"><figcaption>Build strongly typed Polymorphic React components book</figcaption></figure><p><a href="https://www.ohansemmanuel.com/books/how-to-build-strongly-typed-polymorphic-react-components">Get this book for free</a> in exchange you sign up for my newsletter. No spam, ever. Unsubscribe at any time.</p>]]></content:encoded></item><item><title><![CDATA[The Seven Most Asked Typescript Questions on StackOverflow — Explained]]></title><description><![CDATA[<p><em>I hate Stackoverflow</em> &#x2014; said no developer.</p><p>While it&#x2019;s helpful to have your answers a Google search away, what&#x2019;s powerful is truly understanding the solutions you stumble on.</p><p>In this article, I&#x2019;ll explore the seven most <em>stackoverflowed</em> Typescript questions.</p><p>I spent hours researching these.</p>]]></description><link>https://blog.ohansemmanuel.com/the-seven-most-asked-typescript-questions-on-stackoverflow-explained/</link><guid isPermaLink="false">62c73b6443e529f14fb262c4</guid><category><![CDATA[typescript]]></category><dc:creator><![CDATA[Ohans Emmanuel]]></dc:creator><pubDate>Thu, 07 Jul 2022 20:10:17 GMT</pubDate><media:content url="https://blog.ohansemmanuel.com/content/images/2022/07/combined-blog-cover--1-.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.ohansemmanuel.com/content/images/2022/07/combined-blog-cover--1-.png" alt="The Seven Most Asked Typescript Questions on StackOverflow &#x2014; Explained"><p><em>I hate Stackoverflow</em> &#x2014; said no developer.</p><p>While it&#x2019;s helpful to have your answers a Google search away, what&#x2019;s powerful is truly understanding the solutions you stumble on.</p><p>In this article, I&#x2019;ll explore the seven most <em>stackoverflowed</em> Typescript questions.</p><p>I spent hours researching these.</p><p>I hope you gain a deeper understanding of the common problems you may face with Typescript.</p><p>This is also relevant if you&#x2019;re just learning Typescript &#x2014; what better way than to get familiar with your future challenges!</p><p>Let&#x2019;s get right into it.</p><h2 id="table-of-contents">Table of contents</h2><ol><li><a href="#1-what-is-the-difference-between-interfaces-vs-types-in-typescript">What is the difference between Interfaces vs Types in Typescript?</a></li><li><a href="#2-in-typescript-what-is-the-exclamation-mark-bang-operator">In Typescript, what is the ! (exclamation mark / bang) operator?</a></li><li><a href="#3-what-is-a-%E2%80%9Cdts%E2%80%9D-file-in-typescript">What is a &#x201C;.d.ts&#x201D; file in TypeScript?</a></li><li><a href="#4-how-do-you-explicitly-set-a-new-property-on-%E2%80%98window%E2%80%99-in-typescript">How Do You Explicitly Set a New Property on &#x2018;window&#x2019; in Typescript?</a></li><li><a href="#5-are-strongly-typed-functions-as-parameters-possible-in-typescript">Are Strongly Typed Functions as Parameters Possible in Typescript?</a></li><li><a href="#6-how-to-fix-could-not-find-declaration-file-for-module-%E2%80%A6">How to Fix Could Not Find Declaration File for Module &#x2026;?</a></li><li><a href="#7-how-do-i-dynamically-assign-properties-to-an-object-in-typescript">How Do I Dynamically Assign Properties to an Object in Typescript?</a></li></ol><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/7-ts-1-1.png" class="kg-image" alt="The Seven Most Asked Typescript Questions on StackOverflow &#x2014; Explained" loading="lazy"><figcaption>PDF or Epub version of this cheatsheet available</figcaption></figure><p><strong>Note:</strong> You can get a <a href="https://www.ohansemmanuel.com/cheatsheet/top-7-stack-overflowed-typescript-questions">PDF or ePub</a>, version of this cheatsheet for easier reference, or for reading on your Kindle or tablet.</p><h1 id="1-what-is-the-difference-between-interfaces-vs-types-in-typescript">1. What is the difference between Interfaces vs Types in Typescript?</h1><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/DraggedImage.png" class="kg-image" alt="The Seven Most Asked Typescript Questions on StackOverflow &#x2014; Explained" loading="lazy"><figcaption>Interfaces vs Types in Typescript</figcaption></figure><p>The interfaces vs types (technically, type alias) conversation is a well contested one.</p><p>When beginning Typescript, you may find it confusing to settle on a choice. This article clears up the confusion and helps you choose which is right for you.</p><h2 id="tldr">TLDR</h2><p>In numerous instances, you can use either an interface or type alias exchangeably.</p><p>Almost all features of an interface are available via type aliases, except you cannot add new properties to a type by re-declaring it. You must use an intersection type.</p><h2 id="why-the-confusion-in-the-first-place">Why the Confusion in the first place</h2><p>Whenever we&#x2019;re faced with multiple options, most people begin to suffer from the <a href="https://en.wikipedia.org/wiki/The_Paradox_of_Choice">paradox of choice</a>.</p><p>In this case, there are just two options.</p><p>What&#x2019;s so confusing about this?</p><p>Well, the main confusion here stems from the fact that these two options are so <strong>evenly matched</strong> in most regards.</p><p>This makes it difficult to make an obvious choice &#x2014; especially if you&#x2019;re just starting out with Typescript.</p><h2 id="a-basic-example">A Basic Example</h2><p>Let&#x2019;s get on the same page with quick examples of an interface and a type alias.</p><p>Consider the representations of a <code>Human</code> type below:</p><pre><code class="language-ts">// type 
type Human = {
  name: string 
  legs: number 
  head: number
}

// interface 
interface Human {
  name: string 
  legs: number 
  head: number
}
</code></pre><p>These are both correct ways to denote the <code>Human</code> type, i.e., via a type alias or an interface.</p><h2 id="the-differences-between-type-alias-and-interfaces">The differences between Type alias and Interfaces</h2><p>Below are the main differences between a type alias and an interface:</p><h3 id="key-difference-interfaces-can-only-describe-object-shapes-type-aliases-can-be-used-for-other-types-such-as-primitives-unions-and-tuples">Key difference: interfaces can only describe object shapes. Type aliases can be used for other types such as primitives, unions and tuples.</h3><p>A type alias is quite flexible in the data types you can represent. From basic primitives to complex unions and tuples, as shown below:</p><pre><code class="language-ts">// primitives 
type Name = string 

// object 
type Male = {
  name: string
}

type Female = {
  name: string 
}

// union
type HumanSex = Male | Female

// tuple
type Children = [Female, Male, Female]
</code></pre><p>Unlike, type aliases, you may only represent object types with an interface.</p><h3 id="key-difference-interfaces-can-be-extended-by-declaring-it-multiple-times">Key difference: interfaces can be extended by declaring it multiple times</h3><p>Consider the following example:</p><pre><code class="language-ts">interface Human {
  name: string 
}

interface Human {
  legs: number 
}
</code></pre><p>The two declarations above will become:</p><pre><code class="language-ts">interface Human {
  name: string 
  legs: number 
}
</code></pre><p><code>Human</code> will be treated as a single interface: a combination of the members of both declarations.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-06-27-at-07.23.42.png" class="kg-image" alt="The Seven Most Asked Typescript Questions on StackOverflow &#x2014; Explained" loading="lazy"><figcaption>Property &apos;legs&apos; is required in type &apos;Humans&apos;</figcaption></figure><p>See <a href="https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgBIFcC2cTIN4BQyxyIcmEAXMgM5hSgDmBAvgaJLIihtroSWQAbCIxrUQWAEbRWBAgHoFyMAAtgNZNCgB7KJp3owyGQjjoaKAOQixV5JgvGIADw3GCCHSDrJV1XhxkAF58IhIyCmorAHlVHE0AUUw+dAghK1YgA">Typescript playground</a>.</p><p>This is not the case with type aliases.</p><p>With a type alias, the following will lead to an error:</p><pre><code class="language-ts">type Human = {
    name: string 
}
  
type Human =  {
    legs: number 
}

const h: Human = {
   name: &apos;gg&apos;,
   legs: 5 
}  
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-06-27-at-07.24.27.png" class="kg-image" alt="The Seven Most Asked Typescript Questions on StackOverflow &#x2014; Explained" loading="lazy"><figcaption>Duplicate identifier &apos;Human&apos; error</figcaption></figure><p>See <a href="https://www.typescriptlang.org/play?#code/C4TwDgpgBAEgrgWwIYDsoF4oG8BQV9QpIIQBcUAzsAE4CWKA5lDgL57OiSyKob64EoAGwgMK5FIgBGEaszY4AxgHsUVKAAty8ZGkwD8REuQDkDBiYA07YaPFQArPPw5XroA">Typescript playground</a>.</p><p>With Type aliases, you&#x2019;ll have to resort to an intersection type:</p><pre><code class="language-ts">type HumanWithName = {
    name: string 
}
  
type HumanWithLegs =  {
    legs: number 
}

type Human  = HumanWithName &amp; HumanWithLegs

const h: Human = {
   name: &apos;gg&apos;,
   legs: 5 
}  
</code></pre><p>See <a href="https://www.typescriptlang.org/play?#code/C4TwDgpgBAEgrgWwIYDsDqBLYALAckhaAXigG8AoKKqFAiALigGdgAnDFAcynIF9KeoSLESpMOADIROTKCTICqAG2lNGKRACMIrHv3JDo8ZCioljYrHjpQAZCJPjsUmeXIBjAPYoWUbIwtTEgpqWkJGAHJOTgiAGkUVGUYAVj0qNwygA">Typescript playground</a></p><h3 id="minor-difference-both-can-be-extended-but-with-different-syntaxes">Minor difference: Both can be extended, but with different syntaxes</h3><p>With interfaces, you use the <code>extends</code> keyword. For types, you must use an intersection.</p><p>Consider the following examples:</p><h4 id="type-alias-extends-type-alias">Type alias extends type alias</h4><pre><code class="language-ts">
type HumanWithName = {
  name: string 
}

type Human = HumanWithName &amp; {
   legs: number 
   eyes: number 
}
</code></pre><h4 id="type-alias-extends-an-interface">Type alias extends an interface</h4><pre><code class="language-ts">interface HumanWithName {
  name: string 
}

type Human = HumanWithName &amp; {
   legs: number 
   eyes: number 
} 
</code></pre><h4 id="interface-extends-interface">Interface extends interface</h4><pre><code class="language-ts">interface HumanWithName {
  name: string 
}

interface Human extends HumanWithName {
  legs: number 
  eyes: number 
}
</code></pre><h4 id="an-interface-extends-a-type-alias">An interface extends a type alias</h4><pre><code class="language-ts">type HumanWithName = {
  name: string
}

interface Human extends HumanWithName {
  legs: number 
  eyes: number 
}
</code></pre><p>As you can see, this is not particularly a reason to choose one over the other. However, the syntaxes differ.</p><h3 id="minor-difference-classes-can-only-implement-statically-known-members">Minor difference: classes can only implement statically known members</h3><p>A class can implement both interfaces or type aliases. However, a class cannot implement or extend a union type.</p><p>Consider the following example:</p><h4 id="class-implements-interface">Class implements interface</h4><pre><code class="language-ts">interface Human {
  name: string
  legs: number 
  eyes: number 
}

class FourLeggedHuman implements Human {
  name = &apos;Krizuga&apos;
  legs = 4
  eyes = 2
}
</code></pre><h4 id="class-implements-type-alias">Class implements type alias</h4><pre><code class="language-ts">type Human = {
  name: string
  legs: number 
  eyes: number 
}

class FourLeggedHuman implements Human {
  name = &apos;Krizuga&apos;
  legs = 4
  eyes = 2
}
</code></pre><p>Both these work without any errors. However, the following fails:</p><h4 id="class-implements-union-type">Class implements union type</h4><pre><code class="language-ts">type Human = {
    name: string
} | {
    legs: number
    eyes: number
}

class FourLeggedHuman implements Human {
    name = &apos;Krizuga&apos;
    legs = 4
    eyes = 2
}
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-06-27-at-07.27.07.png" class="kg-image" alt="The Seven Most Asked Typescript Questions on StackOverflow &#x2014; Explained" loading="lazy"><figcaption>A class can only implement an object type or intersection of object types with statically known members.</figcaption></figure><p>See <a href="https://www.typescriptlang.org/play?#code/C4TwDgpgBAEgrgWwIYDsoF4oG8BQV9QpIIQBcUAzsAE4CWKA5jgL5QA+2eBANhAxeRSIARhGpd8EEBAGERYljhwBjbkgoUoAMQD2cagBk+DCABN4yNLQRheJFME0XUnAoWLRMAcgDSdAF5wDEheElC8-BhQACxhUjJRAEwsQA">Typescript playground</a>.</p><h2 id="summary">Summary</h2><p>Your mileage may differ, but wherever possible, I stick to type aliases for their flexibility and simpler syntax, i.e., I pick type aliases except I specifically need features from an interface.</p><p>For the most part, you can also decide based on your personal preference, but stay consistent with your choice &#x2014; at least in a single given project.</p><p>For completeness, I must add that in performance critical types, interface comparison checks can be faster than type aliases. I&#x2019;m yet to find this to be an issue.</p><h1 id="2-in-typescript-what-is-the-exclamation-mark-bang-operator">2: In Typescript, what is the ! (exclamation mark / bang) operator?</h1><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/blog-2-1.png" class="kg-image" alt="The Seven Most Asked Typescript Questions on StackOverflow &#x2014; Explained" loading="lazy"><figcaption>What is the bang operator in Typescript?</figcaption></figure><h2 id="tldr-1">TLDR</h2><p>This is technically called the <strong>non-null assertion operator</strong>. If the typescript compiler complains about a value being <code>null</code> or <code>undefined</code>, you can use the <code>!</code> operator to assert that the said value is not <code>null</code> or <code>undefined</code>.</p><p>Personal take: avoid doing this wherever possible.</p><h2 id="what-is-the-non-null-assertion-operator">What is the non-null assertion operator?</h2><p><code>null</code> and <code>undefined</code> are valid Javascript values.</p><p>The statement above holds true for all Typescript applications as well.</p><p>However, Typescript goes one step further.</p><p><code>null</code> and <code>undefined</code> are equally valid types, e.g., consider the following:</p><pre><code class="language-ts">// explicit null
let a: null 

a = null
// the following assignments will yield errors
a= undefined 
a = {}


// explicit undefined
let b: undefined 
// the following assignments will yield errors
b = null 
b = {}
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-04-at-06.21.57@2x-1.png" class="kg-image" alt="The Seven Most Asked Typescript Questions on StackOverflow &#x2014; Explained" loading="lazy"><figcaption>Error: values other than null and undefined not assignable</figcaption></figure><p>See <a href="https://www.typescriptlang.org/play?#code/DYUwLgBAhgXBB2BXYwICg1QgXgc4aA9IRGABYgQBmA9ijQO4CW8A5tAM4dOvwC2IeGA4RmKCAE8mIYABMIIAE6KaijplyJ4skFRYh5mHBADeAXwxpQkAEZwtOvfAPpipCtTrBGLdlC48-ILCokziUjLySipqaDbGSOJxxuZoQA">Typescript playground</a>.</p><p>In certain cases, the Typescript compiler cannot tell whether a certain value is defined or not, i.e., not <code>null</code> or <code>undefined</code>.</p><p>For example, assuming you had a value <code>Foo</code>.</p><p><code>Foo!</code> produces a value of the type of <code>Foo</code> with <code>null</code> and <code>undefined</code> excluded.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-07-at-16.47.49@2x-1.png" class="kg-image" alt="The Seven Most Asked Typescript Questions on StackOverflow &#x2014; Explained" loading="lazy"><figcaption>Foo! excludes null and undefined from the type of Foo</figcaption></figure><p>You essentially say to the Typescript compiler, <em>I am sure, <code>Foo</code> will NOT be <code>null</code> or <code>undefined</code></em></p><p>Let&#x2019;s explore a naive example.</p><p>In standard Javascript, you may concatenate two strings with the <code>.concat</code> method:</p><pre><code class="language-ts">const str1 = &quot;Hello&quot; 
const str2 = &quot;World&quot;

const greeting = str1.concat(&apos; &apos;, str2)
// Hello World
</code></pre><p>Write a simple duplicate string function that calls <code>.concat</code> with itself as an argument:</p><pre><code class="language-ts">function duplicate(text: string | null) {
  return text.concat(text);
}
</code></pre><p>Note that the argument <code>text</code> is typed as <code>string | null</code></p><p>In strict mode, Typescript will complain here, as calling <code>concat</code> with <code>null</code> can lead to unexpected results.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-04-at-06.42.57@2x-1.png" class="kg-image" alt="The Seven Most Asked Typescript Questions on StackOverflow &#x2014; Explained" loading="lazy"><figcaption>The result of calling concat with null</figcaption></figure><p>The typescript error will read: <code>Object is possibly &apos;null&apos;.(2531)</code></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-04-at-06.44.36@2x-1.png" class="kg-image" alt="The Seven Most Asked Typescript Questions on StackOverflow &#x2014; Explained" loading="lazy"><figcaption>Typescript error: Object is possibly null</figcaption></figure><p>On the flip side, a rather lazy way to silence the compiler error is to use the non-null assertion operator:</p><pre><code class="language-ts">function duplicate(text: string | null) {
  return text!.concat(text!);
}
</code></pre><p>Note the exclamation mark after the <code>text</code> variable, i.e, <code>text!</code></p><p>The <code>text</code> type represents <code>string | null</code>.</p><p><code>text!</code> represents just <code>string</code> i.e., with <code>null</code> or <code>undefined</code> removed from the variable type.</p><p>The result? You&#x2019;ve silenced the Typescript error.</p><p>However, this is a silly fix.</p><p><code>duplicate</code> can indeed be called with <code>null</code>, which may lead to unexpected results.</p><p>Note that the following example also holds true if <code>text</code> is an optional property:</p><pre><code class="language-ts">// text could be &quot;undefined&quot;
function duplicate(text?: string) {
  return text!.concat(text!);
}
</code></pre><h2 id="pitfalls-and-what-to-do">Pitfalls (and what to do)</h2><p>When working with Typescript as a new user, you may feel like you&#x2019;re fighting a lost battle.</p><p>The errors don&#x2019;t make sense to you.</p><p>Your goal is to remove the error and move on with your life as swiftly as you can.</p><p>However, you should be careful with using the non-null assertion operator.</p><p>Silencing a Typescript error doesn&#x2019;t mean there may not still be an underlying issue&#x2014;if unaddressed.</p><p>As you saw in the earlier example, you lose every relevant Typescript safety against wrong usages where <code>null</code> and <code>undefined</code> could be unwanted.</p><p>So, what should you do?</p><p>If you write React, consider an example you&#x2019;re likely familiar with:</p><pre><code class="language-ts">const MyComponent = () =&gt; {
   const ref = React.createRef&lt;HTMLInputElement&gt;();
	
   //compilation error: ref.current is possibly null
   const goToInput = () =&gt; ref.current.scrollIntoView(); 

    return (
       &lt;div&gt;
           &lt;input ref={ref}/&gt;
           &lt;button onClick={goToInput}&gt;Go to Input&lt;/button&gt;
       &lt;/div&gt;
   );
};
</code></pre><p>In the example above (for those who do not write React), in the <code>React</code> mental model, <code>ref.current</code> will certainly be available at the time the button is clicked by the user.</p><p>The <code>ref</code> object is set soon after the UI elements are rendered.</p><p>Typescript does not know this, and you may be forced to use the non-null assertion operator here.</p><p>Essentially, say to the Typescript compiler, I know what I&#x2019;m doing, you don&#x2019;t.</p><pre><code class="language-ts">const goToInput = () =&gt; ref.current!.scrollIntoView();
</code></pre><p>Note the exclamation mark <code>!</code>.</p><p>This &#x201C;fixes&#x201D; the error.</p><p>However, if in the future, someone removes the <code>ref</code> from the input, and there were no automated tests to catch this, you now have a bug.</p><pre><code class="language-ts">// before
&lt;input ref={ref}/&gt;

// after
&lt;input /&gt;
</code></pre><p>Typescript will be unable to spot the error in the following line:</p><pre><code class="language-ts">const goToInput = () =&gt; ref.current!.scrollIntoView();
</code></pre><p>By using the non-null assertion operator, the Typescript compiler will act as if <code>null</code> and <code>undefined</code> are never possible for the value in question. In this case, <code>ref.current</code>.</p><h3 id="solution-1-find-an-alternative-fix">Solution 1: find an alternative fix</h3><p>The first line of action you should employ is to find an alternative fix.</p><p>For example, often you can explicitly check for <code>null</code> and <code>undefined</code> values.</p><pre><code class="language-ts">// before 
const goToInput = () =&gt; ref.current!.scrollIntoView();

// now 
const goToInput = () =&gt; {
  if (ref.current) {
   //Typescript will understand that ref.current is certianly 
   //avaialble in this branch
     ref.current.scrollIntoView()
  }
};

// alternatively (use the logical AND operator)
const goToInput = () =&gt; ref.current &amp;&amp; ref.current.scrollIntoView();
</code></pre><p>Numerous engineers will argue over the fact that this is more verbose.</p><p>That&#x2019;s correct.</p><p>However, choose verbose over possibly breaking code being pushed to production.</p><p>This is a personal preference. Your mileage may differ.</p><h3 id="solution-2-explicitly-throw-an-error">Solution 2: explicitly throw an error</h3><p>In cases where an alternative fix doesn&#x2019;t cut it and non-null assertion operator seems like the only solution, I typically advise you throw an error before doing this.</p><p>Here&#x2019;s an example (in pseudocode):</p><pre><code class="language-ts">function doSomething (value) {
   // for some reason TS thinks value could be  
   // null or undefined but you disagree
   
  if(!value) {
    // explicilty assert this is the case 
    // throw an error or log this somewhere you can trace
    throw new Error(&apos;uexpected error: value not present&apos;)
  } 

  // go ahead and use the non-null assertion operator
  console.log(value)
}
</code></pre><p>A practical case where I&#x2019;ve found myself sometimes doing this is while using <code>Formik</code>.</p><p>Except things have changed, I do think <code>Formik</code> is poorly typed in numerous instances.</p><p>The example may go similar to you&apos;ve done your Formik validation and are sure that your values exist.</p><p>Here&#x2019;s some pseudocode:</p><pre><code class="language-ts">&lt;Formik 
  validationSchema={...} 
  onSubmit={(values) =&gt; {
   // you are sure values.name should exist because you had 
   // validated in validationSchema but Typescript doesn&apos;t know this

   if(!values.name) {
    throw new Error(&apos;Invalid form, name is required&apos;)		
   } 
   console.log(values.name!)
}}&gt;


&lt;/Formik&gt;
</code></pre><p>In the pseudocode above, <code>values</code> could be typed as:</p><pre><code class="language-ts">type Values = {
  name?: string
}
</code></pre><p>But before you hit <code>onSubmit</code>, you&#x2019;ve added some validation to show a UI form error for the user to input a <code>name</code> before moving on to the form submission.</p><p>There are other ways to get around this, but if you find yourself in such a case where you&#x2019;re sure a value exists but can&#x2019;t quite communicate that to the Typescript compiler, use the non-null assertion operator. But also add an assertion of your own by throwing an error you can trace.</p><h2 id="how-about-an-implicit-assertion">How about an implicit assertion?</h2><p>Even though the name of the operator reads non-null assertion operator, no &#x201C;assertion&#x201D; is actually being made.</p><p>You&#x2019;re mostly asserting (as the developer), that the value exists.</p><p>The Typescript compiler does NOT assert that this value exists.</p><p>So, if you must, you may go ahead and add your assertion, e.g., as discussed in the earlier section.</p><p>Also, note that no more Javascript code is emitted by using the non-null assertion operator.</p><p>As stated earlier, there&#x2019;s no assertion done here by Typescript.</p><p>Consequently, Typescript will not emit some code that checks if this value exists or not.</p><p>The Javascript code emitted will act as if this value always existed.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-04-at-07.14.51@2x-1.png" class="kg-image" alt="The Seven Most Asked Typescript Questions on StackOverflow &#x2014; Explained" loading="lazy"><figcaption>Emitted javascript code same as Javascript</figcaption></figure><h2 id="conclusion">Conclusion</h2><p>TypeScript 2.0 saw the release of the <strong>non-null assertion operator</strong>. Yes, it&#x2019;s been around for some time (<a href="https://github.com/microsoft/TypeScript/releases/tag/v2.0.3">released in 2016</a>). At the time of writing, the latest version of Typescript is <code>v4.7</code>.</p><p>If the typescript compiler complains about a value being <code>null</code> or <code>undefined</code>, you can use the <code>!</code> operator to assert that the said value is not null or undefined.</p><p>Only do this if you&#x2019;re certain that is the case.</p><p>Even better, go ahead and add an assertion of your own, or try to find an alternative solution.</p><p>Some may argue that if you need to use the non-null assertion operator every time, it&#x2019;s a sign you&#x2019;re poorly representing the state of your application state via Typescript.</p><p>I agree with this school of thought.</p><h1 id="3-what-is-a-%E2%80%9Cdts%E2%80%9D-file-in-typescript">3: What is a &#x201C;.d.ts&#x201D; file in TypeScript?</h1><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/blog-3--1-.png" class="kg-image" alt="The Seven Most Asked Typescript Questions on StackOverflow &#x2014; Explained" loading="lazy"><figcaption>What is a d.ts file?</figcaption></figure><h2 id="tldr-2">TLDR</h2><p><code>.d.ts</code> files are called type declaration files. They exist for one purpose only: to describe the shape of an existing module and they only contain type information used for type checking.</p><h2 id="introduction">Introduction</h2><p>Upon learning the basics of TypeScript, you unlock superpowers.</p><p>At least that&#x2019;s how I felt.</p><p>You automagically get warnings on potential errors, you get auto-completion out of the box in your code editor.</p><p>While seemingly magical, nothing with computers are.</p><p>So, what&#x2019;s the trick here, TypeScript?</p><p>In a clearer language, how does TypeScript know so much? How does it decide what API is correct or not? What methods are available on a certain object or class, and which aren&#x2019;t?</p><p>The answer is less magical.</p><p>TypeScript relies on types.</p><p>Occasionally, you do not write these types, but they exist.</p><p>They exist in files called declaration files.</p><p>These are files with a <code>.d.ts</code> ending.</p><h2 id="a-simple-example">A simple example</h2><p>Consider the following TypeScript code:</p><pre><code class="language-ts">// valid 
const amount = Math.ceil(14.99)

// error: Property &apos;ciil&apos; does not exist on type &apos;Math&apos;.(2339)
const otherAmount = Math.ciil(14.99)
</code></pre><p>See <a href="https://www.TypeScriptlang.org/play?#code/MYewdgzgLgBAhgWxAVzLAvDAsnKALAOmAFMBLAGwAoBGAFgIE4GBKAKFdElhH2ICcAgklQZsuQsFIUa9Jm1ZA">TypeScript playground</a>.</p><p>The first line of code is perfectly valid, but the second, not quite.</p><p>And TypeScript is quick to spot the error: <code>Property &apos;ciil&apos; does not exist on type &apos;Math&apos;.(2339)</code></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-05-at-06.01.49-1.png" class="kg-image" alt="The Seven Most Asked Typescript Questions on StackOverflow &#x2014; Explained" loading="lazy"><figcaption>The Typescript error spotting the wrong property access &quot;ciil&quot;</figcaption></figure><p>How did TypeScript know <code>ciil</code> does not exist on the <code>Math</code> object?</p><p>The <code>Math</code> object isn&#x2019;t a part of our implementation. It&#x2019;s a standard built-in object.</p><p>So, how did TypeScript figure that out?</p><p>The answer is there are <strong>declaration files</strong> that describe these built-in objects.</p><p>Think of a declaration file as containing all type information relating to a certain module. It contains no actual implementation, just type information.</p><p>These files have a <code>.d.ts</code> ending.</p><p>Your implementation files will either have <code>.ts</code> or <code>.js</code> endings to represent TypeScript or javascript files.</p><p>These declaration files have no implementations. They only contain type information and have a <code>.d.ts</code> file ending.</p><h2 id="built-in-type-definitions">Built-in Type Definitions</h2><p>A great way to understand this in practice is to set up a brand-new TypeScript project and explore the type definition files for top-level objects like <code>Math</code>.</p><p>Let&#x2019;s do this.</p><p>Create a new directory, and name it whatever&#x2019;s appropriate.</p><p>I&#x2019;ll call mine <code>dts</code>.</p><p>Change directories to this newly created folder:</p><pre><code class="language-ts">cd dts
</code></pre><p>Now initialise a new project:</p><pre><code class="language-ts">npm init --yes
</code></pre><p>Install TypeScript:</p><pre><code class="language-ts">npm install TypeScript --save-dev
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-05-at-06.17.57@2x-1.png" class="kg-image" alt="The Seven Most Asked Typescript Questions on StackOverflow &#x2014; Explained" loading="lazy"><figcaption>Installing TypeScript</figcaption></figure><p>This directory should contain 2 files and one subdirectory</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-05-at-06.21.06@2x-1.png" class="kg-image" alt="The Seven Most Asked Typescript Questions on StackOverflow &#x2014; Explained" loading="lazy"><figcaption>The files after installation</figcaption></figure><p>Open the folder in your favourite code editor.</p><p>If you investigate the <code>TypeScript</code> directory within <code>node_modules</code>, you&#x2019;ll find a bunch of type declaration files out of the box.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-05-at-06.22.37@2x-1.png" class="kg-image" alt="The Seven Most Asked Typescript Questions on StackOverflow &#x2014; Explained" loading="lazy"><figcaption>Type declaration files in the typescript directory</figcaption></figure><p>These are present courtesy of installing TypeScript.</p><p>By default, TypeScript will include type definition for all DOM APIs, e.g., think <code>window</code> and <code>document</code>.</p><p>As you inspect these type declaration files, you&#x2019;ll notice that the naming convention is straightforward.</p><p>It follows the pattern: <code>lib.[something].d.ts</code>.</p><p>Open up the <code>lib.dom.d.ts</code> declaration file to view all declarations related to the browser DOM API.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-05-at-06.26.40@2x-1.png" class="kg-image" alt="The Seven Most Asked Typescript Questions on StackOverflow &#x2014; Explained" loading="lazy"><figcaption>The dom declaration file</figcaption></figure><p>As you can see, this is quite a gigantic file.</p><p>But so are all the APIs available in the DOM.</p><p>Awesome!</p><p>Now, if you take a look at the <code>lib.es5.d.ts</code> file, you&#x2019;ll see the declaration for the <code>Math</code> object, containing the <code>ceil</code> property.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-07-at-18.20.04@2x-1.png" class="kg-image" alt="The Seven Most Asked Typescript Questions on StackOverflow &#x2014; Explained" loading="lazy"><figcaption>The Math object in the declaration file</figcaption></figure><p>Next time you think, wow, TypeScript is wonderful. Remember, a big part of that awesomeness is due to the lesser-known heroes: type declaration files.</p><p>It&#x2019;s not magic. Just type declaration files.</p><h2 id="external-type-definitions">External type definitions</h2><p>What about APIs that aren&#x2019;t built-in?</p><p>There&#x2019;s a host of <code>npm</code> packages out there to do just about anything you want.</p><p>Is there a way for TypeScript to also understand the relevant type relationships for the said module?</p><p>Well, the answer is a resounding yes.</p><p>There are typically two ways a library author may do this.</p><h3 id="1-bundled-types">(1) Bundled Types</h3><p>In this case, the author of the library has already bundled the type declaration files as part of the package distribution.</p><p>You typically don&#x2019;t need to do anything.</p><p>You just go ahead and install the library in your project, you import the required module from the library and see if TypeScript should automatically resolve the types for you.</p><p>Remember, this is not magic.</p><p>The library author has bundled the type declaration file in the package distribution.</p><h3 id="2-definitelytyped-types">(2) DefinitelyTyped (@types)</h3><p>Imagine a central public repository that hosts declaration files for thousands of libraries?</p><p>Well, bring that image home.</p><p>This repository already exists.</p><p>The <a href="https://github.com/DefinitelyTyped/DefinitelyTyped/">DefinitelyTyped repository</a> is a centralised repository that stores the declaration files for thousands of libraries.</p><p>In all honestly, the vast majority of commonly used libraries have declaration files available on <strong>DefinitelyTyped</strong>.</p><p>These type definition files are automatically published to <code>npm</code> under the <code>@types</code> scope.</p><p>For example, if you wanted to install the types for the <code>react</code> npm package, you&#x2019;d do this:</p><pre><code class="language-ts">npm install --save-dev @types/react
</code></pre><p>If you find yourself using a module whose types TypeScript does not automatically resolve, attempt installing the types directly from DefinitelyTyped.</p><p>See if the types exist there. e.g.:</p><pre><code class="language-ts">npm install --save-dev @types/your-library
</code></pre><p>Definition files added in this manner will be saved to <code>node_modules/@types</code>.</p><p>TypeScript will automatically find these. So, there&#x2019;s no additional step for you to take.</p><h2 id="writing-your-declaration-files">Writing your declaration files</h2><p>In the uncommon event that a library didn&#x2019;t bundle its types and does not have a type definition file on DefinitelyTyped, you can write your own declaration files.</p><p>Writing declaration files in-depth is beyond the scope of this article, but a use case you&#x2019;ll likely come across is silencing errors about a particular module without a declaration file.</p><p>Declaration files all have a <code>.d.ts</code> ending.</p><p>So to create yours, create a file with a <code>.d.ts</code> ending.</p><p>For example, assuming I have installed library <code>untyped-module</code> in my project.</p><p><code>untyped-module</code> has no referenced type definition files, so TypeScript complains about this in my project.</p><p>To silence this warning, I may create a new <code>untyped-module.d.ts</code> file in my project with the following content:</p><pre><code class="language-ts">declare module &quot;some-untyped-module&quot;;
</code></pre><p>This will declare the module as type <code>any</code>.</p><p>We won&#x2019;t get any TypeScript support for that module, but you&#x2019;d have silenced the TypeScript warning.</p><p>Ideal next steps would include opening an issue in the module&#x2019;s public repository to include a TypeScript declaration file, or writing out a decent one yourself.</p><h2 id="conclusion-1">Conclusion</h2><p>Next time you think, wow, TypeScript is remarkable. Remember, a big part of that awesomeness is due to the lesser-known heroes: type declaration files.</p><p>Now you understand how they work!</p><h1 id="4-how-do-you-explicitly-set-a-new-property-on-%E2%80%98window%E2%80%99-in-typescript">4: How Do You Explicitly Set a New Property on &#x2018;window&#x2019; in Typescript?</h1><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/blog-4--1--1.png" class="kg-image" alt="The Seven Most Asked Typescript Questions on StackOverflow &#x2014; Explained" loading="lazy"><figcaption>Set a new property on the window object?</figcaption></figure><h2 id="tldr-3">TLDR</h2><p>Extend the existing interface declaration for the <code>Window</code> object.</p><h2 id="introduction-1">Introduction</h2><p>Knowledge builds upon knowledge.</p><p>Whoever said that was right.</p><p>In this section, we will build upon the knowledge from the last two sections:</p><p><a href="https://blog.ohansemmanuel.com/interfaces-vs-types-in-typescript/">Interfaces vs Types in Typescript</a></p><p><a href="https://blog.ohansemmanuel.com/what-is-a-dts-file-in-typescript/">What is a d.t.s file in Typescript</a></p><p>Ready?</p><p>First, I must say, in my early days with Typescript, this was a question I googled over and over again.</p><p>I never got it. And I didn&#x2019;t bother, I just googled.</p><p>That&#x2019;s never the right mentality to gaining mastery over a subject.</p><p>Let&#x2019;s discuss the solutions to this.</p><h2 id="understanding-the-problem">Understanding the problem</h2><p>The problem here is actually straightforward to reason about.</p><p>Consider the following Typescript code:</p><pre><code class="language-ts">window.__MY_APPLICATION_NAME__ = &quot;freecodecamp&quot;

console.log(window.__MY_APPLICATION_NAME__)
</code></pre><p>Typescript is quick to let you know <code>__MY_APPLICATION_NAME__</code> does not exist on type &#x2018;Window &amp; typeof globalThis&#x2019;.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-05-at-07.05.11-1.png" class="kg-image" alt="The Seven Most Asked Typescript Questions on StackOverflow &#x2014; Explained" loading="lazy"><figcaption>The property does not exist on Window error</figcaption></figure><p>See <a href="%20https://www.typescriptlang.org/play?#code/O4SwdgJg9sB0D68CyBNeBBACpgMgSQGF0AVPAeQDl4L0kBRRAAgF5GAiAMwCcBTHgYygQBAQwC2ABzYAoaYLABnKABsesZVADmAClCQYCZGiy5CJclRr1EASln2gA">TypeScript playground</a>.</p><p>Okay, Typescript.</p><p>We get it.</p><p>On closer look, remember from the last section on <a href="https://blog.ohansemmanuel.com/what-is-a-dts-file-in-typescript/">declaration files</a> that there&#x2019;s a declaration file for all existing browser APIs. This includes built-in objects such as <code>window</code>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-05-at-07.06.53@2x-1.png" class="kg-image" alt="The Seven Most Asked Typescript Questions on StackOverflow &#x2014; Explained" loading="lazy"><figcaption>The default Window interface declaration</figcaption></figure><p>If you look in the <code>lib.dom.d.ts</code> declaration file, you&#x2019;ll find the <code>Window</code> interface described.</p><p>In layman&#x2019;s terms, the error here says the <code>Window</code> interface describes how I understand the <code>window</code> object and its usage. That interface does not specify a certain <code>__MY_APPLICATION_NAME__</code> property.</p><h2 id="fixing-the-error">Fixing the error</h2><p>In the Types vs interface section, I explained how to extend an interface.</p><p>Let&#x2019;s apply that knowledge here.</p><p>We can extend the <code>Window</code> interface declaration to become aware of the <code>__MY_APPLICATION_NAME__</code> property.</p><p>Here&#x2019;s how:</p><pre><code class="language-ts">// before
window.__MY_APPLICATION_NAME__ = &quot;freecodecamp&quot;

console.log(window.__MY_APPLICATION_NAME__)

// now 
interface Window {
  __MY_APPLICATION_NAME__: string
}

window.__MY_APPLICATION_NAME__ = &quot;freecodecamp&quot;

console.log(window.__MY_APPLICATION_NAME__)
</code></pre><p>Errors banished!</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-05-at-07.13.01-1.png" class="kg-image" alt="The Seven Most Asked Typescript Questions on StackOverflow &#x2014; Explained" loading="lazy"><figcaption>The resolved solution</figcaption></figure><p>See <a href="https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgOqgCYHsDuyDeAUMsgPqkCyAmqQIIAK9AMgJIDCtAKiwPIBypPrQoBRcgC5kAZzBRQAc0IBfQoRyZcAOnLU6jVh279BwsaWQBeZACIYUCBARYMjuAFsADtdVOQUrAA2EJoBWPIAFOog2DjalDQMzOxcvAJCouQAlEA">TypeScript playground</a>.</p><p>Remember that a key difference between types and interfaces is that interfaces can be extended by declaring it multiple times.</p><p>What we&#x2019;ve done here is declared the <code>Window</code> interface one more time, hence extending the interface declaration.</p><h3 id="a-real-world-solution">A real-world solution</h3><p>I&#x2019;ve solved this problem within the Typescript playground to show you the solution in its simplest form, i.e., the crux.</p><p>In the real world, though, you wouldn&#x2019;t extend the interface within your code.</p><p>So, what should you do instead?</p><p>Give it a guess, perhaps?</p><p>Yes, you were close &#x2026; or perhaps right!</p><p>Create a type definition file!</p><p>For example, create a <code>window.d.ts</code> file with the following content:</p><pre><code class="language-ts">interface Window {
  __MY_APPLICATION_NAME__: string
}
</code></pre><p>And there you go.</p><p>You&#x2019;ve successfully extended the <code>Window</code> interface and solved the problem.</p><p>If you went ahead to assign the wrong value type to the <code>__MY_APPLICATION_NAME__</code> property, you now have strong type checking enabled.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-05-at-07.21.57-1.png" class="kg-image" alt="The Seven Most Asked Typescript Questions on StackOverflow &#x2014; Explained" loading="lazy"><figcaption>A wrong assignment to the newly defined property caught</figcaption></figure><p>See <a href="https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgOqgCYHsDuyDeAUMsgPqkCyAmqQIIAK9AMgJIDCtAKiwPIBypPrQoBRcgC5kAZzBRQAc0IBfQoRyZcAOnLU6jVh279BwsaWQBeAsWQg4AWwiSARDCgQICLBk8OADs7Kql4gUlgANhCa4VjyABTqINg42pQ0DMzsXLwCQqLkAJSqxUA">Typescript playground</a>.</p><p><em>Voil&#xE0;.</em></p><h2 id="conclusion-2">Conclusion</h2><p>In <a href="https://stackoverflow.com/questions/12709074/how-do-you-explicitly-set-a-new-property-on-window-in-typescript">older stack overflow posts</a>, you&#x2019;ll find more complicated answers based on older Typescript versions.</p><p>The solution is easier to reason about in modern Typescript.</p><p>Now you know &#x1F609;</p><h1 id="5-are-strongly-typed-functions-as-parameters-possible-in-typescript">5: Are Strongly Typed Functions as Parameters Possible in Typescript?</h1><h2 id="tldr-4">TLDR</h2><p>This question does not need to be overly explained. The short answer is yes.</p><p>Functions can be strongly typed &#x2014; even as parameters to other functions.</p><h2 id="introduction-2">Introduction</h2><p>I must say, unlike other sections of this article, I never really found myself searching for this in my early Typescript days.</p><p>However, that&#x2019;s not what&#x2019;s most important.</p><p>It is a well-searched question, so let&#x2019;s answer it!</p><h2 id="how-to-use-strongly-typed-function-parameters">How to use strongly typed function parameters</h2><p>The accepted answer on this <a href="https://stackoverflow.com/questions/14638990/are-strongly-typed-functions-as-parameters-possible-in-typescript">stack overflow post</a> is correct &#x2014; to a degree.</p><p>Assuming you had a function: <code>speak</code>:</p><pre><code class="language-ts">function speak(callback) {
  const sentence = &quot;Hello world&quot;
  alert(callback(sentence))
}
</code></pre><p>It receives a <code>callback</code> that&#x2019;s internally invoked with a <code>string</code>.</p><p>To type this, go ahead and represent the <code>callback</code> with a function type alias:</p><pre><code class="language-ts">type Callback = (value: string) =&gt; void
</code></pre><p>And type the <code>speak</code> function as follows:</p><pre><code class="language-ts">function speak(callback: Callback) {
  const sentence = &quot;Hello world&quot;
  alert(callback(sentence))
}
</code></pre><p>Alternatively, you could also keep the type inline:</p><pre><code class="language-ts">function speak(callback: (value: string) =&gt; void) {
  const sentence = &quot;Hello world&quot;

  alert(callback(sentence))
}
</code></pre><p>See <a href="https://www.typescriptlang.org/play?#code/GYVwdgxgLglg9mABAZwA4FMCGBrAFBTAG0ICNMJsAuRXANyJHWuSgCcYwBzASkQF4AfIlpwYAE14BvAFCJEEBCxTowUFRHT9EAIgAS6YnEQB3OK0Jjt02YiLpWUfEVLk8yFWsjpu3aQF9rQOkgA">Typescript playground</a>.</p><p>And there it is!</p><p>You&#x2019;ve used a strongly typed function as a parameter.</p><h2 id="handling-functions-with-no-return-value">Handling functions with no return value</h2><p>The accepted answer in the referenced stack overflow post for example says <em>the callback parameter&apos;s type must be &quot;function that accepts a number and returns type any&quot;</em></p><p>That&#x2019;s partly true, but the return type does NOT have to be <code>any</code></p><p>In fact, do NOT use <code>any</code>.</p><p>If your function returns a value, go ahead and type it appropriately:</p><pre><code class="language-ts">// Callback returns an object
type Callback = (value: string) =&gt; { result: string }
</code></pre><p>If your callback returns nothing, use <code>void</code> not <code>any</code>:</p><pre><code class="language-ts">// Callback returns nothing
type Callback = (value: string) =&gt; void
</code></pre><p>Note that the signature of your function type should be:</p><pre><code class="language-ts">(arg1: Arg1type, arg2: Arg2type) =&gt; ReturnType
</code></pre><p>Where <code>Arg1type</code> represents the type of the argument <code>arg1</code>, <code>Arg2type</code> the type of the <code>arg2</code> argument, and <code>ReturnType</code> the return type of your function.</p><h2 id="conclusion-3">Conclusion</h2><p>Functions are the primary means of passing data around in Javascript.</p><p>Typescript not only allows you to specify the input and output of functions, but you can also type functions as arguments to other functions.</p><p>Go ahead and use them with confidence.</p><h1 id="6-how-to-fix-could-not-find-declaration-file-for-module-%E2%80%A6">6: How to Fix Could Not Find Declaration File for Module &#x2026;?</h1><p>This is a common source of frustration for Typescript beginners.</p><p>However, do you know how to fix this?</p><p>Yes, you do!</p><p>The solution to this was equally explained in the <em>what is <code>d.ts</code> section.</em></p><h2 id="tldr-5">TLDR</h2><p>Create a declaration file, e.g., <code>untyped-module.d.ts</code> with the following content: <code>declare module &quot;some-untyped-module&quot;;</code>. Note that this will explicitly type the module as <code>any</code>.</p><h2 id="the-solution-explained">The solution explained</h2><p>You can give the writing your declaration files section a fresh read if you don&#x2019;t remember how to fix this.</p><p>Essentially, you have this error because the library in question didn&#x2019;t bundle its types and does not have a type definition file on <a href="https://github.com/DefinitelyTyped/DefinitelyTyped/">DefinitelyTyped</a>.</p><p>This leaves you with one solution: write your own declaration file.</p><p>For example, If you have installed the library <code>untyped-module</code> in your project,</p><p><code>untyped-module</code> has no referenced type definition files, so Typescript complains.</p><p>To silence this warning, create a new <code>untyped-module.d.ts</code> file in my project with the following content:</p><pre><code class="language-ts">declare module &quot;some-untyped-module&quot;;
</code></pre><p>This will declare the module as type <code>any</code>.</p><p>You won&#x2019;t get any Typescript support for that module, but you&#x2019;d have silenced the Typescript warning.</p><p>Ideal next steps would include opening an issue in the module&#x2019;s public repository to include a Typescript declaration file or writing out a decent one yourself (beyond the scope of this article).</p><h1 id="7-how-do-i-dynamically-assign-properties-to-an-object-in-typescript">7: How Do I Dynamically Assign Properties to an Object in Typescript?</h1><figure class="kg-card kg-image-card kg-card-hascaption"><img src="__uploaded__images__/DraggedImage.tiff" class="kg-image" alt="The Seven Most Asked Typescript Questions on StackOverflow &#x2014; Explained" loading="lazy"><figcaption>Dynamically assigning properties to objects in Typescript</figcaption></figure><h2 id="tldr-6">TLDR</h2><p>If you cannot define the variable type at declaration time, use the <code>Record</code> utility type or an object index signature.</p><h2 id="introduction-3">Introduction</h2><p>Consider the following example:</p><pre><code class="language-ts">const organization = {}

organization.name = &quot;Freecodecamp&quot;
                                                                                                                 
</code></pre><p>This seemingly harmless piece of code throws a Typescript error on dynamically assigning <code>name</code> to the <code>organization</code> object.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-07-at-11.08.32@2x.png" class="kg-image" alt="The Seven Most Asked Typescript Questions on StackOverflow &#x2014; Explained" loading="lazy"><figcaption>Typescript error when adding a new property dynamically</figcaption></figure><p>See <a href="https://www.typescriptlang.org/play?#code/MYewdgzgLgBCBOBzAhmAlgL2VN4YF4YBvAXwCgyEV0sdwA6MZAWwFMCYAiAMXlddAATASwAOnCjCnSZsufIWKlylarXqZFIA">Typescript playground</a></p><p>The source of confusion, and perhaps rightly justified if you&#x2019;re a Typescript beginner, is, how is something seemingly so simple a problem in Typescript?</p><h2 id="understanding-the-problem-1">Understanding the problem</h2><p>Generally speaking, Typescript determines the type of a variable when it is declared, and this determined type doesn&#x2019;t change, i.e., it stays the same all through your application.</p><p>There are exceptions to this rule when considering type narrowing or working with the any type, but this is a general rule to remember otherwise.</p><p>In the earlier example, the <code>organization</code> object is declared as follows:</p><pre><code class="language-ts">const organization = {}
</code></pre><p>There is no explicit type assigned to the <code>organization</code> variable, so Typescript infers the type of <code>organization</code> based on the declaration to be <code>{}</code> i.e., the literal empty object.</p><p>For example, if you add a type alias, you can explore the type of <code>organization</code>:</p><pre><code class="language-ts">type Org = typeof organization
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-07-at-11.15.30@2x.png" class="kg-image" alt="The Seven Most Asked Typescript Questions on StackOverflow &#x2014; Explained" loading="lazy"><figcaption>Exploring the literal object type</figcaption></figure><p>See <a href="https://www.typescriptlang.org/play?#code/MYewdgzgLgBCBOBzAhmAlgL2VN4YF4YBvAXwCgyoBPABwFMYB5JAma+kAMziVU21xgKCFOiw5wAOjDIAtg0IAiAGLw6dUABMNcmoooxDR4ydNnzFy1es3bd4xSA">Typescript playground</a>.</p><p>When you then try to reference the <code>name</code> prop on this empty object literal:</p><pre><code class="language-ts">organization.name = ...
</code></pre><p>Typescript yells.</p><blockquote>Property &apos;name&apos; does not exist on type &#x2018; <code>{}</code>&#x2018;</blockquote><p>When you understand the issue, the error does seem appropriate.</p><p>Let&#x2019;s fix this.</p><h2 id="resolving-the-error">Resolving the error</h2><p>There are numerous ways you can resolve the Typescript error here. Let&#x2019;s consider these:</p><h3 id="1-explicitly-type-the-object-at-declaration-time">1. Explicitly type the object at declaration time</h3><p>This is the easiest solution to reason about.</p><p>At the time you declare the object, go ahead and type it. Furthermore, assign it all the relevant values.</p><pre><code class="language-ts">type Org = {
    name: string
}

const organization: Org = {
    name: &quot;Freecodecamp&quot;
}
</code></pre><p>See <a href="https://www.typescriptlang.org/play?#code/C4TwDgpgBA8gTgcygXigbwFBW1AdgQwFsIAuKAZ2DgEtcEMBfDDAYwHtdKo3F9dqAXvmDUOZeElSYceIqSgAiAGJwIEdgBN1RMAsbMDQA">Typescript playground</a>.</p><p>This removes every surprise.</p><p>You&#x2019;re clearly stating what this object type is and rightly declaring all relevant properties when you create the object.</p><p>However, this is not always feasible if the object properties must be added dynamically.</p><h3 id="2-use-an-object-index-signature">2. Use an object index signature</h3><p>Occasionally, the properties of the object truly need to be added at a later time than when declared.</p><p>In this case, you can leverage the object index signature as follows:</p><pre><code class="language-ts">type Org = {[key: string] : string}

const organization: Org = {}

organization.name = &quot;Freecodecamp&quot;
</code></pre><p>See <a href="https://www.typescriptlang.org/play?#code/C4TwDgpgBA8gTgcygXigbwNoGsIgFxQDOwcAlgHYIC6UBxZlAvgFDMDGA9ucVB4gIblSAL37BSXAvCSo0LZnwSCRYieQB05fgFtoqAEQAxOBAicAJmZ1h9rO0A">Typescript playground</a></p><p>At the time the <code>organization</code> variable is declared, you go ahead and explicitly type it to the following <code>{[key: string] : string}</code></p><p>To explain the syntax further, you might be used to object types having fixed property types:</p><pre><code class="language-ts">type obj = {
  name: string
}
</code></pre><p>However, you can also substitute <code>name</code> for a &#x201C;variable type&#x201D;.</p><p>For example, if you want to define any string property on <code>obj</code>:</p><pre><code class="language-ts">type obj = {
 [key: string]: string
}
</code></pre><p>Note that the syntax is similar to how you&#x2019;d use a variable object property in standard Javascript:</p><pre><code class="language-ts">const variable = &quot;name&quot; 

const obj = {
   [variable]: &quot;Freecodecamp&quot;
}
</code></pre><p>The Typescript equivalent is called an object index signature.</p><p>Moreover, note that you could type <code>key</code> with other primitives:</p><pre><code class="language-ts">// number 
type Org = {[key: number] : string}

// string 
type Org = {[key: string] : string}

//boolean
type Org = {[key: boolean] : string}
</code></pre><h3 id="3-use-the-record-utility-type">3. Use the Record utility type</h3><p>The solution here is quite concise:</p><pre><code class="language-ts">type Org = Record&lt;string, string&gt;

const organization: Org = {}


organization.name = &quot;Freecodecamp&quot;
</code></pre><p>Instead of using a type alias, you can also inline the type:</p><pre><code class="language-ts">const organization: Record&lt;string, string&gt; = {}
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-07-at-16.20.22.png" class="kg-image" alt="The Seven Most Asked Typescript Questions on StackOverflow &#x2014; Explained" loading="lazy"><figcaption>Using the Record utility type</figcaption></figure><p>See <a href="https://www.typescriptlang.org/play?#code/C4TwDgpgBA8gTgcygXigJQgYwPZwCYA8AzsHAJYB2CANFCeVQHwBQzOFJUuCAhhWQC8ewMtgoAuWIhRQA3gF9Wzbn0HDRFAHQUeAW2ioARADE4ELNjxY9YQ0tZA">Typescript playground</a>.</p><p>The <code>Record</code> utility type has the following signature: <code>Record&lt;Keys, Type&gt;</code>.</p><p>It allows you to constrict an object type whose properties are <code>Keys</code> and property values are <code>Type</code>,</p><p>In our example, <code>Keys</code> represents <code>string</code> and <code>Type</code>, <code>string</code> as well.</p><h2 id="conclusion-4">Conclusion</h2><p>Apart from primitives, the most common types you&#x2019;ll have to deal with are likely object types.</p><p>In cases where you need to build an object dynamically, take advantage of the Record utility type or use the object index signature to define the allowed properties on the object.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/7-ts-2.png" class="kg-image" alt="The Seven Most Asked Typescript Questions on StackOverflow &#x2014; Explained" loading="lazy" width="1200" height="630" srcset="https://blog.ohansemmanuel.com/content/images/size/w600/2022/07/7-ts-2.png 600w, https://blog.ohansemmanuel.com/content/images/size/w1000/2022/07/7-ts-2.png 1000w, https://blog.ohansemmanuel.com/content/images/2022/07/7-ts-2.png 1200w" sizes="(min-width: 720px) 720px"><figcaption>Download the cheatsheet&#xA0;</figcaption></figure><p>Note that you can get a <a href="https://www.ohansemmanuel.com/cheatsheet/top-7-stack-overflowed-typescript-questions">PDF or ePub</a>, version of this cheatsheet for easier reference, or for reading on your Kindle or tablet.</p><h2 id="fancy-a-free-typescript-book">Fancy a Free Typescript Book?</h2><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/image-4.png" class="kg-image" alt="The Seven Most Asked Typescript Questions on StackOverflow &#x2014; Explained" loading="lazy" width="700" height="367" srcset="https://blog.ohansemmanuel.com/content/images/size/w600/2022/07/image-4.png 600w, https://blog.ohansemmanuel.com/content/images/2022/07/image-4.png 700w"><figcaption>Build strongly typed Polymorphic React components book</figcaption></figure><p><a href="https://www.ohansemmanuel.com/books/how-to-build-strongly-typed-polymorphic-react-components">Get this book for free</a></p>]]></content:encoded></item><item><title><![CDATA[Are strongly typed functions as parameters possible in TypeScript?]]></title><description><![CDATA[<h2 id="tldr">TLDR</h2><p>This question does not need to be overly explained. The short answer is yes.</p><p>Functions can be strongly typed &#x2014; even as parameters to other functions.</p><h2></h2><h2 id="how-to-use-strongly-typed-function-parameters">How to use strongly typed function parameters</h2><p>The accepted answer on this <a href="https://stackoverflow.com/questions/14638990/are-strongly-typed-functions-as-parameters-possible-in-typescript">stack overflow post</a> is correct &#x2014; to a degree.</p><p>Assuming you</p>]]></description><link>https://blog.ohansemmanuel.com/are-strongly-typed-functions-as-parameters-possible-in-typescript/</link><guid isPermaLink="false">62c72cfb43e529f14fb262a4</guid><dc:creator><![CDATA[Ohans Emmanuel]]></dc:creator><pubDate>Thu, 07 Jul 2022 18:59:36 GMT</pubDate><media:content url="https://blog.ohansemmanuel.com/content/images/2022/07/blog-5.png" medium="image"/><content:encoded><![CDATA[<h2 id="tldr">TLDR</h2><img src="https://blog.ohansemmanuel.com/content/images/2022/07/blog-5.png" alt="Are strongly typed functions as parameters possible in TypeScript?"><p>This question does not need to be overly explained. The short answer is yes.</p><p>Functions can be strongly typed &#x2014; even as parameters to other functions.</p><h2></h2><h2 id="how-to-use-strongly-typed-function-parameters">How to use strongly typed function parameters</h2><p>The accepted answer on this <a href="https://stackoverflow.com/questions/14638990/are-strongly-typed-functions-as-parameters-possible-in-typescript">stack overflow post</a> is correct &#x2014; to a degree.</p><p>Assuming you had a function: <code>speak</code>:</p><pre><code class="language-ts">function speak(callback) {
  const sentence = &quot;Hello world&quot;
  alert(callback(sentence))
}
</code></pre><p>It receives a <code>callback</code> that&#x2019;s internally invoked with a <code>string</code>.</p><p>To type this, go ahead and represent the <code>callback</code> with a function type alias:</p><pre><code class="language-ts">type Callback = (value: string) =&gt; void
</code></pre><p>And type the <code>speak</code> function as follows:</p><pre><code class="language-ts">function speak(callback: Callback) {
  const sentence = &quot;Hello world&quot;
  alert(callback(sentence))
}
</code></pre><p>Alternatively, you could also keep the type inline:</p><pre><code class="language-ts">function speak(callback: (value: string) =&gt; void) {
  const sentence = &quot;Hello world&quot;

  alert(callback(sentence))
}
</code></pre><p>See <a href="https://www.typescriptlang.org/play?#code/GYVwdgxgLglg9mABAZwA4FMCGBrAFBTAG0ICNMJsAuRXANyJHWuSgCcYwBzASkQF4AfIlpwYAE14BvAFCJEEBCxTowUFRHT9EAIgAS6YnEQB3OK0Jjt02YiLpWUfEVLk8yFWsjpu3aQF9rQOkgA">Typescript playground</a>.</p><p>And there it is!</p><p>You&#x2019;ve used a strongly typed function as a parameter.</p><p></p><h2 id="functions-that-return-any">Functions that return any</h2><p>The accepted answer in the referenced stack overflow post for example says <em>the callback parameter&apos;s type must be &quot;function that accepts a number and returns type any&quot;</em></p><p>That&#x2019;s partly true, but the return type does NOT have to be <code>any</code></p><p>In fact, do NOT use <code>any</code>.</p><p>If your function returns a value, go ahead and type it appropriately:</p><pre><code class="language-ts">// Callback returns an object
type Callback = (value: string) =&gt; { result: string }
</code></pre><p>If your callback returns nothing, use <code>void</code> not <code>any</code>:</p><pre><code class="language-ts">// Callback returns nothing
type Callback = (value: string) =&gt; void
</code></pre><p>Note that the signature of your function type should be:</p><pre><code class="language-ts">(arg1: Arg1type, arg2: Arg2type) =&gt; ReturnType
</code></pre><p>Where <code>Arg1type</code> represents the type of the argument <code>arg1</code>, <code>Arg2type</code> the type of the <code>arg2</code> argument, and <code>ReturnType</code> the return type of your function.</p><p></p><h2 id="conclusion">Conclusion</h2><p>Functions are the primary means of passing data around in Javascript.</p><p>Typescript not only allows you to specify the input and output of functions, but you can also type functions as arguments to other functions.</p><p>Go ahead and use them with confidence.</p><p></p><h2 id="get-a-free-typescript-book">Get a Free Typescript Book? </h2><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2022/06/image-148.png" class="kg-image" alt="Are strongly typed functions as parameters possible in TypeScript?" loading="lazy"><figcaption><a href="https://www.ohansemmanuel.com/books/how-to-build-strongly-typed-polymorphic-react-components">Build strongly typed Polymorphic React components&#xA0;</a></figcaption></figure><p><a href="https://www.ohansemmanuel.com/books/how-to-build-strongly-typed-polymorphic-react-components">Get this book for free</a> </p>]]></content:encoded></item><item><title><![CDATA[How do you explicitly set a new property on ‘window’ in Typescript?]]></title><description><![CDATA[TLDR: Extend the existing interface declaration for the Window object.]]></description><link>https://blog.ohansemmanuel.com/how-do-you-explicitly-set-a-new-property-on-window-in-typescript/</link><guid isPermaLink="false">62c70f6643e529f14fb2628b</guid><category><![CDATA[typescript]]></category><dc:creator><![CDATA[Ohans Emmanuel]]></dc:creator><pubDate>Thu, 07 Jul 2022 16:53:47 GMT</pubDate><media:content url="https://blog.ohansemmanuel.com/content/images/2022/07/blog-4--1-.png" medium="image"/><content:encoded><![CDATA[<h2 id="tldr">TLDR</h2><img src="https://blog.ohansemmanuel.com/content/images/2022/07/blog-4--1-.png" alt="How do you explicitly set a new property on &#x2018;window&#x2019; in Typescript?"><p>Extend the existing interface declaration for the <code>Window</code> object.</p><h2 id="introduction">Introduction</h2><p>Knowledge builds upon knowledge.</p><p>Whoever said that was right.</p><p>In this section, we will build upon the knowledge from the last two sections:</p><p><a href="https://blog.ohansemmanuel.com/interfaces-vs-types-in-typescript/">Interfaces vs Types in Typescript</a></p><p><a href="https://blog.ohansemmanuel.com/what-is-a-dts-file-in-typescript/">What is a d.t.s file in Typescript</a></p><p>Ready?</p><p>First, I must say, in my early days with Typescript, this was a question I googled over and over again.</p><p>I never got it. And I didn&#x2019;t bother, I just googled.</p><p>That&#x2019;s never the right mentality to gaining mastery over a subject.</p><p>Let&#x2019;s discuss the solutions to this.</p><p></p><h2 id="understanding-the-problem">Understanding the problem</h2><p>The problem here is actually straightforward to reason about.</p><p>Consider the following Typescript code:</p><pre><code class="language-ts">window.__MY_APPLICATION_NAME__ = &quot;freecodecamp&quot;

console.log(window.__MY_APPLICATION_NAME__)
</code></pre><p>Typescript is quick to let you know <code>__MY_APPLICATION_NAME__</code> does not exist on type <code>&#x2018;Window &amp; typeof globalThis&#x2019;</code>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-05-at-07.05.11.png" class="kg-image" alt="How do you explicitly set a new property on &#x2018;window&#x2019; in Typescript?" loading="lazy"><figcaption>The property does not exist on Window error</figcaption></figure><p>See <a href="%20https://www.typescriptlang.org/play?#code/O4SwdgJg9sB0D68CyBNeBBACpgMgSQGF0AVPAeQDl4L0kBRRAAgF5GAiAMwCcBTHgYygQBAQwC2ABzYAoaYLABnKABsesZVADmAClCQYCZGiy5CJclRr1EASln2gA">TypeScript playground</a>.</p><p>Okay, Typescript.</p><p>We get it.</p><p>On closer look, remember from the last section on <a href="https://blog.ohansemmanuel.com/what-is-a-dts-file-in-typescript/">declaration files</a> that there&#x2019;s a declaration file for all existing browser APIs. This includes built-in objects such as <code>window</code>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-05-at-07.06.53@2x.png" class="kg-image" alt="How do you explicitly set a new property on &#x2018;window&#x2019; in Typescript?" loading="lazy"><figcaption>The default Window interface declaration</figcaption></figure><p>If you look in the <code>lib.dom.d.ts</code> declaration file, you&#x2019;ll find the <code>Window</code> interface described.</p><p>In layman&#x2019;s terms, the error here says the <code>Window</code> interface describes how I understand the <code>window</code> object and its usage. That interface does not specify a certain <code>__MY_APPLICATION_NAME__</code> property.</p><p></p><h2 id="fixing-the-error">Fixing the error</h2><p>In the Types vs interface section, I explained how to extend an interface.</p><p>Let&#x2019;s apply that knowledge here.</p><p>We can extend the <code>Window</code> interface declaration to become aware of the <code>__MY_APPLICATION_NAME__</code> property.</p><p>Here&#x2019;s how:</p><pre><code class="language-ts">// before
window.__MY_APPLICATION_NAME__ = &quot;freecodecamp&quot;

console.log(window.__MY_APPLICATION_NAME__)

// now 
interface Window {
  __MY_APPLICATION_NAME__: string
}

window.__MY_APPLICATION_NAME__ = &quot;freecodecamp&quot;

console.log(window.__MY_APPLICATION_NAME__)
</code></pre><p>Errors banished!</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-05-at-07.13.01.png" class="kg-image" alt="How do you explicitly set a new property on &#x2018;window&#x2019; in Typescript?" loading="lazy"><figcaption>The resolved solution</figcaption></figure><p>See <a href="https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgOqgCYHsDuyDeAUMsgPqkCyAmqQIIAK9AMgJIDCtAKiwPIBypPrQoBRcgC5kAZzBRQAc0IBfQoRyZcAOnLU6jVh279BwsaWQBeZACIYUCBARYMjuAFsADtdVOQUrAA2EJoBWPIAFOog2DjalDQMzOxcvAJCouQAlEA">TypeScript playground</a>.</p><p>Remember that a key difference between types and interfaces is that interfaces can be extended by declaring it multiple times.</p><p>What we&#x2019;ve done here is declared the <code>Window</code> interface one more time, hence extending the interface declaration.</p><p></p><h3 id="a-real-world-solution">A real-world solution</h3><p>I&#x2019;ve solved this problem within the Typescript playground to show you the solution in its simplest form, i.e., the crux.</p><p>In the real world, though, you wouldn&#x2019;t extend the interface within your code.</p><p>So, what should you do instead?</p><p>Give it a guess, perhaps?</p><p>Yes, you were close &#x2026; or perhaps right!</p><p>Create a type definition file!</p><p>For example, create a <code>window.d.ts</code> file with the following content:</p><pre><code class="language-ts">interface Window {
  __MY_APPLICATION_NAME__: string
}
</code></pre><p>And there you go.</p><p>You&#x2019;ve successfully extended the <code>Window</code> interface and solved the problem.</p><p>If you went ahead to assign the wrong value type to the <code>__MY_APPLICATION_NAME__</code> property, you now have strong type checking enabled.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-05-at-07.21.57.png" class="kg-image" alt="How do you explicitly set a new property on &#x2018;window&#x2019; in Typescript?" loading="lazy"><figcaption>A wrong assignment to the newly defined property caught</figcaption></figure><p>See <a href="https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgOqgCYHsDuyDeAUMsgPqkCyAmqQIIAK9AMgJIDCtAKiwPIBypPrQoBRcgC5kAZzBRQAc0IBfQoRyZcAOnLU6jVh279BwsaWQBeAsWQg4AWwiSARDCgQICLBk8OADs7Kql4gUlgANhCa4VjyABTqINg42pQ0DMzsXLwCQqLkAJSqxUA">Typescript playground</a>.</p><p><em>Voil&#xE0;.</em></p><p></p><h2 id="conclusion">Conclusion</h2><p>In <a href="https://stackoverflow.com/questions/12709074/how-do-you-explicitly-set-a-new-property-on-window-in-typescript">older stack overflow posts</a>, you&#x2019;ll find more complicated answers based on older Typescript versions.</p><p>The solution is easier to reason about in modern Typescript.</p><p>Now you know &#x1F609;</p><p></p><h2 id="get-a-free-typescript-book">Get a Free Typescript Book? </h2><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2022/06/image-148.png" class="kg-image" alt="How do you explicitly set a new property on &#x2018;window&#x2019; in Typescript?" loading="lazy"><figcaption><a href="https://www.ohansemmanuel.com/books/how-to-build-strongly-typed-polymorphic-react-components">Build strongly typed Polymorphic React components&#xA0;</a></figcaption></figure><p><a href="https://www.ohansemmanuel.com/books/how-to-build-strongly-typed-polymorphic-react-components">Get this book for free</a> </p>]]></content:encoded></item><item><title><![CDATA[What is a “.d.ts” file in TypeScript?]]></title><description><![CDATA[.d.ts files are called type declaration files. They exist for one purpose only: to describe the shape of an existing module and they only contain type information used for type checking.]]></description><link>https://blog.ohansemmanuel.com/what-is-a-dts-file-in-typescript/</link><guid isPermaLink="false">62c709c543e529f14fb26282</guid><category><![CDATA[typescript]]></category><dc:creator><![CDATA[Ohans Emmanuel]]></dc:creator><pubDate>Thu, 07 Jul 2022 16:30:18 GMT</pubDate><media:content url="https://blog.ohansemmanuel.com/content/images/2022/07/blog-3.png" medium="image"/><content:encoded><![CDATA[<h2 id="tldr">TLDR</h2><img src="https://blog.ohansemmanuel.com/content/images/2022/07/blog-3.png" alt="What is a &#x201C;.d.ts&#x201D; file in TypeScript?"><p><code>.d.ts</code> files are called type declaration files. They exist for one purpose only: to describe the shape of an existing module and they only contain type information used for type checking.</p><h2 id="introduction">Introduction</h2><p>Upon learning the basics of TypeScript, you unlock superpowers.</p><p>At least that&#x2019;s how I felt.</p><p>You automagically get warnings on potential errors, you get auto-completion out of the box in your code editor.</p><p>While seemingly magical, nothing with computers are.</p><p>So, what&#x2019;s the trick here, TypeScript?</p><p>In a clearer language, how does TypeScript know so much? How does it decide what API is correct or not? What methods are available on a certain object or class, and which aren&#x2019;t?</p><p>The answer is less magical.</p><p>TypeScript relies on types.</p><p>Occasionally, you do not write these types, but they exist.</p><p>They exist in files called declaration files.</p><p>These are files with a <code>.d.ts</code> ending.</p><h2 id="a-simple-example">A simple example</h2><p>Consider the following TypeScript code:</p><pre><code class="language-ts">// valid 
const amount = Math.ceil(14.99)

// error: Property &apos;ciil&apos; does not exist on type &apos;Math&apos;.(2339)
const otherAmount = Math.ciil(14.99)
</code></pre><p>See <a href="https://www.TypeScriptlang.org/play?#code/MYewdgzgLgBAhgWxAVzLAvDAsnKALAOmAFMBLAGwAoBGAFgIE4GBKAKFdElhH2ICcAgklQZsuQsFIUa9Jm1ZA">TypeScript playground</a>.</p><p>The first line of code is perfectly valid, but the second, not quite.</p><p>And TypeScript is quick to spot the error: <code>Property &apos;ciil&apos; does not exist on type &apos;Math&apos;.(2339)</code></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-05-at-06.01.49.png" class="kg-image" alt="What is a &#x201C;.d.ts&#x201D; file in TypeScript?" loading="lazy"><figcaption>The Typescript error spotting the wrong property access &quot;ciil&quot;</figcaption></figure><p>How did TypeScript know <code>ciil</code> does not exist on the <code>Math</code> object?</p><p>The <code>Math</code> object isn&#x2019;t a part of our implementation. It&#x2019;s a standard built-in object.</p><p>So, how did TypeScript figure that out?</p><p>The answer is there are <strong>declaration files</strong> that describe these built-in objects.</p><p>Think of a declaration file as containing all type information relating to a certain module. It contains no actual implementation, just type information.</p><p>These files have a <code>.d.ts</code> ending.</p><p>Your implementation files will either have <code>.ts</code> or <code>.js</code> endings to represent TypeScript or javascript files.</p><p>These declaration files have no implementations. They only contain type information and have a <code>.d.ts</code> file ending.</p><h2 id="built-in-type-definitions">Built-in Type Definitions</h2><p>A great way to understand this in practice is to set up a brand-new TypeScript project and explore the type definition files for top-level objects like <code>Math</code>.</p><p>Let&#x2019;s do this.</p><p>Create a new directory, and name it whatever&#x2019;s appropriate.</p><p>I&#x2019;ll call mine <code>dts</code>.</p><p>Change directories to this newly created folder:</p><pre><code class="language-ts">cd dts
</code></pre><p>Now initialise a new project:</p><pre><code class="language-ts">npm init --yes
</code></pre><p>Install TypeScript:</p><pre><code class="language-ts">npm install TypeScript --save-dev
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-05-at-06.17.57@2x.png" class="kg-image" alt="What is a &#x201C;.d.ts&#x201D; file in TypeScript?" loading="lazy"><figcaption>Installing TypeScript</figcaption></figure><p>This directory should contain 2 files and one subdirectory</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-05-at-06.21.06@2x.png" class="kg-image" alt="What is a &#x201C;.d.ts&#x201D; file in TypeScript?" loading="lazy"><figcaption>The files after installation</figcaption></figure><p>Open the folder in your favourite code editor.</p><p>If you investigate the <code>TypeScript</code> directory within <code>node_modules</code>, you&#x2019;ll find a bunch of type declaration files out of the box.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-05-at-06.22.37@2x.png" class="kg-image" alt="What is a &#x201C;.d.ts&#x201D; file in TypeScript?" loading="lazy"><figcaption>Type declaration files in the typescript directory</figcaption></figure><p>These are present courtesy of installing TypeScript.</p><p>By default, TypeScript will include type definition for all DOM APIs, e.g., think <code>window</code> and <code>document</code>.</p><p>As you inspect these type declaration files, you&#x2019;ll notice that the naming convention is straightforward.</p><p>It follows the pattern: <code>lib.[something].d.ts</code>.</p><p>Open up the <code>lib.dom.d.ts</code> declaration file to view all declarations related to the browser DOM API.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-05-at-06.26.40@2x.png" class="kg-image" alt="What is a &#x201C;.d.ts&#x201D; file in TypeScript?" loading="lazy"><figcaption>The dom declaration file</figcaption></figure><p>As you can see, this is quite a gigantic file.</p><p>But so are all the APIs available in the DOM.</p><p>Awesome!</p><p>Now, if you take a look at the <code>lib.es5.d.ts</code> file, you&#x2019;ll see the declaration for the <code>Math</code> object, containing the <code>ceil</code> property.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-07-at-18.20.04@2x.png" class="kg-image" alt="What is a &#x201C;.d.ts&#x201D; file in TypeScript?" loading="lazy"><figcaption>The Math object in the declaration file</figcaption></figure><p>Next time you think, wow, TypeScript is wonderful. Remember, a big part of that awesomeness is due to the lesser-known heroes: type declaration files.</p><p>It&#x2019;s not magic. Just type declaration files.</p><h2 id="external-type-definitions">External type definitions</h2><p>What about APIs that aren&#x2019;t built-in?</p><p>There&#x2019;s a host of <code>npm</code> packages out there to do just about anything you want.</p><p>Is there a way for TypeScript to also understand the relevant type relationships for the said module?</p><p>Well, the answer is a resounding yes.</p><p>There are typically two ways a library author may do this.</p><h3 id="1-bundled-types">(1) Bundled Types</h3><p>In this case, the author of the library has already bundled the type declaration files as part of the package distribution.</p><p>You typically don&#x2019;t need to do anything.</p><p>You just go ahead and install the library in your project, you import the required module from the library and see if TypeScript should automatically resolve the types for you.</p><p>Remember, this is not magic.</p><p>The library author has bundled the type declaration file in the package distribution.</p><h3 id="2-definitelytyped-types">(2) DefinitelyTyped (@types)</h3><p>Imagine a central public repository that hosts declaration files for thousands of libraries?</p><p>Well, bring that image home.</p><p>This repository already exists.</p><p>The <a href="https://github.com/DefinitelyTyped/DefinitelyTyped/">DefinitelyTyped repository</a> is a centralised repository that stores the declaration files for thousands of libraries.</p><p>In all honestly, the vast majority of commonly used libraries have declaration files available on <strong>DefinitelyTyped</strong>.</p><p>These type definition files are automatically published to <code>npm</code> under the <code>@types</code> scope.</p><p>For example, if you wanted to install the types for the <code>react</code> npm package, you&#x2019;d do this:</p><pre><code class="language-ts">npm install --save-dev @types/react
</code></pre><p>If you find yourself using a module whose types TypeScript does not automatically resolve, attempt installing the types directly from DefinitelyTyped.</p><p>See if the types exist there. e.g.:</p><pre><code class="language-ts">npm install --save-dev @types/your-library
</code></pre><p>Definition files added in this manner will be saved to <code>node_modules/@types</code>.</p><p>TypeScript will automatically find these. So, there&#x2019;s no additional step for you to take.</p><h2 id="writing-your-declaration-files">Writing your declaration files</h2><p>In the uncommon event that a library didn&#x2019;t bundle its types and does not have a type definition file on DefinitelyTyped, you can write your own declaration files.</p><p>Writing declaration files in-depth is beyond the scope of this article, but a use case you&#x2019;ll likely come across is silencing errors about a particular module without a declaration file.</p><p>Declaration files all have a <code>.d.ts</code> ending.</p><p>So to create yours, create a file with a <code>.d.ts</code> ending.</p><p>For example, assuming I have installed library <code>untyped-module</code> in my project.</p><p><code>untyped-module</code> has no referenced type definition files, so TypeScript complains about this in my project.</p><p>To silence this warning, I may create a new <code>untyped-module.d.ts</code> file in my project with the following content:</p><pre><code class="language-ts">declare module &quot;some-untyped-module&quot;;
</code></pre><p>This will declare the module as type <code>any</code>.</p><p>We won&#x2019;t get any TypeScript support for that module, but you&#x2019;d have silenced the TypeScript warning.</p><p>Ideal next steps would include opening an issue in the module&#x2019;s public repository to include a TypeScript declaration file, or writing out a decent one yourself.</p><h2 id="conclusion">Conclusion</h2><p>Next time you think, wow, TypeScript is remarkable. Remember, a big part of that awesomeness is due to the lesser-known heroes: type declaration files.</p><p>Now you understand how they work!</p><p></p><h2 id="get-a-free-typescript-book">Get a Free Typescript Book? </h2><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2022/06/image-148.png" class="kg-image" alt="What is a &#x201C;.d.ts&#x201D; file in TypeScript?" loading="lazy"><figcaption><a href="https://www.ohansemmanuel.com/books/how-to-build-strongly-typed-polymorphic-react-components">Build strongly typed Polymorphic React components&#xA0;</a></figcaption></figure><p><a href="https://www.ohansemmanuel.com/books/how-to-build-strongly-typed-polymorphic-react-components">Get this book for free</a> </p>]]></content:encoded></item><item><title><![CDATA[In Typescript, what is the ! (exclamation mark / bang) operator?]]></title><description><![CDATA[This is technically called the non-null assertion operator. If the typescript compiler complains that a value could be null or undefined, you can use the ! operator to assert that the said value is NOT null or undefined.]]></description><link>https://blog.ohansemmanuel.com/in-typescript-what-is-the-exclamation-mark-or-bang-operator/</link><guid isPermaLink="false">62c6fea343e529f14fb26276</guid><category><![CDATA[typescript]]></category><dc:creator><![CDATA[Ohans Emmanuel]]></dc:creator><pubDate>Thu, 07 Jul 2022 15:44:06 GMT</pubDate><media:content url="https://blog.ohansemmanuel.com/content/images/2022/07/blog-2.png" medium="image"/><content:encoded><![CDATA[<h2 id="tldr">TLDR</h2><img src="https://blog.ohansemmanuel.com/content/images/2022/07/blog-2.png" alt="In Typescript, what is the ! (exclamation mark / bang) operator?"><p>This is technically called the <strong>non-null assertion operator</strong>. If the typescript compiler complains about a value being <code>null</code> or <code>undefined</code>, you can use the <code>!</code> operator to assert that the said value is not <code>null</code> or <code>undefined</code>.</p><p>Personal take: avoid doing this wherever possible.</p><h2 id="what-is-the-non-null-assertion-operator">What is the non-null assertion operator?</h2><p><code>null</code> and <code>undefined</code> are valid Javascript values.</p><p>The statement above holds true for all Typescript applications as well.</p><p>However, Typescript goes one step further.</p><p><code>null</code> and <code>undefined</code> are equally valid types, e.g., consider the following:</p><pre><code class="language-ts">// explicit null
let a: null 

a = null
// the following assignments will yield errors
a= undefined 
a = {}


// explicit undefined
let b: undefined 
// the following assignments will yield errors
b = null 
b = {}
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-04-at-06.21.57@2x.png" class="kg-image" alt="In Typescript, what is the ! (exclamation mark / bang) operator?" loading="lazy"><figcaption>Error: unassignable values other than null and undefined</figcaption></figure><p>See <a href="https://www.typescriptlang.org/play?#code/DYUwLgBAhgXBB2BXYwICg1QgXgc4aA9IRGABYgQBmA9ijQO4CW8A5tAM4dOvwC2IeGA4RmKCAE8mIYABMIIAE6KaijplyJ4skFRYh5mHBADeAXwxpQkAEZwtOvfAPpipCtTrBGLdlC48-ILCokziUjLySipqaDbGSOJxxuZoQA">Typescript playground</a>.</p><p>In certain cases, the Typescript compiler cannot tell whether a certain value is defined or not, i.e., not <code>null</code> or <code>undefined</code>.</p><p>For example, assuming you had a value <code>Foo</code>.</p><p><code>Foo!</code> produces a value of the type of <code>Foo</code> with <code>null</code> and <code>undefined</code> excluded.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-07-at-16.47.49@2x.png" class="kg-image" alt="In Typescript, what is the ! (exclamation mark / bang) operator?" loading="lazy"><figcaption>Foo! excludes null and undefined from the type of Foo</figcaption></figure><p>You essentially say to the Typescript compiler, <em>I am sure, <code>Foo</code> will NOT be <code>null</code> or <code>undefined</code></em></p><p>Let&#x2019;s explore a naive example.</p><p>In standard Javascript, you may concatenate two strings with the <code>.concat</code> method:</p><pre><code class="language-ts">const str1 = &quot;Hello&quot; 
const str2 = &quot;World&quot;

const greeting = str1.concat(&apos; &apos;, str2)
// Hello World
</code></pre><p>Write a simple duplicate string function that calls <code>.concat</code> with itself as an argument:</p><pre><code class="language-ts">function duplicate(text: string | null) {
  return text.concat(text);
}
</code></pre><p>Note that the argument <code>text</code> is typed as <code>string | null</code></p><p>In strict mode, Typescript will complain here, as calling <code>concat</code> with <code>null</code> can lead to unexpected results.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-04-at-06.42.57@2x.png" class="kg-image" alt="In Typescript, what is the ! (exclamation mark / bang) operator?" loading="lazy"><figcaption>The result of calling concat with null</figcaption></figure><p>The typescript error will read: <code>Object is possibly &apos;null&apos;.(2531)</code></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-04-at-06.44.36@2x.png" class="kg-image" alt="In Typescript, what is the ! (exclamation mark / bang) operator?" loading="lazy"><figcaption>Typescript error: Object is possibly null</figcaption></figure><p>On the flip side, a rather lazy way to silence the compiler error is to use the non-null assertion operator:</p><pre><code class="language-ts">function duplicate(text: string | null) {
  return text!.concat(text!);
}
</code></pre><p>Note the exclamation mark after the <code>text</code> variable, i.e, <code>text!</code></p><p>The <code>text</code> type represents <code>string | null</code>.</p><p><code>text!</code> represents just <code>string</code> i.e., with <code>null</code> or <code>undefined</code> removed from the variable type.</p><p>The result? You&#x2019;ve silenced the Typescript error.</p><p>However, this is a silly fix.</p><p><code>duplicate</code> can indeed be called with <code>null</code>, which may lead to unexpected results.</p><p>Note that the following example also holds true if <code>text</code> is an optional property:</p><pre><code class="language-ts">// text could be &quot;undefined&quot;
function duplicate(text?: string) {
  return text!.concat(text!);
}
</code></pre><h2 id="pitfalls-and-what-to-do">Pitfalls (and what to do)</h2><p>When working with Typescript as a new user, you may feel like you&#x2019;re fighting a lost battle.</p><p>The errors don&#x2019;t make sense to you.</p><p>Your goal is to remove the error and move on with your life as swiftly as you can.</p><p>However, you should be careful with using the non-null assertion operator.</p><p>Silencing a Typescript error doesn&#x2019;t mean there may not still be an underlying issue&#x2014;if unaddressed.</p><p>As you saw in the earlier example, you lose every relevant Typescript safety against wrong usages where <code>null</code> and <code>undefined</code> could be unwanted.</p><p>So, what should you do?</p><p>If you write React, consider an example you&#x2019;re likely familiar with:</p><pre><code class="language-ts">const MyComponent = () =&gt; {
   const ref = React.createRef&lt;HTMLInputElement&gt;();
	
   //compilation error: ref.current is possibly null
   const goToInput = () =&gt; ref.current.scrollIntoView(); 

    return (
       &lt;div&gt;
           &lt;input ref={ref}/&gt;
           &lt;button onClick={goToInput}&gt;Go to Input&lt;/button&gt;
       &lt;/div&gt;
   );
};
</code></pre><p>In the example above (for those who do not write React), in the <code>React</code> mental model, <code>ref.current</code> will certainly be available at the time the button is clicked by the user.</p><p>The <code>ref</code> object is set soon after the UI elements are rendered.</p><p>Typescript does not know this, and you may be forced to use the non-null assertion operator here.</p><p>Essentially, say to the Typescript compiler, I know what I&#x2019;m doing, you don&#x2019;t.</p><pre><code class="language-ts">const goToInput = () =&gt; ref.current!.scrollIntoView();
</code></pre><p>Note the exclamation mark <code>!</code>.</p><p>This &#x201C;fixes&#x201D; the error.</p><p>However, if in the future, someone removes the <code>ref</code> from the input, and there were no automated tests to catch this, you now have a bug.</p><pre><code class="language-ts">// before
&lt;input ref={ref}/&gt;

// after
&lt;input /&gt;
</code></pre><p>Typescript will be unable to spot the error in the following line:</p><pre><code class="language-ts">const goToInput = () =&gt; ref.current!.scrollIntoView();
</code></pre><p>By using the non-null assertion operator, the Typescript compiler will act as if <code>null</code> and <code>undefined</code> are never possible for the value in question. In this case, <code>ref.current</code>.</p><h3 id="solution-1-find-an-alternative-fix">Solution 1: find an alternative fix</h3><p>The first line of action you should employ is to find an alternative fix.</p><p>For example, often you can explicitly check for <code>null</code> and <code>undefined</code> values.</p><pre><code class="language-ts">// before 
const goToInput = () =&gt; ref.current!.scrollIntoView();

// now 
const goToInput = () =&gt; {
  if (ref.current) {
   //Typescript will understand that ref.current is certianly 
   //avaialble in this branch
     ref.current.scrollIntoView()
  }
};

// alternatively (use the logical AND operator)
const goToInput = () =&gt; ref.current &amp;&amp; ref.current.scrollIntoView();
</code></pre><p>Numerous engineers will argue over the fact that this is more verbose.</p><p>That&#x2019;s correct.</p><p>However, choose verbose over possibly breaking code being pushed to production.</p><p>This is a personal preference. Your mileage may differ.</p><h3 id="solution-2-explicitly-throw-an-error">Solution 2: explicitly throw an error</h3><p>In cases where an alternative fix doesn&#x2019;t cut it and non-null assertion operator seems like the only solution, I typically advise you throw an error before doing this.</p><p>Here&#x2019;s an example (in pseudocode):</p><pre><code class="language-ts">function doSomething (value) {
   // for some reason TS thinks value could be  
   // null or undefined but you disagree
   
  if(!value) {
    // explicilty assert this is the case 
    // throw an error or log this somewhere you can trace
    throw new Error(&apos;uexpected error: value not present&apos;)
  } 

  // go ahead and use the non-null assertion operator
  console.log(value)
}
</code></pre><p>A practical case where I&#x2019;ve found myself sometimes doing this is while using <code>Formik</code>.</p><p>Except things have changed, I do think <code>Formik</code> is poorly typed in numerous instances.</p><p>The example may go similar to you&apos;ve done your Formik validation and are sure that your values exist.</p><p>Here&#x2019;s some pseudocode:</p><pre><code class="language-ts">&lt;Formik 
  validationSchema={...} 
  onSubmit={(values) =&gt; {
   // you are sure values.name should exist because you had 
   // validated in validationSchema but Typescript doesn&apos;t know this

   if(!values.name) {
    throw new Error(&apos;Invalid form, name is required&apos;)		
   } 
   console.log(values.name!)
}}&gt;


&lt;/Formik&gt;
</code></pre><p>In the pseudocode above, <code>values</code> could be typed as:</p><pre><code class="language-ts">type Values = {
  name?: string
}
</code></pre><p>But before you hit <code>onSubmit</code>, you&#x2019;ve added some validation to show a UI form error for the user to input a <code>name</code> before moving on to the form submission.</p><p>There are other ways to get around this, but if you find yourself in such a case where you&#x2019;re sure a value exists but can&#x2019;t quite communicate that to the Typescript compiler, use the non-null assertion operator. But also add an assertion of your own by throwing an error you can trace.</p><h2 id="how-about-an-implicit-assertion">How about an implicit assertion?</h2><p>Even though the name of the operator reads non-null assertion operator, no &#x201C;assertion&#x201D; is actually being made.</p><p>You&#x2019;re mostly asserting (as the developer), that the value exists.</p><p>The Typescript compiler does NOT assert that this value exists.</p><p>So, if you must, you may go ahead and add your assertion, e.g., as discussed in the earlier section.</p><p>Also, note that no more Javascript code is emitted by using the non-null assertion operator.</p><p>As stated earlier, there&#x2019;s no assertion done here by Typescript.</p><p>Consequently, Typescript will not emit some code that checks if this value exists or not.</p><p>The Javascript code emitted will act as if this value always existed.</p><figure class="kg-card kg-image-card"><img src="https://blog.ohansemmanuel.com/content/images/2022/07/CleanShot-2022-07-04-at-07.14.51@2x.png" class="kg-image" alt="In Typescript, what is the ! (exclamation mark / bang) operator?" loading="lazy"></figure><h2 id="conclusion">Conclusion</h2><p>TypeScript 2.0 saw the release of the <strong>non-null assertion operator</strong>. Yes, it&#x2019;s been around for some time (<a href="https://github.com/microsoft/TypeScript/releases/tag/v2.0.3">released in 2016</a>). At the time of writing, the latest version of Typescript is <code>v4.7</code>.</p><p>If the typescript compiler complains about a value being <code>null</code> or <code>undefined</code>, you can use the <code>!</code> operator to assert that the said value is not null or undefined.</p><p>Only do this if you&#x2019;re certain that is the case.</p><p>Even better, go ahead and add an assertion of your own, or try to find an alternative solution.</p><p>Some may argue that if you need to use the non-null assertion operator every time, it&#x2019;s a sign you&#x2019;re poorly representing the state of your application state via Typescript.</p><p>I agree with this school of thought.</p><p></p><h2 id="get-a-free-typescript-book">Get a Free Typescript Book? </h2><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2022/06/image-148.png" class="kg-image" alt="In Typescript, what is the ! (exclamation mark / bang) operator?" loading="lazy"><figcaption><a href="https://www.ohansemmanuel.com/books/how-to-build-strongly-typed-polymorphic-react-components">Build strongly typed Polymorphic React components&#xA0;</a></figcaption></figure><p><a href="https://www.ohansemmanuel.com/books/how-to-build-strongly-typed-polymorphic-react-components">Get this book for free</a> </p>]]></content:encoded></item><item><title><![CDATA[Interfaces vs Types in Typescript]]></title><description><![CDATA[When beginning Typescript, you may find it confusing to settle on a choice. This article clears up the confusion and helps you choose which is right for you.]]></description><link>https://blog.ohansemmanuel.com/interfaces-vs-types-in-typescript/</link><guid isPermaLink="false">62b9dd4843e529f14fb261fe</guid><category><![CDATA[typescript]]></category><dc:creator><![CDATA[Ohans Emmanuel]]></dc:creator><pubDate>Tue, 28 Jun 2022 06:32:44 GMT</pubDate><media:content url="https://blog.ohansemmanuel.com/content/images/2022/06/blog-1-1-1.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.ohansemmanuel.com/content/images/2022/06/blog-1-1-1.png" alt="Interfaces vs Types in Typescript"><p>The interfaces vs types (technically, type alias) conversation is a well contested one.</p><p>When beginning Typescript, you may find it confusing to settle on a choice. This article clears up the confusion and helps you choose which is right for you.</p><h2 id="tldr">TLDR</h2><p>In many cases, you can use either an interface or type alias exchangeably.</p><p>Almost all features of an interface are available via type aliases except you cannot add new properties to a type by re-declaring it. You must use an intersection type.</p><hr><h2 id="why-the-confusion-in-the-first-place">Why the Confusion in the first place</h2><p>Whenever we&#x2019;re faced with multiple options, most people begin to suffer from the <a href="https://en.wikipedia.org/wiki/The_Paradox_of_Choice">paradox of choice</a>.</p><p>In this case, there are just two options.</p><p>What&#x2019;s so confusing about this?</p><p>Well, the main confusion here stems from the fact that these two options are so <strong>evenly matched</strong> in most regards.</p><p>This makes it difficult to make an obvious choice &#x2014; especially if you&#x2019;re just starting out with Typescript.</p><h2 id="a-basic-example">A Basic Example</h2><p>Let&#x2019;s get on the same page with quick examples of an interface and a type alias.</p><p>Consider the representations of a <code>Human</code> type below:</p><pre><code class="language-ts">// type 
type Human = {
  name: string 
  legs: number 
  head: number
}

// interface 
interface Human {
  name: string 
  legs: number 
  head: number
}
</code></pre><p>These are both correct ways to denote the <code>Human</code> type i.e., via a type alias or an interface.</p><h2 id="the-differences-between-type-aliases-and-interfaces">The differences between Type aliases and Interfaces</h2><p>Below are the main differences between a type alias and an interface:</p><h3 id="key-difference-interfaces-can-only-describe-object-shapes-type-aliases-can-be-used-for-other-types-such-as-primitives-unions-and-tuples">Key difference: interfaces can only describe object shapes. Type aliases can be used for other types such as primitives, unions and tuples</h3><p>A type alias is quite flexible in the data types you can represent. From basic primitives to complex unions and tuples as shown below:</p><pre><code class="language-ts">// primitives 
type Name = string 

// object 
type Male = {
  name: string
}

type Female = {
  name: string 
}

// union
type HumanSex = Male | Female

// tuple
type Children = [Female, Male, Female]
</code></pre><p>Unlike, type aliases, you may only represent object types with an interface.</p><h3 id="key-difference-interfaces-can-be-extended-by-declaring-it-multiple-times">Key difference: interfaces can be extended by declaring it multiple times</h3><p>Consider the following example:</p><pre><code class="language-ts">interface Human {
  name: string 
}

interface Human {
  legs: number 
}
</code></pre><p>The two declarations above will become:</p><pre><code class="language-ts">interface Human {
  name: string 
  legs: number 
}
</code></pre><p><code>Human</code> will be treated as a single interface: a combination of the members of both declarations.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/CleanShot-2022-06-27-at-07.23.42.png" class="kg-image" alt="Interfaces vs Types in Typescript" loading="lazy"><figcaption>Property &apos;legs&apos; is required in type &apos;Humans&apos;</figcaption></figure><p>See <a href="https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgBIFcC2cTIN4BQyxyIcmEAXMgM5hSgDmBAvgaJLIihtroSWQAbCIxrUQWAEbRWBAgHoFyMAAtgNZNCgB7KJp3owyGQjjoaKAOQixV5JgvGIADw3GCCHSDrJV1XhxkAF58IhIyCmorAHlVHE0AUUw+dAghK1YgA">Typescript playground</a>.</p><p>This is not the case with type aliases.</p><p>With a type alias, the following will lead to an error:</p><pre><code class="language-ts">type Human = {
    name: string 
}
  
type Human =  {
    legs: number 
}

const h: Human = {
   name: &apos;gg&apos;,
   legs: 5 
}  
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/CleanShot-2022-06-27-at-07.24.27.png" class="kg-image" alt="Interfaces vs Types in Typescript" loading="lazy"><figcaption>Duplicate identifier &apos;Human&apos; error</figcaption></figure><p>See <a href="https://www.typescriptlang.org/play?#code/C4TwDgpgBAEgrgWwIYDsoF4oG8BQV9QpIIQBcUAzsAE4CWKA5lDgL57OiSyKob64EoAGwgMK5FIgBGEaszY4AxgHsUVKAAty8ZGkwD8REuQDkDBiYA07YaPFQArPPw5XroA">Typescript playground</a>.</p><p>With Type aliases, you&#x2019;ll have to resort to an intersection type:</p><pre><code class="language-ts">type HumanWithName = {
    name: string 
}
  
type HumanWithLegs =  {
    legs: number 
}

type Human  = HumanWithName &amp; HumanWithLegs

const h: Human = {
   name: &apos;gg&apos;,
   legs: 5 
}  
</code></pre><p>See <a href="https://www.typescriptlang.org/play?#code/C4TwDgpgBAEgrgWwIYDsDqBLYALAckhaAXigG8AoKKqFAiALigGdgAnDFAcynIF9KeoSLESpMOADIROTKCTICqAG2lNGKRACMIrHv3JDo8ZCioljYrHjpQAZCJPjsUmeXIBjAPYoWUbIwtTEgpqWkJGAHJOTgiAGkUVGUYAVj0qNwygA">Typescript playground</a></p><h3 id="minor-difference-both-can-be-extended-but-with-different-syntaxes">Minor difference: Both can be extended but with different syntaxes</h3><p>With interfaces, you use the <code>extends</code> keyword. For types, you must use an intersection.</p><p>Consider the following examples:</p><h4 id="type-alias-extends-type-alias">Type alias extends type alias</h4><pre><code class="language-ts">
type HumanWithName = {
  name: string 
}

type Human = HumanWithName &amp; {
   legs: number 
   eyes: number 
}
</code></pre><h4 id="type-alias-extends-an-interface">Type alias extends an interface</h4><pre><code class="language-ts">interface HumanWithName {
  name: string 
}

type Human = HumanWithName &amp; {
   legs: number 
   eyes: number 
} 
</code></pre><h4 id="interface-extends-interface">Interface extends interface</h4><pre><code class="language-ts">interface HumanWithName {
  name: string 
}

interface Human extends HumanWithName {
  legs: number 
  eyes: number 
}
</code></pre><h4 id="an-interface-extends-a-type-alias">An interface extends a type alias</h4><pre><code class="language-ts">type HumanWithName = {
  name: string
}

interface Human extends HumanWithName {
  legs: number 
  eyes: number 
}
</code></pre><p>As you can see, this is not particularly a reason to choose one over the other. However, the syntaxes differ.</p><h3 id="minor-difference-classes-can-only-implement-statically-known-members">Minor difference: classes can only implement statically known members</h3><p>A class can implement both interfaces or type aliases. However, a class cannot implement or extend a union type.</p><p>Consider the following example:</p><h4 id="class-implements-interface">Class implements interface</h4><pre><code class="language-ts">interface Human {
  name: string
  legs: number 
  eyes: number 
}

class FourLeggedHuman implements Human {
  name = &apos;Krizuga&apos;
  legs = 4
  eyes = 2
}
</code></pre><h4 id="class-implements-type-alias">Class implements type alias</h4><pre><code class="language-ts">type Human = {
  name: string
  legs: number 
  eyes: number 
}

class FourLeggedHuman implements Human {
  name = &apos;Krizuga&apos;
  legs = 4
  eyes = 2
}
</code></pre><p>These both work without any errors. However, the following fails:</p><h4 id="class-implements-union-type">Class implements union type</h4><pre><code class="language-ts">type Human = {
    name: string
} | {
    legs: number
    eyes: number
}

class FourLeggedHuman implements Human {
    name = &apos;Krizuga&apos;
    legs = 4
    eyes = 2
}
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/CleanShot-2022-06-27-at-07.27.07.png" class="kg-image" alt="Interfaces vs Types in Typescript" loading="lazy"><figcaption>A class can only implement an object type or intersection of object types with statically known members.</figcaption></figure><p>See <a href="https://www.typescriptlang.org/play?#code/C4TwDgpgBAEgrgWwIYDsoF4oG8BQV9QpIIQBcUAzsAE4CWKA5jgL5QA+2eBANhAxeRSIARhGpd8EEBAGERYljhwBjbkgoUoAMQD2cagBk+DCABN4yNLQRheJFME0XUnAoWLRMAcgDSdAF5wDEheElC8-BhQACxhUjJRAEwsQA">Typescript playground</a>.</p><h2 id="summary-personal-preference">Summary &amp; Personal preference</h2><p>Your mileage may differ but wherever possible, I stick to type aliases for their flexibility and simpler syntax i.e., I choose type aliases except I specifically need features from an interface.</p><p>For the most part, you can also choose based on your personal preference, but stay consistent with your choice &#x2014; at least in a single given project.</p><p>For completeness, I must add that. In performance-critical types, interface comparison checks can be faster than type aliases. I&#x2019;m yet to find this to be an issue.</p><p></p><h2 id="get-a-free-typescript-book">Get a Free Typescript Book? </h2><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2022/06/image-148.png" class="kg-image" alt="Interfaces vs Types in Typescript" loading="lazy"><figcaption><a href="https://www.ohansemmanuel.com/books/how-to-build-strongly-typed-polymorphic-react-components">Build strongly typed Polymorphic React components&#xA0;</a></figcaption></figure><p><a href="https://www.ohansemmanuel.com/books/how-to-build-strongly-typed-polymorphic-react-components">Get this book for free</a> </p>]]></content:encoded></item><item><title><![CDATA[What is Typescript?]]></title><description><![CDATA[<p>I published an <a href="https://www.ohansemmanuel.com/books/how-to-build-strongly-typed-polymorphic-react-components">Intermediate Typescript and React Handbook</a> a few weeks ago.</p><p>It received numerous views and I got several emails. Most were &#x201C;thank you&#x201D; emails, but then there were others like:</p><blockquote>&#x201C;&#x2026; I am new to programming, what is Typescript?&#x201D;<br>&#x201C;Thanks for this free</blockquote>]]></description><link>https://blog.ohansemmanuel.com/what-is-typescript/</link><guid isPermaLink="false">62aef7c8d8ecb906a8af3759</guid><dc:creator><![CDATA[Ohans Emmanuel]]></dc:creator><pubDate>Wed, 22 Jun 2022 04:31:48 GMT</pubDate><media:content url="https://blog.ohansemmanuel.com/content/images/2022/06/blog.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.ohansemmanuel.com/content/images/2022/06/blog.png" alt="What is Typescript?"><p>I published an <a href="https://www.ohansemmanuel.com/books/how-to-build-strongly-typed-polymorphic-react-components">Intermediate Typescript and React Handbook</a> a few weeks ago.</p><p>It received numerous views and I got several emails. Most were &#x201C;thank you&#x201D; emails, but then there were others like:</p><blockquote>&#x201C;&#x2026; I am new to programming, what is Typescript?&#x201D;<br>&#x201C;Thanks for this free ebook, but how do I learn Typescript as a beginner?&#x201D;</blockquote><p>I had clearly stated that the handbook was for intermediate developers who already knew some Typescript&#x2014;but when did that ever stop anyone from downloading a free resource! :) </p><p>In this guide, I&#x2019;ve decided to answer the queries in those emails with the article I wish I had when I learned Typescript.</p><p>Now, if you&#x2019;re still reading, I&#x2019;ll assume you&#x2019;re a Typescript beginner.</p><p>Strap up. You&#x2019;re in for a fun ride.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/image.png" class="kg-image" alt="What is Typescript?" loading="lazy" width="800" height="600" srcset="https://blog.ohansemmanuel.com/content/images/size/w600/2022/06/image.png 600w, https://blog.ohansemmanuel.com/content/images/2022/06/image.png 800w" sizes="(min-width: 720px) 720px"><figcaption>GIF by Jona Dinges (https://dribbble.com/jonadinges)</figcaption></figure><p></p><h2 id="explaining-typescript-to-a-5-year-old">Explaining Typescript to a 5-year-Old</h2><p>My approach to teaching has always remained the same.</p><p>If you can&#x2019;t explain it to a 5-year-old, you may not know the subject well enough.</p><p>Instead of swarming you with a lot of technical jargon, let&#x2019;s try something different.</p><p>Let&#x2019;s use an analogy you&#x2019;ll never forget.</p><p>When was the last time you visited the grocery store?</p><p>Consider TypeMart:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/image-1.png" class="kg-image" alt="What is Typescript?" loading="lazy" width="1048" height="788" srcset="https://blog.ohansemmanuel.com/content/images/size/w600/2022/06/image-1.png 600w, https://blog.ohansemmanuel.com/content/images/size/w1000/2022/06/image-1.png 1000w, https://blog.ohansemmanuel.com/content/images/2022/06/image-1.png 1048w" sizes="(min-width: 720px) 720px"><figcaption>The TypeMart grocery store</figcaption></figure><p>TypeMart is your typical <strong>big</strong> grocery store.</p><p>Do you want a variety of grocery items picked up after work? They&#x2019;ve got you covered.</p><p>On the other hand, here&#x2019;s JMart:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/image-2.png" class="kg-image" alt="What is Typescript?" loading="lazy" width="1040" height="734" srcset="https://blog.ohansemmanuel.com/content/images/size/w600/2022/06/image-2.png 600w, https://blog.ohansemmanuel.com/content/images/size/w1000/2022/06/image-2.png 1000w, https://blog.ohansemmanuel.com/content/images/2022/06/image-2.png 1040w" sizes="(min-width: 720px) 720px"><figcaption>The JMart grocery store</figcaption></figure><p>JMart is a smaller grocery store for quick purchases.</p><p>In Berlin, where I live, we call these <a href="https://allaboutberlin.com/glossary/Sp%C3%A4ti#:~:text=A%20Sp%C3%A4ti%20or%20Sp%C3%A4tkauf%20(pronounced,and%20bodegas%20in%20other%20countries.">Sp&#xE4;tis</a>. These are essentially small convenience shops.</p><p>I&#x2019;m sure you&#x2019;re not here for a German lesson.</p><p>What&#x2019;s important to us here is how the grocery stores, JMart and TypeMart work.</p><h3 id="how-jmart-and-typemart-work">How JMart and TypeMart work</h3><p>With <em>JMart</em>, you go into the shop, find the grocery item you need and take it to the cashier.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/image-3.png" class="kg-image" alt="What is Typescript?" loading="lazy" width="1076" height="792" srcset="https://blog.ohansemmanuel.com/content/images/size/w600/2022/06/image-3.png 600w, https://blog.ohansemmanuel.com/content/images/size/w1000/2022/06/image-3.png 1000w, https://blog.ohansemmanuel.com/content/images/2022/06/image-3.png 1076w" sizes="(min-width: 720px) 720px"><figcaption>Going over to the Cashier to get your bill</figcaption></figure><p>At this point, you&#x2019;re not quite sure how much the grocery item you&#x2019;ve picked costs.</p><p>Well, that&#x2019;s why you go to the cashier!</p><p>The cashier takes your item, scans it and tells you how much it cost.</p><p>If they&#x2019;re &#x201C;better&#x201D; at their job, they&#x2019;ll tell you how much the item cost off the top of their head (or some manual catalogue they keep in the drawer)</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/image-4.png" class="kg-image" alt="What is Typescript?" loading="lazy" width="1060" height="738" srcset="https://blog.ohansemmanuel.com/content/images/size/w600/2022/06/image-4.png 600w, https://blog.ohansemmanuel.com/content/images/size/w1000/2022/06/image-4.png 1000w, https://blog.ohansemmanuel.com/content/images/2022/06/image-4.png 1060w" sizes="(min-width: 720px) 720px"><figcaption>Receiving the bill from the Cashier</figcaption></figure><p>The process seems brittle, but boy does it work!</p><p>These cashiers are smart as hell. No items are off-limit.</p><p>They know what every item costs.</p><p>One beautiful Tuesday, you decide to try out <em>TypeMart</em>.</p><p>You soon realise that things are different in TypeMart.</p><p><em>&#x2019;Those pesky big stores,&#x2019;</em> you may say.</p><p>Unlike JMart, they&#x2019;ve got a price tag for every damn thing.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/image-5.png" class="kg-image" alt="What is Typescript?" loading="lazy" width="1422" height="608" srcset="https://blog.ohansemmanuel.com/content/images/size/w600/2022/06/image-5.png 600w, https://blog.ohansemmanuel.com/content/images/size/w1000/2022/06/image-5.png 1000w, https://blog.ohansemmanuel.com/content/images/2022/06/image-5.png 1422w" sizes="(min-width: 720px) 720px"><figcaption>Basket of fruits with price tags</figcaption></figure><p>They rob you of the thrill and the look on the cashier&#x2019;s face as they compute your bill.</p><p>On the other hand, what they give you is some sort of assurance.</p><p>There are no surprises!</p><p>You know exactly how much every item you&#x2019;ve picked up costs.</p><p>That is beneficial on days when your wallet is slim.</p><p>Every pence matter.</p><h3 id="why-does-this-analogy-matter">Why does this analogy matter?</h3><p>Your intuition was correct.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/image-6.png" class="kg-image" alt="What is Typescript?" loading="lazy" width="1914" height="1308" srcset="https://blog.ohansemmanuel.com/content/images/size/w600/2022/06/image-6.png 600w, https://blog.ohansemmanuel.com/content/images/size/w1000/2022/06/image-6.png 1000w, https://blog.ohansemmanuel.com/content/images/size/w1600/2022/06/image-6.png 1600w, https://blog.ohansemmanuel.com/content/images/2022/06/image-6.png 1914w" sizes="(min-width: 720px) 720px"><figcaption>JMart represents Javscript. Typemart, Typescript.</figcaption></figure><p>In the analogy, JMart represents Javascript and TypeMart, Typescript.</p><p>When you go to a supermarket, there&#x2019;s an unwritten contract.</p><p>They promise to have what you need at a fair price.</p><p>You promise to pay for what you buy (except you&#x2019;re shoplifting. Don&#x2019;t do this.)</p><p>The same is true for code.</p><p>It&#x2019;s an unwritten contract, but a clear and brutal one.</p><p>Your contract is with the user of your application.</p><p>You promise that your application works.</p><p>Consider an example with a conference call application like Google meet.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/image-7.png" class="kg-image" alt="What is Typescript?" loading="lazy" width="1200" height="807" srcset="https://blog.ohansemmanuel.com/content/images/size/w600/2022/06/image-7.png 600w, https://blog.ohansemmanuel.com/content/images/size/w1000/2022/06/image-7.png 1000w, https://blog.ohansemmanuel.com/content/images/2022/06/image-7.png 1200w" sizes="(min-width: 720px) 720px"><figcaption>The Google meet web interface. Source: https://shrtm.nu/L0yk</figcaption></figure><p>The promise with Google meet is you&#x2019;ll always be able to make video calls. They also promise you can mute the button while you chat with your partner or watch a quick TikTok.</p><p>Good thing they can&#x2019;t hear you!</p><p>Or, so you think?</p><p>Imagine if the mute button didn&#x2019;t do what it promised.</p><p>There goes your secret.</p><p>And with it goes your trust in Google meet.</p><p>The same is true for the applications you write.</p><p>You promise a working application, and your users trust that&#x2019;s the case &#x2014; assuming you&#x2019;ve earned their trust.</p><p>Let&#x2019;s now bring this home.</p><p>In JMart and TypeMart, the goods are money. With software, the goods are data.</p><p>Assume you had a basic counter application.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/image-8.png" class="kg-image" alt="What is Typescript?" loading="lazy" width="2000" height="1326" srcset="https://blog.ohansemmanuel.com/content/images/size/w600/2022/06/image-8.png 600w, https://blog.ohansemmanuel.com/content/images/size/w1000/2022/06/image-8.png 1000w, https://blog.ohansemmanuel.com/content/images/size/w1600/2022/06/image-8.png 1600w, https://blog.ohansemmanuel.com/content/images/2022/06/image-8.png 2066w" sizes="(min-width: 720px) 720px"><figcaption>A basic counter application user interface</figcaption></figure><p>Your user sees a fancy UI, but under the hood what&#x2019;s really making magic is the counter <em>variable</em> you increase or decrease.</p><p>With JMart (analogous to Javascript), the goods are not labelled (price tagged). You don&#x2019;t know how much anything costs. You go to the cashier to meet your fate.</p><p>This is similar to how Javascript works.</p><p>You define and manipulate all sorts of variables, but there&#x2019;s no explicit label for what the variables are.</p><p>You trust what you&#x2019;ve written and pass it on to the Javascript compiler to meet your fate.</p><p>Consider the following trivial Javascript code:</p><pre><code class="language-js">const JMart = {
   bananas: true,
   apples: true, 
   mangos: true
}
</code></pre><p>In a standard Javascript application, you may go-ahead to write the following:</p><pre><code class="language-js">const myOrder = JMart.cars.price
</code></pre><p>Even though <code>cars</code> does not exist on the <code>JMArt</code> object, there&#x2019;s no explicit label that defines that.</p><p>So, as you write your code, you may not know this line of code is faulty &#x2026;</p><p>Until you go to the cashier to meet your fate.</p><p>The cashier here is the Javascript interpreter.</p><p>Typically, this happens when you run the code in a browser.</p><p>If you do, you then get an error that reads <code>can&apos;t read price of undefined</code></p><p>If you shipped this code (mistakenly) to production, your uses will be met with this ugly error as well.</p><p>You&#x2019;ve just compromised their trust in your application.</p><p>With Typescript, things are different.</p><p>Every data is &#x201C;labelled&#x201D; just like in TypeMart.</p><p>Before you go to the cashier aka the browser to run the code, you can tell if your application is working as it should!</p><p>The Typescript compiler will throw an error letting you know you&#x2019;ve made a mistake accessing a wrong value.</p><p>This happens within your code editor before you open the application in a browser.</p><p>Like picking up a grocery item you can&#x2019;t afford at TypeMart, you see the price label.</p><p>You know what&#x2019;s in your wallet. It&#x2019;s fair to say you&#x2019;ve been warned.</p><p>This right here is the major initial definition of Typescript you should know.</p><blockquote>TypeScript is JavaScript with syntax for types.</blockquote><p>Where types are labels dangling around your grocery item (data), telling you exactly what each data represents.</p><p>Consider the following trivial Javascript example:</p><pre><code class="language-js">const myFunction = (a, b) =&gt; {
   return a * b
}</code></pre><p>In Typescript, this code could look like this:</p><pre><code class="language-js">const myFunction = (a: string, b: string) =&gt; {
   return a * b
}</code></pre><p>Note how this looks identical to the Javascript code.</p><p>However, it&#x2019;s got a major difference: the data <code>a</code> and <code>b</code> are <code>&apos;labelled&apos;</code>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/image-9.png" class="kg-image" alt="What is Typescript?" loading="lazy" width="1994" height="954" srcset="https://blog.ohansemmanuel.com/content/images/size/w600/2022/06/image-9.png 600w, https://blog.ohansemmanuel.com/content/images/size/w1000/2022/06/image-9.png 1000w, https://blog.ohansemmanuel.com/content/images/size/w1600/2022/06/image-9.png 1600w, https://blog.ohansemmanuel.com/content/images/2022/06/image-9.png 1994w" sizes="(min-width: 720px) 720px"><figcaption>The type function parameter type annotations</figcaption></figure><p>This code specifically states that <code>a</code> and <code>b</code> expected in <code>myFunction</code> are strings.</p><p>With this information (called type annotation), Typescript can now show you errors as you write your code.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/image-10.png" class="kg-image" alt="What is Typescript?" loading="lazy" width="1282" height="428" srcset="https://blog.ohansemmanuel.com/content/images/size/w600/2022/06/image-10.png 600w, https://blog.ohansemmanuel.com/content/images/size/w1000/2022/06/image-10.png 1000w, https://blog.ohansemmanuel.com/content/images/2022/06/image-10.png 1282w" sizes="(min-width: 720px) 720px"><figcaption>View this code in the Typescript playground: https://shrtm.nu/FlC0</figcaption></figure><p>These errors will usually be seen in the form of red squiggly lines. Similar to errors in applications like Microsoft Word.</p><p>You may then hover over these lines to view the details of the error.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/image-11.png" class="kg-image" alt="What is Typescript?" loading="lazy" width="1900" height="560" srcset="https://blog.ohansemmanuel.com/content/images/size/w600/2022/06/image-11.png 600w, https://blog.ohansemmanuel.com/content/images/size/w1000/2022/06/image-11.png 1000w, https://blog.ohansemmanuel.com/content/images/size/w1600/2022/06/image-11.png 1600w, https://blog.ohansemmanuel.com/content/images/2022/06/image-11.png 1900w" sizes="(min-width: 720px) 720px"><figcaption>The details of the Typescript error</figcaption></figure><p>In this simple example, the crux of the error is that the multiplication operation should not be run on strings.</p><h3 id="non-exception-errors">Non-exception errors</h3><p>If you&#x2019;re a more experienced Javascript developer, you can already notice that the code example earlier examined doesn&#x2019;t throw an error in standard Javascript.</p><pre><code class="language-js">const myFunction = (a, b) =&gt; {
   return a * b
}</code></pre><p>If you computed <code>&#x201C;1&#x201D; * &quot;6&quot;</code> in Javascript, you&#x2019;ll get <code>6</code>.</p><p>Internally, Javascript coerces the strings to numbers and performs the multiplication operation.</p><p>These sorts of errors that don&#x2019;t fail in Javascript, but error out in Typescript, are called non-exception errors.</p><p>These are supposed to help you prevent nasty bugs in your application.</p><p>You shouldn&#x2019;t necessarily worry about this at this stage of your learning Typescript journey, but it&#x2019;s worth mentioning.</p><p>As you can see, Typescript goes far and beyond to help you catch unwanted behaviours in your code.</p><p>A simple way to fix this would be to type the parameters explicitly, i.e., <code>a</code> and <code>b</code> as numbers:</p><pre><code class="language-ts">const myFunction = (a: number, b: number) =&gt; {
   return a * b
}</code></pre><p>And away goes the error!</p><p>Don&#x2019;t despite Typescript for bringing these non-exception errors to your attention.</p><p>They are potential sources of bugs in your application.</p><p>Typescript to the rescue &#x1F4AA;&#x1F3FD;</p><p></p><h2 id="conclusion">Conclusion</h2><p>Ask yourself, do I now know what Typescript is?</p><p>Yes, you do. Conceptually.</p><p>Typescript is to Javascript what TypeMart is to JMart.</p><p>An organised way to <em>label</em> the data within your application to prevent unknown errors.</p><p>These errors will be caught and brought to your attention before you go to the cashier, i.e., before you run your application.</p><p>Take a moment to digest this information. It&#x2019;ll be crucial as you learn more about Typescript.</p><p>Give yourself a pat on the back, and go write your first Typescript application.</p><p></p><h3 id="further-resources">Further Resources</h3><ul><li><a href="https://www.ohansemmanuel.com/books/how-to-build-strongly-typed-polymorphic-react-components">Intermediate TypeScript and React Handbook</a>: Learn intermediate Typescript with React by building a strongly typed Polymorphic component.</li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/image-12.png" class="kg-image" alt="What is Typescript?" loading="lazy" width="1200" height="630" srcset="https://blog.ohansemmanuel.com/content/images/size/w600/2022/06/image-12.png 600w, https://blog.ohansemmanuel.com/content/images/size/w1000/2022/06/image-12.png 1000w, https://blog.ohansemmanuel.com/content/images/2022/06/image-12.png 1200w" sizes="(min-width: 720px) 720px"><figcaption>https://www.ohansemmanuel.com/books/how-to-build-strongly-typed-polymorphic-react-components</figcaption></figure><p></p><ul><li>Fancy a quick Typescript exercise? Spot and fix the error in the earlier described example. Use the official online editor called the Typescript playground here: [<a href="https://shrtm.nu/FlC0">https://shrtm.nu/FlC0</a>]</li></ul>]]></content:encoded></item><item><title><![CDATA[Build Strongly-Typed Polymorphic Components with React and Typescript]]></title><description><![CDATA[<p>Hey! &#x1F60E;</p><p>In this detailed (and explanatory) guide, I&#x2019;ll discuss how to build strongly typed <strong>Polymorphic</strong> React components with Typescript.</p><p>If you have no idea what that means, that&#x2019;s fine. That&#x2019;s a decent pointer that this is just the right guide for you.</p><p>Ready?</p>]]></description><link>https://blog.ohansemmanuel.com/build-strongly-typed-polymorphic-components-with-react-and-typescript/</link><guid isPermaLink="false">6296e8d908cc9405be551342</guid><category><![CDATA[typescript]]></category><dc:creator><![CDATA[Ohans Emmanuel]]></dc:creator><pubDate>Wed, 01 Jun 2022 04:24:36 GMT</pubDate><media:content url="https://blog.ohansemmanuel.com/content/images/2022/06/Frame-1.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.ohansemmanuel.com/content/images/2022/06/Frame-1.png" alt="Build Strongly-Typed Polymorphic Components with React and Typescript"><p>Hey! &#x1F60E;</p><p>In this detailed (and explanatory) guide, I&#x2019;ll discuss how to build strongly typed <strong>Polymorphic</strong> React components with Typescript.</p><p>If you have no idea what that means, that&#x2019;s fine. That&#x2019;s a decent pointer that this is just the right guide for you.</p><p>Ready?</p><h2 id="introduction">Introduction</h2><p>Let&#x2019;s get some housekeeping out of the way.</p><h3 id="audience">Audience</h3><p>I wrote this article for Typescript beginners / intermediate developers. If you&#x2019;re a more advanced engineer, you may find reading the <a href="https://github.com/ohansemmanuel/polymorphic-react-component">source code</a> a faster way to get the same information (without my ramblings)</p><h3 id="free-pdf-download">Free PDF download</h3><figure class="kg-card kg-image-card"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/oQfJtME.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"></figure><p>If you&#x2019;d rather read this as a well put together PDF, <a href="https://www.ohansemmanuel.com/books/how-to-build-strongly-typed-polymorphic-react-components">download it here</a>.</p><p>At 55-pages, this makes for a decent weekend read.</p><h2 id="what-are-polymorphic-components">What are Polymorphic Components?</h2><p>When you learn React, one of the first concepts you learn is building reusable components.</p><p>The fine art of writing components once, and reusing them multiple times.</p><p>If you remember from React 101, the essential building blocks of classic reusable components are props and state&#x2014;where props are external, and state, internal.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/roTfQm8.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>Building blocks of reusable components</figcaption></figure><p>The essential building blocks of reusability remain valid for this article. However, we will take advantage of props to allow the users of your component decide what &#x201C;element&#x201D; to eventually render.</p><p>OK, wait, don&#x2019;t get confused by that.</p><p>Consider the following React component:</p><pre><code class="language-js">const MyComponent = (props) =&gt; {
  return (
    &lt;div&gt;
     This is an excellent component with props {JSON.stringify(props)}
   &lt;/div&gt;
  );
};
</code></pre><p>Typically, your component would receive some props. You&#x2019;d go ahead to use these internally and then finally render some React element which gets translated to the corresponding DOM element. In this case, the <code>div</code> element.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/iRoavj3.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>Rendering a single element</figcaption></figure><p>What if your component could take in <code>props</code> to do more than just provide some data to be consumed within your component?</p><p>Instead of <code>MyComponent</code> always rendering a <code>div</code>, what if you could pass in a prop to determine the eventual element rendered to the DOM?</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/s3HkSoR.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>Polymorphic component props</figcaption></figure><p>This is where polymorphic components come in.</p><p>By standard definition, the word <em>Polymorphic</em> means occurring in several forms. In the world of React components, a polymorphic component is a component that can be rendered with a different container element / node.</p><p>Even though the concept may sound alien to you (if you&#x2019;re new to it in general), you&#x2019;ve likely already used a Polymorphic component.</p><h2 id="examples-of-polymorphic-components-in-the-real-world">Examples of Polymorphic components in the real-world</h2><p>Open-source component libraries typically implement some sort of Polymorphic component.</p><p>Let&#x2019;s consider some you may be familiar with.</p><blockquote>I may not discuss your favourite open-source library, but please don&apos;t hesitate to take a look at your favourite OS library after you understand the examples here.</blockquote><h3 id="chakra-ui">Chakra ui</h3><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/0n7tgQc.jpg" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>Chakra UI</figcaption></figure><p><a href="https://chakra-ui.com/">Chakra UI</a> has been my component library of choice for a decent number of production applications.</p><p>It&#x2019;s easy to use, has dark-theme support and is accessible by default (oh, not to forget the subtle component animations!).</p><p>So, how does <code>Chakra UI</code> implement polymorphic props? The answer is by exposing an <code>as</code> prop.</p><p>The <code>as</code> prop is passed to a component to determine what eventual container element to render.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/CleanShot-2022-04-09-at-10.52.37@2x.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>The chakra UI as prop</figcaption></figure><p>Using the <code>as</code> prop is quite straightforward.</p><p>You pass it to the component, in this case, <code>Box</code> :</p><pre><code class="language-js">&lt;Box as=&quot;button&quot;&gt; Hello &lt;/Box&gt;
</code></pre><p>And the component will render a <code>button</code> element.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/CleanShot-2022-04-09-at-10.53.34@2x.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>The Box component rendered as a &quot;button&quot;</figcaption></figure><p>If you went ahead to change the <code>as</code> prop to a <code>h1</code>:</p><pre><code class="language-js">&lt;Box as=&quot;h1&quot;&gt; Hello &lt;/Box&gt;
</code></pre><p>Now, the <code>Box</code> component renders a <code>h1</code>:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/CleanShot-2022-04-09-at-10.55.30@2x.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>The Box component rendered as an &quot;h1&quot;</figcaption></figure><p>That&#x2019;s a polymorphic component at work!</p><p>This component can be rendered to entirely unique elements, all by passing down a single prop.</p><h3 id="material-ui%E2%80%99s-component-prop">Material UI&#x2019;s component prop</h3><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/IMpJTMq.jpg" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>Material UI</figcaption></figure><p>Material UI in most cases needs no introduction. It&#x2019;s been a staple of component libraries for years now. It&#x2019;s a robust component library with a mature user base.</p><p>Similar to chakra UI, material UI allows for a polymorphic prop called <code>component</code> &#x2014; it obviously doesn&#x2019;t matter what you choose to call your polymorphic prop.</p><p>Its usage is just as similar. You pass it to a component, stating what element or custom component you&#x2019;d like to render.</p><p>Enough talking, here&#x2019;s an example from the official docs:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/CleanShot-2022-04-09-at-11.02.51@2x.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>The Material UI component prop</figcaption></figure><pre><code class="language-js">&lt;List component=&quot;nav&quot;&gt;
  &lt;ListItem button&gt;
    &lt;ListItemText primary=&quot;Trash&quot; /&gt;
  &lt;/ListItem&gt;
&lt;/List&gt;
</code></pre><p><code>List</code> is passed a component prop of <code>nav</code>, and so when this is rendered, it&#x2019;ll render a <code>nav</code> container element.</p><p>Another user may use the same component, but not as a navigation. They may just want to render a to-do list:</p><pre><code class="language-js">&lt;List component=&quot;ol&quot;&gt;
  ...
&lt;/List&gt;
</code></pre><p>And in this case, <code>List</code> will render an ordered list <code>ol</code> element.</p><p>Talk about flexibility! See a summary of the <a href="https://github.com/ohansemmanuel/polymorphic-react-component/blob/master/use-cases.pdf">use cases</a> (PDF) for polymorphic components.</p><p>As you&#x2019;ll come to see in the following sections of this article, polymorphic components are powerful. Apart from just accepting a prop of an element type, they can also accept custom components as props.</p><p>This will be discussed in a coming section of this article. For now, let&#x2019;s get you building your first Polymorphic component!</p><h2 id="build-your-first-polymorphic-component">Build your first Polymorphic component</h2><p>Contrary to what you may think, building your first Polymorphic component is quite straightforward.</p><p>Here&#x2019;s a basic implementation:</p><pre><code class="language-js">const MyComponent = ({ as, children }) =&gt; {
  const Component = as || &quot;span&quot;;

  return &lt;Component&gt;{children}&lt;/Component&gt;;
};
</code></pre><p>What to note here is the polymorphic prop <code>as</code> &#x2014; similar to chakra UI&#x2019;s. This is the exposed prop to control the render element of the Polymorphic component.</p><p>Secondly, note that the <code>as</code> prop isn&#x2019;t rendered directly. The following would be wrong:</p><pre><code class="language-js">const MyComponent = ({ as, children }) =&gt; {
  // wrong render below &#x1F447; 
  return &lt;as&gt;{children}&lt;/as&gt;;
};
</code></pre><p>When rendering an <a href="https://reactjs.org/docs/jsx-in-depth.html#choosing-the-type-at-runtime">element type at runtime</a>, you must first assign to a capitalised variable, and then render the capitalised variable.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/V2XfWQz.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>Using a capitlized variable</figcaption></figure><p>Now you can go ahead and use this component as follows:</p><pre><code class="language-js">&lt;MyComponent as=&quot;button&quot;&gt;Hello Polymorphic!&lt;MyComponent&gt;
&lt;MyComponent as=&quot;div&quot;&gt;Hello Polymorphic!&lt;/MyComponent&gt;
&lt;MyComponent as=&quot;span&quot;&gt;Hello Polymorphic!&lt;/MyComponent&gt;
&lt;MyComponent as=&quot;em&quot;&gt;Hello Polymorphic!&lt;/MyComponent&gt;
</code></pre><p>Note the different <code>as</code> prop passed to the rendered components above.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/hK2Bt8Q.jpg" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>The different rendered elements</figcaption></figure><h2 id="the-problems-with-this-simple-implementation">The Problems with this simple implementation</h2><p>The implementation in the previous section, while quite standard, has many demerits.</p><p>Let&#x2019;s explore some of these.</p><h3 id="1-the-as-prop-can-receive-invalid-html-elements">1. The as prop can receive invalid HTML elements.</h3><p>Presently, it is possible for a user to go ahead and write the following:</p><pre><code class="language-js">&lt;MyComponent as=&quot;emmanuel&quot;&gt;Hello Wrong Element&lt;/MyComponent&gt;
</code></pre><p>The<code>as</code> prop passed here is <code>emmanuel</code>. Emmanuel is obviously a wrong HTML element, but the browser also tries to render this element.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/CfOkz1q.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>Rendering a wrong HTML element type</figcaption></figure><p>An ideal development experience is to show some kind of error during development. For example, a user may make a simple typo: <code>divv</code> instead of <code>div</code> &#x2014; and would get no indication of what&#x2019;s wrong.</p><h3 id="2-wrong-attributes-can-be-passed-for-valid-elements">2. Wrong attributes can be passed for valid elements.</h3><p>Consider the following component usage:</p><pre><code class="language-js">&lt;MyComponent as=&quot;span&quot; href=&quot;https://www.google.com&quot;&gt;
   Hello Wrong Attribute
&lt;/MyComponent&gt;
</code></pre><p>A consumer can pass a <code>span</code> element to the <code>as</code> prop, and an <code>href</code> prop as well.</p><p>This is technically invalid.</p><p>A <code>span</code> element does not (and should not) take in an <code>href</code> attribute. That is an invalid HTML syntax.</p><p>However, now, a consumer of the component we&apos;ve built could go ahead to write this, and they&#x2019;ll get no errors during development.</p><h3 id="3-no-attribute-support">3. No attribute support!</h3><p>Consider the simple implementation again:</p><p>The only props this component accepts are <code>as</code> and <code>children</code>, nothing else. There&#x2019;s no attribute support for even valid <code>as</code> element props, i.e., if <code>as</code> were an anchor element <code>a</code>, we should also support passing an <code>href</code> to the component.</p><pre><code class="language-js">&lt;MyComponent as=&quot;a&quot; href=&quot;...&quot;&gt;A link &lt;/MyComponent&gt;
</code></pre><p>Even though <code>href</code> is passed in the example above, the component implementation receives no other props. Only <code>as</code> and <code>children</code> are deconstructed.</p><p>Your initial thoughts may be to go ahead and spread every other prop passed to the component as follows:</p><pre><code class="language-js">const MyComponent = ({ as, children, ...rest }) =&gt; {
  const Component = as || &quot;span&quot;;

  return &lt;Component {...rest}&gt;{children}&lt;/Component&gt;;
};
</code></pre><p>It seems like a decent solution, but now it highlights the second problem mentioned above. Wrong attributes will now be passed down to the component as well.</p><p>Consider the following:</p><pre><code class="language-js">&lt;MyComponent as=&quot;span&quot; href=&quot;https://www.google.com&quot;&gt;
   Hello Wrong Attribute
&lt;/MyComponent&gt;
</code></pre><p>And note the eventual rendered markup:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/FCdGoED.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>A span element with an href attribute</figcaption></figure><p>A <code>span</code> with an <code>href</code> is invalid HTML.</p><p>How do we resolve these concerns?</p><p>To be clear, there&#x2019;s no magic wand to wave here. However, we&#x2019;re going to leverage Typescript to ensure you build strongly typed Polymorphic components.</p><p>Upon completion, developers using your component will avoid the runtime errors above and instead catch them during development or build time&#x2014;thanks to Typescript.</p><h3 id="why-is-this-bad">Why is this bad?</h3><p>To recap, the current issues with our simple implementation is subpar because:</p><ul><li>It provides a terrible developer experience</li><li>It is not type-safe. Bugs can (and will) creep in.</li></ul><h2 id="welcome-typescript">Welcome, Typescript</h2><p>If you&#x2019;re reading this, a prerequisite is you already know some Typescript&#x2014;at least the basics. If you have no clue what Typescript is, I strongly recommend giving this <a href="https://www.typescriptlang.org/docs/handbook/typescript-from-scratch.html">document</a> a read first.</p><p>OK, we&#x2019;ve established a starting point: we will leverage Typescript to solve the concerns aforementioned. Essentially, we will leverage Typescript to build strongly typed Polymorphic components.</p><p>The first two requirements we will start off with include:</p><ul><li>The <code>as</code> prop should not receive invalid HTML element strings</li><li>Wrong attributes should not be passed for valid elements</li></ul><p>In the following section, we will get started introducing Typescript to make our solution more robust, developer friendly and production worthy.</p><h3 id="introduction-to-typescript-generics">Introduction to Typescript Generics</h3><p>If you have a solid grasp of Typescript generics, please feel free to skip this section. This only provides a brief introduction for readers who aren&#x2019;t as familiar with generics.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/7la9gRw.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>Javascript variables and Typescript generics</figcaption></figure><p>If you&#x2019;re new to Typescript generics, they can come off as difficult, but once you get the hang of it, you&#x2019;ll see it for what it truly is: an arguably simple construct for parametizing your types.</p><p>So, what are generics?</p><p>A simple mental model to approach generics is to see them as special variables for your types. Where Javascript has variables, Typescript has generics (for types).</p><p>Let&#x2019;s have a look at a classic example.</p><p>Below is an <code>echo</code> function where <code>v</code> represents any arbitrary value:</p><pre><code class="language-js">const echo = (v) =&gt; {
  console.log(v)
  return v
}
</code></pre><p>The <code>echo</code> function takes in this value <code>v</code>, logs it to the console, and then returns the same value to the caller. No input transformations carried out!</p><p>Now, we can go ahead and use this function on varying input types:</p><pre><code class="language-js">echo(1) // number
echo(&quot;hello world&quot;) // string 
echo({}) // object 
</code></pre><p>And this works perfectly!</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/fA2xte1.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>The echo function</figcaption></figure><p>There&#x2019;s just one problem. We haven&#x2019;t typed this function at all.</p><p>Let&#x2019;s sprinkle some typescript in here. &#x1F9C1;</p><p>Start off with a naive way to accept any input values <code>v</code> by using the <code>any</code> keyword:</p><pre><code class="language-ts">const echo = (v: any) =&gt; {
  console.log(v)
  return v
}
</code></pre><p>It seems to work.</p><p>You&#x2019;ll get no typescript errors when you do this. However, if you take a look at where you invoke this function, you&#x2019;ll notice one important thing. You&#x2019;ve now lost every form of type safety.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/KuISxYH.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>The any type</figcaption></figure><p>This may not be clear now, but if you went ahead to perform an operation as follows:</p><pre><code class="language-ts">const result = echo(&quot;hello world&quot;)
let failure = result.hi.me
</code></pre><p>Line 2 will fail with an error.</p><pre><code class="language-ts">let failure = result.hi.me
</code></pre><p><code>result</code> is technically a <code>string</code> because <code>echo</code> will return the string <code>hello world</code>, and <code>&quot;hello world&quot;.hi.me</code> will throw an error.</p><p>However, by typing <code>v</code> as <code>any</code>, <code>result</code> is equally typed as <code>any</code>. This is because <code>echo</code> returns the same value. Typescript infers the return type as the same as <code>v</code>. i.e., <code>any</code>.</p><p>With <code>result</code> being of type <code>any</code>, you get no type safety here. Typescript cannot catch this error. This is one of the downsides of using <code>any</code>.</p><p>OK, using <code>any</code> here is a bad idea. Let&#x2019;s avoid it.</p><p>What else could you possibly do?</p><p>Another solution will be to spell out exactly what types are acceptable by the <code>echo</code> function, as follows:</p><pre><code class="language-ts">const echo = (v: string | number | object) =&gt; {
  console.log(v);
  return v;
};
</code></pre><p>Essentially, you represent <code>v</code> with a union type.</p><p><code>v</code> can either be a <code>string</code>, a <code>number</code> or an <code>object</code>.</p><p>This works great.</p><p>Now, If you go ahead to wrongly access a property on the return type of <code>echo</code>, you&#x2019;ll get an appropriate error. e.g.,</p><pre><code class="language-ts">const result = echo(&quot;hi&quot;).hi
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/u6wbltd.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>Property &quot;hi&quot; does not exist error</figcaption></figure><p>You&#x2019;ll get the following error: <code>Property &apos;hi&apos; does not exist on type &apos;string | number | object&apos;.</code></p><p>This seems perfect.</p><p>We&#x2019;ve represented <code>v</code> with a decent rage of acceptable values.</p><p>However, what if you wanted to accept more value types? You&#x2019;d have to keep adding more union types.</p><p>Is there a better way to handle this? e.g., by declaring some sort of variable type based on whatever the user passes to <code>echo</code> ?</p><p>For a start, let&apos;s replace the union type with an hypothetical type we&#x2019;ll call <code>Value</code>:</p><pre><code class="language-ts">const echo = (v: Value) =&gt; {
  console.log(v);
  return v;
};

</code></pre><p>Once you do this, you&#x2019;ll get the following Typescript error:</p><blockquote>Cannot find name &apos;Value&apos;.ts (2304)</blockquote><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/zOE94uU.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>Cannot find name &quot;Value&quot; error</figcaption></figure><p>This is expected.</p><p>However, here&#x2019;s the beauty of generics. We can go ahead to define this <code>Value</code> type as a generic&#x2014;some sort of variable represented by the type of <code>v</code> passed to <code>echo</code> when invoked.</p><p>To complete this, we&#x2019;ll use angle braces just after the <code>=</code> sign as follows:</p><pre><code class="language-ts">const echo = &lt;Value&gt; (v: Value) =&gt; {
  console.log(v);
  return v;
};
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/ZbCVd2e.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>The &quot;Value&quot; generic</figcaption></figure><p>If you&#x2019;re coding along, you&#x2019;ll notice there are no more Typescript errors. Typescript understands this to be a generic. The <code>Value</code> type is a generic.</p><p>But how does Typescript know what <code>Value</code> is?</p><p>Well, this is where the variable form of a generic becomes evident.</p><p>Take a look at how <code>echo</code> is invoked:</p><pre><code class="language-ts">echo(1)
echo(&quot;hello world&quot;)
echo({})
</code></pre><p>The generic <code>Value</code> will take on whatever the argument type passed into <code>echo</code> at invocation time.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/5jdJEFr.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>The generic represnets the argument type of the echo function</figcaption></figure><p>For example, with <code>echo(1)</code>, the type of <code>Value</code> will be the literal number <code>1</code>. For <code>echo(&quot;hello world&quot;)</code>, the type of <code>Value</code> will be the literal string <code>hello world</code></p><p>Note how this changes based on the type of argument passed to <code>echo</code>.</p><p>This is wonderful.</p><p>If you went ahead to perform any operations on the return type of <code>echo</code>, you&#x2019;ll get all the type safety you&#x2019;d expect&#x2014;without specifically specifying a single type but by representing the input with a <strong>generic</strong> aka a variable type.</p><h4 id="constraining-generics">Constraining Generics</h4><p>Having learned the basics of generics, there&#x2019;s one more concept to understand before we get back to leveraging Typescript in our polymorphic component solution.</p><p>Let&#x2019;s consider a variant of the <code>echo</code> function. Call this <code>echoLength</code>:</p><pre><code class="language-ts">const echoLength = &lt;Value&gt; (v: Value) =&gt; {
    console.log(v.length);
    return v.length;
};
</code></pre><p>Instead of echoing the input value <code>v</code>, the function echoes the <code>length</code> of the input value, i.e., <code>v.length</code>.</p><p>If you wrote this code out as is, the Typescript compiler will yell with an error:</p><blockquote>Property &apos;length&apos; does not exist on type &apos;Value&apos;.ts (2339)</blockquote><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/XlhPvYk.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>Property &quot;length&quot; does not exist error</figcaption></figure><p>This is quite an important error.</p><p>The <code>echoLength</code> parameter, <code>v</code>, is represented by the generic <code>Value</code> - which in fact represents the type of the argument passed to the function.</p><p>However, within the body of the function, we are accessing the <code>length</code> property of the variable parameter.</p><p>So, what&#x2019;s the problem here?</p><p>The issue is, not every input will have a <code>length</code> property.</p><p>The generic <code>Value</code> as it stands represents any argument type passed by the function caller, however, not every argument type will have a <code>length</code> property.</p><p>Consider the following:</p><pre><code class="language-ts">echoLength(&quot;hello world&quot;)
echoLength(2)
echoLength({})
</code></pre><p><code>echoLength(&quot;hello world&quot;)</code> will work as expected because a string has a <code>length</code> property.</p><p>However, the other two examples will return <code>undefined</code>. Numbers and objects don&#x2019;t have <code>length</code> properties. Hence, the code within the function body isn&#x2019;t the most type safe.</p><p>Now, how do we fix this?</p><p>We need to be able to take in a generic, but we want to specify exactly what kind of generic is valid.</p><p>In more technical terms, we need to constrain the generic accepted by this function to be limited to types that have a <code>length</code> property.</p><p>To accomplish this, we will leverage the <code>extends</code> keyword.</p><p>Take a look:</p><pre><code class="language-ts">const echoLength = &lt;Value extends {length: number}&gt; (v: Value) =&gt; {
    console.log(v.length);
    return v.length;
};
</code></pre><p>Now, when you declare the <code>Value</code> generic, add <code>extends {length: number}</code> to denote that the generic will be constrained to types which have a <code>lenght</code> property.</p><p>If you go ahead to use <code>echoLength</code> as before, you should now get a Typescript error when you pass in values without a length property, e.g.,</p><pre><code class="language-ts">// these will yield a typescript error
echoLength(2)
echoLength({})
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/FfBCfLG.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>Argument of type number is not assignable error</figcaption></figure><p>What we&#x2019;ve done here is constraining the <code>Value</code> generic to a specific mould. Yes, we want variable types. But we only want those that fit this specific mould, i.e., that fit a certain type signature.</p><p>Lovely!</p><p>With these two concepts understood, we&#x2019;ll now head back to updating our polymorphic component solution to be a lot more type safe &#x2014; starting with the initial requirements we&#x2019;d set.</p><h2 id="making-sure-the-as-prop-only-receives-valid-html-element-strings">Making sure the as prop only receives valid HTML element strings</h2><p>Here&#x2019;s our current solution:</p><pre><code class="language-ts">const MyComponent = ({ as, children }) =&gt; {
  const Component = as || &quot;span&quot;;

  return &lt;Component&gt;{children}&lt;/Component&gt;;
};
</code></pre><p>To make the next sections of this guide practical, we&#x2019;ll change the name of the component from <code>MyComponent</code> to <code>Text</code> i.e., assuming we&#x2019;re building a polymorphic Text component.</p><p>Now, with your knowledge of generics, it becomes obvious that we&#x2019;re better off representing <code>as</code> with a generic type, i.e., a variable type based on whatever the user passes in.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/jnVL2bk.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>The as prop</figcaption></figure><p>Let&#x2019;s go ahead and take the first step as follows:</p><pre><code class="language-ts">export const Text = &lt;C&gt;({
  as,
  children,
}: {
  as?: C;
  children: React.ReactNode;
}) =&gt; {
  const Component = as || &quot;span&quot;;

  return &lt;Component&gt;{children}&lt;/Component&gt;;
};
</code></pre><p>Note how the generic <code>C</code> is defined and then passed on in the type definition for the prop <code>as</code>.</p><p>However, if you wrote this seemingly perfect code, you&#x2019;ll have Typescript yelling out numerous errors with more squiggly red lines than you&#x2019;d like &#x1F937;&#x200D;&#x2640;&#xFE0F;</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/S12QYoG.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>The JSX generic error</figcaption></figure><p>What&#x2019;s going on here is a flaw in the <a href="https://stackoverflow.com/questions/32308370/what-is-the-syntax-for-typescript-arrow-functions-with-generics?">syntax for generics</a> in <code>.tsx</code> files. There are two ways to solve this.</p><h4 id="1-add-a-comma-after-the-generic-declaration">1. Add a comma after the generic declaration.</h4><p>This is the syntax for declaring multiple generics. Once you do this, the typescript compiler clearly understands your intent and the errors banished.</p><pre><code class="language-ts">// note the comma after &quot;C&quot; below &#x1F447;
export const Text = &lt;C,&gt;({
  as,
  children,
}: {
  as?: C;
  children: React.ReactNode;
}) =&gt; {
  const Component = as || &quot;span&quot;;

  return &lt;Component&gt;{children}&lt;/Component&gt;;
};
</code></pre><h4 id="2-constrain-the-generic">2. Constrain the generic.</h4><p>The second option is to constrain the generic as you see fit. For starters, you can just use the <code>unknown</code> type as follows:</p><pre><code class="language-ts">// note the extends keyword below &#x1F447;
export const Text = &lt;C extends unknown&gt;({
  as,
  children,
}: {
  as?: C;
  children: React.ReactNode;
}) =&gt; {
  const Component = as || &quot;span&quot;;

  return &lt;Component&gt;{children}&lt;/Component&gt;;
};
</code></pre><p>For now, I&#x2019;ll stick to solution 2 because it&#x2019;s closer to our final solution. In most cases, I use the multiple generic syntax (adding a comma).</p><p>OK, now what next?</p><p>With our current solution, we get another Typescript error:</p><blockquote>JSX element type &apos;Component&apos; does not have any construct or call signatures.ts(2604)</blockquote><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/Qwm3L4i.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>No construct or call error</figcaption></figure><p>This is similar to the error we had when we worked with the <code>echoLength</code> function. Just like accessing the <code>length</code> property of an unknown variable type, the same may be said here.</p><p>Trying to render any generic type as a valid React component doesn&#x2019;t make sense.</p><p>We need to constrain the generic ONLY to fit the mould of a valid React element type.</p><p>To achieve this, we&#x2019;ll leverage the internal React type: <code>React.ElementType</code>, and make sure the generic is constrained to fit that type:</p><pre><code class="language-ts">// look just after the extends keyword &#x1F447;
export const Text = &lt;C extends React.ElementType&gt;({
  as,
  children,
}: {
  as?: C;
  children: React.ReactNode;
}) =&gt; {
  const Component = as || &quot;span&quot;;

  return &lt;Component&gt;{children}&lt;/Component&gt;;
};
</code></pre><p>Note that if you&#x2019;re on an older version of React, you may have to import React as follows:</p><pre><code class="language-ts">import React from &apos;react&apos;
</code></pre><p>With this, we have no more errors!</p><p>Now, if you go ahead and use this component as follows, it&#x2019;ll work just fine:</p><pre><code class="language-ts">&lt;Text as=&quot;div&quot;&gt;Hello Text world&lt;/Text&gt;
</code></pre><p>However, If you pass an invalid <code>as</code> prop, you&#x2019;ll now get an appropriate typescript error. Consider the example below:</p><pre><code class="language-ts">&lt;Text as=&quot;emmanuel&quot;&gt;Hello Text world&lt;/Text&gt;
</code></pre><p>And the error thrown:</p><blockquote>Type &apos;&quot;emmanuel&quot;&apos; is not assignable to type &apos;ElementType&lt;any&gt; | undefined&apos;.</blockquote><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/4LMUlJ4.jpg" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>Type emmanuel is not assignable error</figcaption></figure><p>This is excellent!</p><p>We now have a solution that doesn&#x2019;t accept gibberish for the <code>as</code> prop and will also prevent against nasty typos, e.g., <code>divv</code> instead of <code>div</code>.</p><p>This is a much better developer experience!</p><h2 id="handling-valid-component-attributes">Handling valid component attributes</h2><p>In solving this second use case, you&#x2019;ll come to appreciate how powerful generics truly are.</p><p>First, you do have to understand what we&#x2019;re trying to accomplish here.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/q0KQkE6.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>The &apos;as&apos; prop and other valid props based on &apos;as&apos;</figcaption></figure><p>Once we receive a generic <code>as</code> type, we want to make sure that the remaining props passed to our component are relevant, based on the <code>as</code> prop.</p><p>So, for example, if a user passed in an <code>as</code> prop of <code>img</code>, we&#x2019;d want <code>href</code> to equally be a valid prop!</p><p>To give you a sense of how we&#x2019;d accomplish this, take a look at the current state of our solution:</p><pre><code class="language-ts">export const Text = &lt;C extends React.ElementType&gt;({
  as,
  children,
}: {
  as?: C;
  children: React.ReactNode;
}) =&gt; {
  const Component = as || &quot;span&quot;;

  return &lt;Component&gt;{children}&lt;/Component&gt;;
};
</code></pre><p>The component props are now represented by:</p><pre><code class="language-ts">{
  as?: C;
  children: React.ReactNode;
}
</code></pre><p>In pseudocode, what we&#x2019;d like would be the following:</p><pre><code class="language-ts">{
  as?: C;
  children: React.ReactNode;
} &amp; {
  ...otherValidPropsBasedOnTheValueOfAs
}
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/bwNRHea.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>The pseudocode illustrated</figcaption></figure><p>This requirement is enough to leave one grasping at straws. We can&#x2019;t possibly write a function that determines appropriate types based on the value of <code>as</code>, and it&#x2019;s not smart to list out a union type manually.</p><p>Well, what if there was a provided type from <code>React</code> that acted as a &#x201C;function&#x201D; that&#x2019;ll return valid element types based on what you pass it?</p><p>Before introducing the solution, let&#x2019;s have a bit of a refactor. Let&#x2019;s pull out the props of the component into a separate type:</p><pre><code class="language-ts">
// &#x1F447; See TextProps pulled out below 
type TextProps&lt;C extends React.ElementType&gt; = {
  as?: C;
  children: React.ReactNode;
} 


export const Text = &lt;C extends React.ElementType&gt;({
  as,
  children,
}: TextProps&lt;C&gt;) =&gt; { // &#x1F448; see TextProps used 
  const Component = as || &quot;span&quot;;
  return &lt;Component&gt;{children}&lt;/Component&gt;;
};

</code></pre><p>What&#x2019;s important here is to note how the generic is passed on to <code>TextProps&lt;C&gt;</code>. Similar to a function call in Javascript &#x2014; but with angle braces.</p><p>Now, on to the solution.</p><p>The magic wand here is to leverage the <code>React.ComponentPropsWithoutRef</code> type as shown below:</p><pre><code class="language-ts">type TextProps&lt;C extends React.ElementType&gt; = {
  as?: C;
  children: React.ReactNode;
} &amp; React.ComponentPropsWithoutRef&lt;C&gt;; // &#x1F448; look here 

export const Text = &lt;C extends React.ElementType&gt;({
  as,
  children,
}: TextProps&lt;C&gt;) =&gt; {
  const Component = as || &quot;span&quot;;
  return &lt;Component&gt;{children}&lt;/Component&gt;;
};
 
</code></pre><p>Note that we&#x2019;re introducing an intersection here. Essentially, we&#x2019;re saying, the type of <code>TextProps</code> is an object type containing <code>as</code> and <code>children</code>, and some other types represented by <code>React.ComponentPropsWithoutRef</code>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/bwNRHea.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>ComponentPropsWithoutRef plus TextProps</figcaption></figure><p>If you read the code, it perhaps becomes apparent what&#x2019;s going on here.</p><p>Based on the type of <code>as</code>, represented by the generic <code>C</code>, <code>React.componentPropsWithoutRef</code> will return valid component props that correlates with the string attribute passed to the <code>as</code> prop.</p><p>There&#x2019;s one more significant point to note.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/YN9SUJz.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>Exploring the different ComponentProps type variants</figcaption></figure><p>If you just started typing and rely on intellisense from your editor, you&#x2019;d realise there are three variants of the <code>React.ComponentProps...</code> type.</p><p>(I) <code>React.ComponentProps</code></p><p>(Ii) <code>React.ComponentPropsWithRef</code></p><p>(Iii) <code>React.ComponentPropsWithoutRef</code></p><p>If you attempted to use the first, <code>ComponentProps</code>, you&#x2019;d see a relevant note that reads:</p><blockquote>Prefer <code>ComponentPropsWithRef</code>, if the <code>ref</code> is forwarded, or <code>ComponentPropsWithoutRef</code> when refs are not supported.</blockquote><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/fVK1VWf.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>Note to prefer ComponentPropsWithRef or ComponentPropsWithoutRef</figcaption></figure><p>And this is precisely what we&#x2019;ve done.</p><p>For now, we will ignore the use case for supporting a <code>ref</code> prop and stick to <code>ComponentPropsWithoutRef</code>.</p><p>Now, let&#x2019;s give the solution a try!</p><p>If you go ahead and use this component wrongly, e.g., passing a valid <code>as</code> prop with other incompatible props, you&#x2019;ll get an error.</p><pre><code class="language-ts">&lt;Text as=&quot;div&quot; href=&quot;www.google.com&quot;&gt;Hello Text world&lt;/Text&gt;

</code></pre><p>A value of <code>div</code> is perfectly valid for the <code>as</code> prop, but a <code>div</code> should NOT have an <code>href</code> attribute. That&#x2019;s wrong, and righty caught by Typescript with the error: <code>Property &apos;href&apos; does not exist on type ...</code></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/ZQbJ9zG.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>Property href does not exist error</figcaption></figure><p>This is great! We&#x2019;ve got an even better (robust) solution.</p><p>Finally, make sure to pass on other props down to the rendered element:</p><pre><code class="language-ts">type TextProps&lt;C extends React.ElementType&gt; = {
  as?: C;
  children: React.ReactNode;
} &amp; React.ComponentPropsWithoutRef&lt;C&gt;; 

export const Text = &lt;C extends React.ElementType&gt;({
  as,
  children,
  ...restProps, // &#x1F448; look here
}: TextProps&lt;C&gt;) =&gt; {
  const Component = as || &quot;span&quot;;
	
  // see restProps passed &#x1F447;
  return &lt;Component {...restProps}&gt;{children}&lt;/Component&gt;;
};
</code></pre><p>Let&#x2019;s keep going &#x1F64C;&#x1F3FC;</p><h2 id="handling-default-as-attributes">Handling default as attributes</h2><p>Consider our current solution:</p><pre><code class="language-ts">export const Text = &lt;C extends React.ElementType&gt;({
  as,
  children,
  ...restProps
}: TextProps&lt;C&gt;) =&gt; {
  const Component = as || &quot;span&quot;; // &#x1F448; look here

  return &lt;Component {...restProps}&gt;{children}&lt;/Component&gt;;
};
</code></pre><p>Particularly pay attention to where a default element is provided if the <code>as</code> prop is omitted.</p><pre><code class="language-ts">const Component = as || &quot;span&quot;
</code></pre><p>This is properly represented in the Javascript world, i.e., by implementation, if <code>as</code> is optional, it&#x2019;ll default to a <code>span</code>.</p><p>The question is, how does Typescript handle this case? i.e., when <code>as</code> isn&#x2019;t passed? Are we equally passing a default type?</p><p>Well, the answer is no. But below&#x2019;s a practical example.</p><p>If you went ahead to use the <code>Text</code> component as follows:</p><pre><code class="language-ts">&lt;Text&gt;Hello Text world&lt;/Text&gt;
</code></pre><p>Note that we&#x2019;ve passed no <code>as</code> prop here. Will Typescript be aware of the valid props for this component?</p><p>Let&#x2019;s go ahead and add an <code>href</code>:</p><pre><code class="language-ts">&lt;Text href=&quot;https://www.google.com&quot;&gt;Hello Text world&lt;/Text&gt;

</code></pre><p>If you go ahead and do this, you&#x2019;ll get no errors.</p><p>That&#x2019;s bad.</p><p>A <code>span</code> should not receive an <code>href</code> prop / attribute. While we default to a span in the implementation, Typescript is unaware of this default implementation. Let&#x2019;s fix this with a simple, generic default assignment:</p><pre><code class="language-ts">type TextProps&lt;C extends React.ElementType&gt; = {
  as?: C;
  children: React.ReactNode;
} &amp; React.ComponentPropsWithoutRef&lt;C&gt;;

/**
* See default below. TS will treat the rendered element as a 
span and provide typings accordingly
*/
export const Text = &lt;C extends React.ElementType = &quot;span&quot;&gt;({
  as,
  children,
  ...restProps
}: TextProps&lt;C&gt;) =&gt; {
  const Component = as || &quot;span&quot;;
  return &lt;Component {...restProps}&gt;{children}&lt;/Component&gt;;
};

</code></pre><p>The important bit is highlighted below:</p><pre><code class="language-ts">&lt;C extends React.ElementType = &quot;span&quot;&gt;
</code></pre><p>And voil&#xE0;! The previous example we had should now throw an error, i.e., when you pass <code>href</code> to the <code>Text</code> component without an <code>as</code> prop.</p><p>The error should read: <code>Property &apos;href&apos; does not exist on type ...</code></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/aMn1V7p.jpg" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>Property href does not exist on ... error</figcaption></figure><h2 id="the-component-should-be-reusable-with-its-props">The component should be reusable with its props</h2><p>Our current solution is much better than where we started. Give yourself a pat on the back for making it this far. However, it only gets more interesting from here.</p><p>The use case to cater to in this section is very applicable in the real world. There&#x2019;s a high chance that if you&#x2019;re building some sort of component, then that component will also take in some specific props, i.e., unique to the component.</p><p>Our current solution takes into consideration the <code>as</code>, <code>children</code> and other component prop based on the <code>as</code> prop. However, what if we wanted this component to handle its props?</p><p>Let&#x2019;s make this practical.</p><p>We will have the <code>Text</code> component receive a <code>color</code> prop. The <code>color</code> here will be any of the rainbow colours, or <code>black</code>.</p><p>We will go ahead and represent this as follows:</p><pre><code class="language-ts">type Rainbow =
  | &quot;red&quot;
  | &quot;orange&quot;
  | &quot;yellow&quot;
  | &quot;green&quot;
  | &quot;blue&quot;
  | &quot;indigo&quot;
  | &quot;violet&quot;;
</code></pre><p>Next, we must define the <code>color</code> prop in the <code>TextProps</code> object as follows:</p><pre><code class="language-ts">type TextProps&lt;C extends React.ElementType&gt; = {
  as?: C;
  color?: Rainbow | &quot;black&quot;; // &#x1F448; look here
  children: React.ReactNode;
} &amp; React.ComponentPropsWithoutRef&lt;C&gt;;
</code></pre><p>Before we go ahead, let&#x2019;s have a bit of a refactor. Let&apos;s represent the actual props of the <code>Text</code> component by a <code>Props</code> object, and specifically type only props specific to our component in the <code>TextProps</code> object.</p><p>This will become obvious, as you&#x2019;ll see below:</p><pre><code class="language-ts">
// new &quot;Props&quot; type
type Props &lt;C extends React.ElementType&gt; = TextProps&lt;C&gt;

export const Text = &lt;C extends React.ElementType = &quot;span&quot;&gt;({
  as,
  children,
  ...restProps,
}: Props&lt;C&gt;) =&gt; {
  const Component = as || &quot;span&quot;;
  return &lt;Component {...restProps}&gt;{children}&lt;/Component&gt;;
};
</code></pre><p>Now let&#x2019;s clean up <code>TextProps</code>:</p><pre><code class="language-ts">// before 
type TextProps&lt;C extends React.ElementType&gt; = {
  as?: C;
  color?: Rainbow | &quot;black&quot;; // &#x1F448; look here
  children: React.ReactNode;
} &amp; React.ComponentPropsWithoutRef&lt;C&gt;;

// after
type TextProps&lt;C extends React.ElementType&gt; = {
  as?: C;
  color?: Rainbow | &quot;black&quot;;
};
</code></pre><p>Now, <code>TextProps</code> should just contain props specific to our <code>Text</code> component: <code>as</code> and <code>color</code>.</p><p>We must now update the definition for <code>Props</code> to include the types we&#x2019;ve removed from <code>TextProps</code> i.e., <code>children</code> and <code>React.ComponentPropsWithoutRef&lt;C&gt;</code></p><p>For the <code>children</code> prop, we&#x2019;ll take advantage of the <code>React.PropsWithChildren</code> prop.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/32M5GDB.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>The PropsWithChildren type</figcaption></figure><p>The way <code>PropsWithChildren</code> works is easy to reason about. You pass it your component props, and it&#x2019;ll inject the children props definition for you.</p><p>Let&#x2019;s leverage that below:</p><pre><code class="language-ts">type Props &lt;C extends React.ElementType&gt; = 
React.PropsWithChildren&lt;TextProps&lt;C&gt;&gt;
</code></pre><p>Note how we use the angle braces.</p><p>This is the syntax for passing on generics. Essentially, the <code>React.PropsWithChildren</code> accepts your component props as a generic and augments it with the <code>children</code> prop. Sweet!</p><p>For <code>React.ComponentPropsWithoutRef&lt;C&gt;</code>, we&#x2019;ll just go ahead and leverage an intersection type here:</p><pre><code class="language-ts">type Props &lt;C extends React.ElementType&gt; = 
React.PropsWithChildren&lt;TextProps&lt;C&gt;&gt; &amp; 
React.ComponentPropsWithoutRef&lt;C&gt;
</code></pre><p>And here&#x2019;s the full current solution:</p><pre><code class="language-ts">type Rainbow =
  | &quot;red&quot;
  | &quot;orange&quot;
  | &quot;yellow&quot;
  | &quot;green&quot;
  | &quot;blue&quot;
  | &quot;indigo&quot;
  | &quot;violet&quot;;

type TextProps&lt;C extends React.ElementType&gt; = {
  as?: C;
  color?: Rainbow | &quot;black&quot;;
};

type Props &lt;C extends React.ElementType&gt; = 
React.PropsWithChildren&lt;TextProps&lt;C&gt;&gt; &amp; 
React.ComponentPropsWithoutRef&lt;C&gt;

export const Text = &lt;C extends React.ElementType = &quot;span&quot;&gt;({
  as,
  children,
}: Props&lt;C&gt;) =&gt; {
  const Component = as || &quot;span&quot;;
  return &lt;Component&gt; {children} &lt;/Component&gt;;
};
</code></pre><p>I know these can feel like a lot, but take a closer look, and it&#x2019;ll all make sense. It&#x2019;s really just putting together everything you&#x2019;ve learnt so far. Nothing should be particularly new.</p><p>All clear? Now, you&#x2019;re becoming something of a pro!</p><p>Having done this necessary refactor, we can now continue our solution. What we have now actually works. We&#x2019;ve explicitly typed the <code>color</code> prop, and you may go ahead to use it as follows:</p><pre><code class="language-ts">&lt;Text color=&quot;violet&quot;&gt;Hello world&lt;/Text&gt;
</code></pre><p>There&#x2019;s just one thing I&#x2019;m not particularly comfortable with.</p><p><code>color</code> turns out to also be a valid attribute for numerous HTML tags. This was the case pre-HTML5. So, if we removed <code>color</code> from our type definition, it&#x2019;ll be accepted as any valid string.</p><p>See below:</p><pre><code class="language-ts">
type TextProps&lt;C extends React.ElementType&gt; = {
  as?: C;
  // remove color from the definition here
};
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/m79hJH4.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>Removing the color type definition</figcaption></figure><p>Now, if you go ahead to use <code>Text</code> as before, it&#x2019;s equally valid:</p><pre><code class="language-ts">&lt;Text color=&quot;violet&quot;&gt;Hello world&lt;/Text&gt;
</code></pre><p>The only difference here is how it is typed. <code>color</code> is now represented by the following definition <code>color?: string | undefined</code></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/Nn9vY2A.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>The default color type</figcaption></figure><p>Again, this is NOT a definition we wrote in our types!</p><p>This is a default HTML typing where <code>color</code> is a valid attribute for most HTML elements. See this <a href="https://stackoverflow.com/questions/67142430/why-color-appears-as-html-attribute-on-a-div">stack-overflow question</a> for some more context.</p><p>Now, there are two ways to go here.</p><p>Firstly, you can keep our initial solution where we explicitly declared the <code>color</code> prop:</p><pre><code class="language-ts">type TextProps&lt;C extends React.ElementType&gt; = {
  as?: C;
  color?: Rainbow | &quot;black&quot;; // &#x1F448; look here
};
</code></pre><p>Secondly, you can go ahead and arguably provide some more safety. To achieve this, you must realise where the previous default <code>color</code> definition came from.</p><p>It came from the definition <code>React.ComponentPropsWithoutRef&lt;C&gt;</code> - this is what adds other props based on what the type of <code>as</code> is.</p><p>So, what we can do here is to explicitly remove any definition that exists in our component types from <code>React.ComponentPropsWithoutRef&lt;C&gt;</code></p><p>This can be tough to understand before you see it in action, so let&#x2019;s take it step by step.</p><p><code>React.ComponentPropsWithoutRef&lt;C&gt;</code> as stated earlier contains every other valid props based on the type of <code>as</code> e.g., <code>href</code>, <code>color</code>, etc.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/S7Yhi6A.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>The ComponentPropsWithoutRef type</figcaption></figure><p>Where these types all have their definition, e.g., <code>color?: string | undefined</code> etc.</p><p>It is possible that some values that exist in <code>React.ComponentPropsWithoutRef&lt;C&gt;</code> also exist in our component props type definition.</p><p>In our case, <code>color</code> exists in both!</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/U3XI6S3.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>ComponentPropsWithoutRef and TextProps</figcaption></figure><p>Instead of relying on our <code>color</code> definition to override what&#x2019;s coming from <code>React.ComponentPropsWithoutRef&lt;C&gt;</code>, we will explicitly remove any type that also exist in our component types definition.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/Vd2YT3K.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>Removing existing props from ComponentPropsWithoutRef</figcaption></figure><p>So, if any type exists in our component types definition, we will explicitly remove it from <code>React.ComponentPropsWithoutRef&lt;C&gt;</code>.</p><p>How do we do this?</p><p>Well, here&#x2019;s what we had before:</p><pre><code class="language-ts">type Props &lt;C extends React.ElementType&gt; = 
React.PropsWithChildren&lt;TextProps&lt;C&gt;&gt; &amp; 
React.ComponentPropsWithoutRef&lt;C&gt;
</code></pre><p>Instead of just having an intersection type where we just add everything that comes from <code>React.ComponentPropsWithoutRef&lt;C&gt;</code>, we will be more selective. We will use the <code>Omit</code> and <code>keyof</code> typescript utility types to perform some TS magic.</p><p>Take a look:</p><pre><code class="language-ts">// before 
type Props &lt;C extends React.ElementType&gt; = 
React.PropsWithChildren&lt;TextProps&lt;C&gt;&gt; &amp; 
React.ComponentPropsWithoutRef&lt;C&gt;

// after
type Props &lt;C extends React.ElementType&gt; = 
React.PropsWithChildren&lt;TextProps&lt;C&gt;&gt; &amp;   
Omit&lt;React.ComponentPropsWithoutRef&lt;C&gt;, keyof TextProps&lt;C&gt;&gt;;
</code></pre><p>The important bit is this:</p><pre><code class="language-ts">Omit&lt;React.ComponentPropsWithoutRef&lt;C&gt;, keyof TextProps&lt;C&gt;&gt;;
</code></pre><p>If you don&#x2019;t know how <code>Omit</code> and <code>keyof</code> work, below&#x2019;s a quick summary.</p><p><code>Omit</code> takes in two generics. The first is an object type, and the second a union of types you&#x2019;d like to &#x201C;omit&#x201D; from the object type.</p><p>Here&#x2019;s my favourite example. Consider a <code>Vowel</code> object type as follows:</p><pre><code class="language-ts">type Vowels = {
  a: &apos;a&apos;,
  e: &apos;e&apos;,
  i: &apos;i&apos;,
  o: &apos;o&apos;,
  u: &apos;u&apos;
}
</code></pre><p>This is an object type of key and value.</p><p>What if I wanted to derive a new type from <code>Vowels</code> called <code>VowelsInOhans</code>.</p><p>Well, I do know that the name <code>Ohans</code> contains two vowels <code>o</code> and <code>a</code>.</p><p>Instead of manually declaring these:</p><pre><code class="language-ts">type VowelsInOhans = {
  a: &apos;a&apos;,
  o: &apos;o&apos;
}
</code></pre><p>I can go ahead to leverage <code>Omit</code> as follows:</p><pre><code class="language-ts">type VowelsInOhans = Omit&lt;Vowels, &apos;e&apos; | &apos;i&apos; | &apos;u&apos;&gt;
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/ORYolPZ.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>The VowelsInOhans type using Omit</figcaption></figure><p><code>Omit</code> will &#x201C;omit&#x201D; the <code>e</code>, <code>i</code> and <code>u</code> keys from the object type <code>Vowels</code>.</p><p>On the other hand, <code>keyof</code> works as you would imagine. Think of <code>Object.keys</code> in Javascript.</p><p>Given an <code>object</code> type, <code>keyof</code> will return a union type of the keys of the object. Phew! That&#x2019;s a mouth full.</p><p>Here&#x2019;s an example:</p><pre><code class="language-ts">type Vowels = {
  a: &apos;a&apos;,
  e: &apos;e&apos;,
  i: &apos;i&apos;,
  o: &apos;o&apos;,
  u: &apos;u&apos;
}

type Vowel = keyof Vowels 
</code></pre><p>Now, <code>Vowel</code> will be a union type of the keys of <code>Vowels</code> i.e.,</p><pre><code class="language-ts">type Vowel = &apos;a&apos; | &apos;e&apos; | &apos;i&apos; | &apos;o&apos; | &apos;u&apos;
</code></pre><p>If you put these together and take a second look at our solution, it&#x2019;ll all come together nicely:</p><pre><code class="language-ts">Omit&lt;React.ComponentPropsWithoutRef&lt;C&gt;, keyof TextProps&lt;C&gt;&gt;;
</code></pre><p><code>keyof TextProps&lt;C&gt;</code> returns a union type of the keys of our component props. This is in turn passed to <code>Omit</code> i.e., to omit them from <code>React.ComponentPropsWithoutRef&lt;C&gt;</code>.</p><p>Sweet! &#x1F57A;</p><p>For completeness, let&#x2019;s go ahead and actually pass the <code>color</code> prop down to the rendered element:</p><pre><code class="language-ts">export const Text = &lt;C extends React.ElementType = &quot;span&quot;&gt;({
  as,
  color, // &#x1F448; look here
  children,
  ...restProps
}: Props&lt;C&gt;) =&gt; {
  const Component = as || &quot;span&quot;;
	
  // &#x1F447; compose an inline style object
  const style = color ? { style: { color } } : {};
	
  // &#x1F447; pass the inline style to the rendered element
  return (
    &lt;Component {...restProps} {...style}&gt;
      {children}
    &lt;/Component&gt;
  );
};
</code></pre><h2 id="create-a-reusable-utility-for-polymorphic-types">Create a reusable utility for Polymorphic types</h2><p>You must be proud of how come you&#x2019;ve come if you&#x2019;ve been following along.</p><p>We&#x2019;ve got a solution that works &#x2014; well.</p><p>However, now, let&#x2019;s take it one step further.</p><p>The solution we have works great for our <code>Text</code> component. However, what if you&#x2019;d rather have a solution you can reuse on any component of your choosing?</p><p>This way, you can have a reusable solution for every use case.</p><p>How does that sound? Lovely, I bet!</p><p>Let&#x2019;s get started.</p><p>First, here&#x2019;s the current complete solution with no annotations:</p><pre><code class="language-ts">type Rainbow =
  | &quot;red&quot;
  | &quot;orange&quot;
  | &quot;yellow&quot;
  | &quot;green&quot;
  | &quot;blue&quot;
  | &quot;indigo&quot;
  | &quot;violet&quot;;

type TextProps&lt;C extends React.ElementType&gt; = {
  as?: C;
  color?: Rainbow | &quot;black&quot;;
};

type Props&lt;C extends React.ElementType&gt; = React.PropsWithChildren&lt;
  TextProps&lt;C&gt;
&gt; &amp;
  Omit&lt;React.ComponentPropsWithoutRef&lt;C&gt;, keyof TextProps&lt;C&gt;&gt;;

export const Text = &lt;C extends React.ElementType = &quot;span&quot;&gt;({
  as,
  color,
  children,
  ...restProps
}: Props&lt;C&gt;) =&gt; {
  const Component = as || &quot;span&quot;;

  const style = color ? { style: { color } } : {};

  return (
    &lt;Component {...restProps} {...style}&gt;
      {children}
    &lt;/Component&gt;
  );
};
</code></pre><p>Succinct and practical.</p><p>If we made this reusable, then it has to work for any component. This means removing the hardcoded <code>TextProps</code> and representing that with a generic &#x2014; so anyone can pass in whatever component props they need.</p><p>Currently, we represent our component props with the definition <code>Props&lt;C&gt;</code>. Where <code>C</code> represents the element type passed for the <code>as</code> prop.</p><p>We will now change that to:</p><pre><code class="language-ts">// before
Props&lt;C&gt;

// after 
PolymorphicProps&lt;C, TextProps&gt;
</code></pre><p><code>PolymorphicProps</code> represents the utility type we will write shortly. However, note that this accepts two generic types. The second being the component props in question, i.e., <code>TextProps</code>.</p><p>Let&#x2019;s go ahead and define the <code>PolymorphicProps</code> type:</p><pre><code class="language-ts">type PolymorphicComponentProp&lt;
  C extends React.ElementType,
  Props = {}
&gt; = {} // &#x1F448; empty object for now 
</code></pre><p>The definition above should be understandable. <code>C</code> represents the element type passed in <code>as</code> and <code>Props</code> the actual component props, e.g., <code>TextProps</code>.</p><p>Before going ahead, let&#x2019;s go ahead and actually split the <code>TextProps</code> we had before into the following:</p><pre><code class="language-ts">type AsProp&lt;C extends React.ElementType&gt; = {
  as?: C;
};

type TextProps = { color?: Rainbow | &quot;black&quot; };
</code></pre><p>So, we&#x2019;ve separated the <code>AsProp</code> from the <code>TextProps</code>. To be fair, they represent two different things. This is a nicer representation.</p><p>Now, let&#x2019;s change the <code>PolymorphicComponentProp</code> utility definition to include the <code>as</code> prop, component props and <code>children</code> prop as we&#x2019;ve done in the past:</p><pre><code class="language-ts">type AsProp&lt;C extends React.ElementType&gt; = {
  as?: C;
};

type PolymorphicComponentProp&lt;
  C extends React.ElementType,
  Props = {}
&gt; = React.PropsWithChildren&lt;Props &amp; AsProp&lt;C&gt;&gt;
</code></pre><p>I&#x2019;m sure you understand what&#x2019;s going on here.</p><p>We now have an intersection type of <code>Props</code> (representing the component props) and <code>AsProp</code> representing the <code>as</code> prop, and these all passed into <code>PropsWithChildren</code> to add the <code>children</code> prop definition.</p><p>Excellent!</p><p>Now, we need to include the bit where we add the <code>React.ComponentPropsWithoutRef&lt;C&gt;</code> definition. However, we must remember to omit props that exist in our component definition.</p><p>Let&#x2019;s come up with a robust solution.</p><p>Write out a new type that just comprises the props we&#x2019;d like to omit. Namely, the keys of the <code>AsProp</code> and the component props as well.</p><pre><code class="language-ts">type PropsToOmit&lt;C extends React.ElementType, P&gt; = keyof (AsProp&lt;C&gt; &amp; P);
</code></pre><p>Remember the <code>keyof</code> utility type?</p><p><code>PropsToOmit</code> will now comprise a union type of the props we want to omit, which is, every prop of our component represented by <code>P</code> and the actual polymorphic prop <code>as</code> represented by <code>AsProps</code></p><p>I&#x2019;m glad you&#x2019;re still following.</p><p>Now, let&#x2019;s put this all together nicely in the <code>PolymorphicComponentProp</code> definition:</p><pre><code class="language-ts">type AsProp&lt;C extends React.ElementType&gt; = {
  as?: C;
};

// before 
type PolymorphicComponentProp&lt;
  C extends React.ElementType,
  Props = {}
&gt; = React.PropsWithChildren&lt;Props &amp; AsProp&lt;C&gt;&gt;

// after
type PolymorphicComponentProp&lt;
  C extends React.ElementType,
  Props = {}
&gt; = React.PropsWithChildren&lt;Props &amp; AsProp&lt;C&gt;&gt; &amp;
  Omit&lt;React.ComponentPropsWithoutRef&lt;C&gt;, 
   PropsToOmit&lt;C, Props&gt;&gt;;
</code></pre><p>What&#x2019;s important here is we&#x2019;ve added the following definition:</p><pre><code class="language-ts">Omit&lt;React.ComponentPropsWithoutRef&lt;C&gt;, 
   PropsToOmit&lt;C, Props&gt;&gt;;
</code></pre><p>This basically omits the right types from <code>React.componentPropsWithoutRef</code>. Do you still remember how <a href="https://www.typescriptlang.org/docs/handbook/utility-types.html#omittype-keys">Omit</a> works?</p><p>Simple as it may seem, you now have a solution you can reuse on multiple components across different projects!</p><p>Here&#x2019;s the complete implementation:</p><pre><code class="language-ts">type PropsToOmit&lt;C extends React.ElementType, P&gt; = keyof (AsProp&lt;C&gt; &amp; P);

type PolymorphicComponentProp&lt;
  C extends React.ElementType,
  Props = {}
&gt; = React.PropsWithChildren&lt;Props &amp; AsProp&lt;C&gt;&gt; &amp;
  Omit&lt;React.ComponentPropsWithoutRef&lt;C&gt;, PropsToOmit&lt;C, Props&gt;&gt;;
</code></pre><p>Now we can go ahead and use <code>PolymorphicComponentProp</code> on our <code>Text</code> component as follows:</p><pre><code class="language-ts">
export const Text = &lt;C extends React.ElementType = &quot;span&quot;&gt;({
  as,
  color,
  children,
  // look here &#x1F447;
}: PolymorphicComponentProp&lt;C, TextProps&gt;) =&gt; {
  const Component = as || &quot;span&quot;;
  const style = color ? { style: { color } } : {};
  return &lt;Component {...style}&gt;{children}&lt;/Component&gt;;
};
</code></pre><p>How nice!</p><p>Now if you build another component, you can go ahead and type it like this:</p><pre><code class="language-ts">PolymorphicComponentProp&lt;C, MyNewComponentProps&gt;
</code></pre><p>Do you hear that sound? That&#x2019;s the sound of victory &#x2014; you&#x2019;ve come so far!</p><h2 id="the-component-should-support-refs">The component should support refs</h2><p>Do you remember every reference to <code>React.ComponentPropsWithoutRef</code> so far? &#x1F605;</p><p>Component props &#x2026;. <em>without</em> ref. Well, now&#x2019;s the time to put the ref in it!</p><p>This is the final and most complex part of our solution. I&#x2019;ll need you to be patient here, but I&#x2019;ll also do my best to explain every step in detail.</p><p>Let&#x2019;s delve in.</p><p>First things first, do you remember how <code>refs</code> in React work?</p><p>The most important concept here is the fact that you just don&#x2019;t pass <code>ref</code> as a prop and expect it to be passed down into your component like every other prop.</p><p>The recommended way to handle <code>refs</code> in your functional components is to use the <code>forwardRef</code> function.</p><p>Let&#x2019;s start off on a practical note.</p><p>If you go ahead and pass a <code>ref</code> to our <code>Text</code> component now, you&#x2019;ll get an error that reads <code>Property &apos;ref&apos; does not exist on type ...</code></p><pre><code class="language-ts">// Create the ref object 
const divRef = useRef&lt;HTMLDivElement | null&gt;(null);
... 
// Pass the ref to the rendered Text component
&lt;Text as=&quot;div&quot; ref={divRef}&gt;
  Hello Text world
&lt;/Text&gt;
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/v9Ko7hW.jpg" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>Property ref does not exist error</figcaption></figure><p>This is expected.</p><p>Our first shot at supporting refs will be to use <code>forwardRef</code> in the <code>Text</code> component as shown below:</p><pre><code class="language-ts">// before 
export const Text = &lt;C extends React.ElementType = &quot;span&quot;&gt;({
  as,
  color,
  children,
}: PolymorphicComponentProp&lt;C, TextProps&gt;) =&gt; {
  ...
};


// after
import React from &quot;react&quot;;

export const Text = React.forwardRef(
  &lt;C extends React.ElementType = &quot;span&quot;&gt;({
    as,
    color,
    children,
  }: PolymorphicComponentProp&lt;C, TextProps&gt;) =&gt; {
    ...
  }
);
</code></pre><p>This is essentially just wrapping the previous code in <code>React.forwardRef</code>, that&#x2019;s all.</p><p>Now, <code>React.forwardRef</code> has the following signature:</p><pre><code class="language-ts">React.forwardRef((props, ref) ... )
</code></pre><p>Essentially, the second argument received is the <code>ref</code> object.</p><p>Let&#x2019;s go ahead and handle that:</p><pre><code class="language-ts">type PolymorphicRef&lt;C extends React.ElementType&gt; = unknown;

export const Text = React.forwardRef(
  &lt;C extends React.ElementType = &quot;span&quot;&gt;(
    { as, color, children }: PolymorphicComponentProp&lt;C, TextProps&gt;,
    // &#x1F447; look here
    ref?: PolymorphicRef&lt;C&gt;
  ) =&gt; {
    ...
  }
);
</code></pre><p>What we&#x2019;ve done here is added the second argument <code>ref</code> and declared its type as <code>PolymorphicRef</code>.</p><p>A type that just points to <code>unknown</code> for now.</p><p>Also note that <code>PolymorphicRef</code> takes in the generic <code>C</code>. This is similar to previous solutions. The <code>ref</code> object for a <code>div</code> differs to that of a <code>span</code>. So, we need to take into consideration the element type passed in the <code>as</code> prop.</p><p>Let&#x2019;s now point our attention to the <code>PolymorphicRef</code> type.</p><p>I need you to think with me.</p><p>How can we get the <code>ref</code> object type based on the <code>as</code> prop?</p><p>Let me give you a clue: <code>React.ComponentPropsWithRef</code> !</p><p>Note that this says <em>with</em> ref. Not <em>without</em> ref.</p><p>Essentially, if this were a bundle of keys (which in fact it is), it&#x2019;ll include all the relevant component props based on the element type, <em>plus</em> the ref object.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/lTSL6dF.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>The ComponentPropsWithRef type</figcaption></figure><p>So now, if we know this object type contains the <code>ref</code> key, we may as well get that ref type by doing the following:</p><pre><code class="language-ts">// before 
type PolymorphicRef&lt;C extends React.ElementType&gt; = unknown;

// after 
type PolymorphicRef&lt;C extends React.ElementType&gt; =
  React.ComponentPropsWithRef&lt;C&gt;[&quot;ref&quot;];
</code></pre><p>Essentially, <code>React.ComponentPropsWithRef&lt;C&gt;</code> returns an object type, e.g.,</p><pre><code class="language-ts">{
  ref: SomeRefDefinition, 
  // ... other keys, 
  color: string 
  href: string 
  // ... etc
}
</code></pre><p>To pick out just the <code>ref</code> type, we then do this:</p><pre><code class="language-ts">React.ComponentPropsWithRef&lt;C&gt;[&quot;ref&quot;];
</code></pre><p>Note that the syntax is similar to the property accessor syntax in javascript, i.e., <code>[&quot;ref&quot;]</code>. In Typescript, we call this is called Type indexing.</p><blockquote><strong>Quick quiz</strong>: Do you know why using &#x201C;Pick&#x201D; may not work well here, e.g., <code>Pick&lt;React.ComponentPropsWithRef&lt;C&gt;, &quot;ref&quot;&gt;</code>? <br>You may use the comment section or <a href="https://twitter.com/ohansemmanuel">tweet me</a> your answers.</blockquote><p>Now that we&#x2019;ve got the <code>ref</code> prop typed, we can go ahead and pass that down to the rendered element:</p><pre><code class="language-ts">export const Text = React.forwardRef(
  &lt;C extends React.ElementType = &quot;span&quot;&gt;(
    { as, color, children }: PolymorphicComponentProp&lt;C, TextProps&gt;,
    ref?: PolymorphicRef&lt;C&gt;
  ) =&gt; {
    //...
    
    return (
      &lt;Component {...style} ref={ref}&gt; // &#x1F448; look here
        {children}
      &lt;/Component&gt;
    );
  }
);
</code></pre><p>We&#x2019;ve made decent progress! In fact, if you go ahead and check the usage of <code>Text</code> like we did before, there&#x2019;ll be no more errors:</p><pre><code class="language-ts">// create the ref object 
const divRef = useRef&lt;HTMLDivElement | null&gt;(null);
... 
// pass ref to the rendered Text component
&lt;Text as=&quot;div&quot; ref={divRef}&gt;
  Hello Text world
&lt;/Text&gt;
</code></pre><p>However, our solution still isn&#x2019;t as strongly typed as I&#x2019;d like.</p><p>Let&#x2019;s go ahead and change the ref passed to the <code>Text</code> as shown below:</p><pre><code class="language-ts">// create a &quot;button&quot; ref object 
const buttonRef = useRef&lt;HTMLButtonElement | null&gt;(null);
... 
// pass a button ref to a &quot;div&quot;. NB: as = &quot;div&quot;
&lt;Text as=&quot;div&quot; ref={buttonRef}&gt;
  Hello Text world
&lt;/Text&gt;
</code></pre><p>Typescript should throw an error here, but it doesn&#x2019;t. We&#x2019;re creating a &#x201C;button&#x201D; ref, but passing that to a <code>div</code> element.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.ohansemmanuel.com/content/images/2022/06/LzRouWU.png" class="kg-image" alt="Build Strongly-Typed Polymorphic Components with React and Typescript" loading="lazy"><figcaption>No error thrown when a wrong element ref is passed</figcaption></figure><p>If you take a look at the exact type, <code>ref</code> it looks like this:</p><pre><code class="language-ts">React.RefAttributes&lt;unknown&gt;.ref?: React.Ref&lt;unknown&gt;
</code></pre><p>Do you see the <code>unknown</code> in there? That&#x2019;s the sign of a weak typing. We should ideally have <code>HTMLDivElement</code> in there, i.e., explicitly defining the ref object as a <code>div</code> element ref.</p><p>We&#x2019;ve got work to do.</p><p>Firstly, the types for the other props of the <code>Text</code> component still reference the <code>PolymorphicComponentProp</code> type.</p><p>Let&#x2019;s change this to a new type called <code>PolymorphicComponentPropWithRef</code>. You guessed right. This will just be a union of <code>PolymorphicComponentProp</code> and the ref prop.</p><p>Here it is:</p><pre><code class="language-ts">type PolymorphicComponentPropWithRef&lt;
  C extends React.ElementType,
  Props = {}
&gt; = PolymorphicComponentProp&lt;C, Props&gt; &amp; 
{ ref?: PolymorphicRef&lt;C&gt; };
</code></pre><p>Note that this is just a union of the previous <code>PolymorphicComponentProp</code> and <code>{ ref?: PolymorphicRef&lt;C&gt; }</code></p><p>Now we need to change the props of the component to reference the new<code>PolymorphicComponentPropWithRef</code> type:</p><pre><code class="language-ts">// before
type TextProps = { color?: Rainbow | &quot;black&quot; };

export const Text = React.forwardRef(
  &lt;C extends React.ElementType = &quot;span&quot;&gt;(
    { as, color, children }: PolymorphicComponentProp&lt;C, TextProps&gt;,
    ref?: PolymorphicRef&lt;C&gt;
  ) =&gt; {
    ...
  }
);


// now 
type TextProps&lt;C extends React.ElementType&gt; = 
PolymorphicComponentPropWithRef&lt;
  C,
  { color?: Rainbow | &quot;black&quot; }
&gt;;

export const Text = React.forwardRef(
  &lt;C extends React.ElementType = &quot;span&quot;&gt;(
    { as, color, children }: TextProps&lt;C&gt;, // &#x1F448; look here
    ref?: PolymorphicRef&lt;C&gt;
  ) =&gt; {
    ...
  }
);
</code></pre><p>Now, we&#x2019;ve updated <code>TextProps</code> to reference <code>PolymorphicComponentPropWithRef</code> and that&#x2019;s now passed as the props for the <code>Text</code> component.</p><p>Lovely!</p><p>There&#x2019;s solely one final thing to do now. We will provide a type annotation for the <code>Text</code> component. It goes similar to:</p><pre><code class="language-ts">export const Text : TextComponent = ...
</code></pre><p>Where <code>TextComponent</code> is the type annotation we&#x2019;ll write. Here it is:</p><pre><code class="language-ts">type TextComponent = &lt;C extends React.ElementType = &quot;span&quot;&gt;(
  props: TextProps&lt;C&gt;
) =&gt; React.ReactElement | null;
</code></pre><p>This is essentially a functional component that takes in <code>TextProps</code> and returns <code>React.ReactElement | null</code></p><p>Where <code>TextProps</code> is as defined earlier:</p><pre><code class="language-ts">type TextProps&lt;C extends React.ElementType&gt; = 
PolymorphicComponentPropWithRef&lt;
  C,
  { color?: Rainbow | &quot;black&quot; }
&gt;;
</code></pre><p>With this, we now have a complete solution! I&#x2019;m going to share the complete solution now. It may seem daunting at first, but remember we&#x2019;ve worked line by line through everything you see here. Read it with that confidence.</p><pre><code class="language-ts">import React from &quot;react&quot;;

type Rainbow =
  | &quot;red&quot;
  | &quot;orange&quot;
  | &quot;yellow&quot;
  | &quot;green&quot;
  | &quot;blue&quot;
  | &quot;indigo&quot;
  | &quot;violet&quot;;

type AsProp&lt;C extends React.ElementType&gt; = {
  as?: C;
};

type PropsToOmit&lt;C extends React.ElementType, P&gt; = keyof (AsProp&lt;C&gt; &amp; P);

// This is the first reusable type utility we built
type PolymorphicComponentProp&lt;
  C extends React.ElementType,
  Props = {}
&gt; = React.PropsWithChildren&lt;Props &amp; AsProp&lt;C&gt;&gt; &amp;
  Omit&lt;React.ComponentPropsWithoutRef&lt;C&gt;, PropsToOmit&lt;C, Props&gt;&gt;;

// This is a new type utitlity with ref!
type PolymorphicComponentPropWithRef&lt;
  C extends React.ElementType,
  Props = {}
&gt; = PolymorphicComponentProp&lt;C, Props&gt; &amp; { ref?: PolymorphicRef&lt;C&gt; };

// This is the type for the &quot;ref&quot; only
type PolymorphicRef&lt;C extends React.ElementType&gt; =
  React.ComponentPropsWithRef&lt;C&gt;[&quot;ref&quot;];

/**
* This is the updated component props using PolymorphicComponentPropWithRef
*/
type TextProps&lt;C extends React.ElementType&gt; = 
PolymorphicComponentPropWithRef&lt;
  C,
  { color?: Rainbow | &quot;black&quot; }
&gt;;

/**
* This is the type used in the type annotation for the component
*/
type TextComponent = &lt;C extends React.ElementType = &quot;span&quot;&gt;(
  props: TextProps&lt;C&gt;
) =&gt; React.ReactElement | null;

export const Text: TextComponent = React.forwardRef(
  &lt;C extends React.ElementType = &quot;span&quot;&gt;(
    { as, color, children }: TextProps&lt;C&gt;,
    ref?: PolymorphicRef&lt;C&gt;
  ) =&gt; {
    const Component = as || &quot;span&quot;;

    const style = color ? { style: { color } } : {};

    return (
      &lt;Component {...style} ref={ref}&gt;
        {children}
      &lt;/Component&gt;
    );
  }
);
</code></pre><p>And there you go!</p><p>You have successfully built a robust solution for handling Polymorphic components in React.</p><p>I know it wasn&#x2019;t an easy ride, but you did it.</p><h2 id="conclusion-and-next-steps">Conclusion and Next Steps</h2><p>Thanks for following along. Remember to star the official <a href="https://github.com/ohansemmanuel/polymorphic-react-component">GitHub repository</a>, where you&#x2019;ll find all the code for this guide.</p>]]></content:encoded></item></channel></rss>