<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Shane Osbourne</title>
        <link>https://shane-o.dev</link>
        <description>Software Engineering</description>
        <lastBuildDate>Thu, 16 Apr 2026 13:25:14 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Shane Osbourne</title>
            <url>https://shane-o.dev/favicon.ico</url>
            <link>https://shane-o.dev</link>
        </image>
        <copyright>All rights reserved 2026</copyright>
        <item>
            <title><![CDATA[At least 7 reasons to avoid @ts-expect-error and @ts-ignore]]></title>
            <link>https://shane-o.dev/articles/any-or-expect</link>
            <guid>https://shane-o.dev/articles/any-or-expect</guid>
            <pubDate>Sat, 18 Nov 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h2>After a deep dive, I now consider both to be dangerous.</h2>
<p>Picture the scene. You've run into a type-level
problem in your application, and you just want to suppress the error for now and come back later to resolve it. You
know (via tests) that the value works, you just need to please Typescript temporarily so you can move forward...</p>
<div data-mdx-next="pre" data-2up="left" data-error="true"></div>
<pre class="language-tsx"><code class="language-tsx code-highlight"><span class="code-line line-number" line="1"><span class="token keyword">interface</span> <span class="token class-name">Args</span> <span class="token punctuation">{</span>
</span><span class="code-line line-number" line="2">  selector<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
</span><span class="code-line line-number" line="3">  element<span class="token operator">:</span> <span class="token maybe-class-name">HTMLElement</span>
</span><span class="code-line line-number" line="4"><span class="token punctuation">}</span>
</span><span class="code-line line-number" line="5">
</span><span class="code-line line-number" line="6"><span class="token keyword">function</span> <span class="token function">matches</span><span class="token punctuation">(</span><span class="token punctuation">{</span>selector<span class="token punctuation">,</span> element<span class="token punctuation">}</span><span class="token operator">:</span> <span class="token maybe-class-name">Args</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
</span><span class="code-line line-number" line="7">
</span><span class="code-line line-number" line="8"><span class="token punctuation">}</span>
</span><span class="code-line line-number" line="9">
</span><span class="code-line line-number" line="10"><span class="token function">matches</span><span class="token punctuation">(</span><span class="token punctuation">{</span> selector<span class="token operator">:</span> <span class="token number">120</span><span class="token punctuation">,</span> element<span class="token operator">:</span> <span class="token dom variable">document</span><span class="token punctuation">.</span><span class="token property-access">body</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
</span><span class="code-line line-number highlight-line" line="11">                    <span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span>
</span><span class="code-line line-number highlight-line" line="12"><span class="token maybe-class-name">Type</span> &nbsp;<span class="token builtin">number</span>&nbsp; <span class="token keyword">is</span> not assignable to <span class="token keyword">type</span> &nbsp;<span class="token class-name"><span class="token builtin">string</span></span>&nbsp;
</span></code></pre>
<ul>
<li>✅ Starting with a <em>correct</em> example - this <em>should</em> be a Typescript error. The property <code>selector</code> of <code>Args</code> has
the type <code>string</code>, but we're passing a <code>number</code>. Nothing too interesting here, but we'd like to silence that error somehow.</li>
</ul>
<h2>Line-level error silencing <code>@ts-expect-error</code> / <code>@ts-ignore</code></h2>
<p>A common technique is to throw a quick <code>@ts-expect-error</code> down, and then annotate the
comment with a link to a ticketing system. The idea is that it's a searchable thing in the codebase and will be worked
on when time allows. In the meantime it provides a single place for others to read up on the reasoning.</p>
<blockquote>
<p>For the remainder of this post I'll refer to <code>@ts-expect-error</code> and <code>@ts-ignore</code> interchangeably. For what I'm describing
they are equal.</p>
</blockquote>
<pre class="language-tsx"><code class="language-diff-tsx code-highlight"><span class="code-line line-number" line="1"><span class="token keyword">function</span> <span class="token function">matches</span><span class="token punctuation">(</span><span class="token punctuation">{</span>selector<span class="token punctuation">,</span> element<span class="token punctuation">}</span><span class="token operator">:</span> <span class="token punctuation">{</span> selector<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> element<span class="token operator">:</span> <span class="token maybe-class-name">HTMLElement</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
</span><span class="code-line line-number" line="2">
</span><span class="code-line line-number" line="3"><span class="token punctuation">}</span>
</span><span class="code-line line-number" line="4">
</span><span class="code-line line-number highlight-line" line="5"><span class="token comment">// @ts-expect-error - https://example.com/ticket/123</span>
</span><span class="code-line line-number" line="6"><span class="token function">matches</span><span class="token punctuation">(</span><span class="token punctuation">{</span> selector<span class="token operator">:</span> <span class="token number">120</span><span class="token punctuation">,</span> element<span class="token operator">:</span> <span class="token dom variable">document</span><span class="token punctuation">.</span><span class="token property-access">body</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
</span></code></pre>
<ul>
<li>This silences the error from the subsequent line, and we're linking to a 'cleanup' task that the next developer can view.</li>
</ul>
<p>On the surface, this looks good, it seems like this feature from Typescript is specifically built for this scenario. After
all, libraries change, Typescript upgrades, your app evolves... sometimes we just need to silence an error and move on.</p>
<p>But, is this really the best way to do that?</p>
<h2>Exploring the gotchas</h2>
<p>So, what can go wrong with a seemingly harmless <code>@ts-expect-error</code>?</p>
<p>The following is nowhere near an exhaustive list, it's just 7 individual examples I could think of, all based around the
single topic of calling a function.</p>
<blockquote>
<p>BTW - if I was able to find this many, on a single Javascript construct (a function call), just
imagine how many <em>actual</em> cases there would be in any modern Typescript codebase 🤯</p>
</blockquote>
<h2><strong>1️⃣ A 'typo' on the property name is allowed</strong> 😭</h2>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token comment">// @ts-expect-error - https://example.com/ticket/123</span>
</span><span class="code-line"><span class="token function">matches</span><span class="token punctuation">(</span><span class="token punctuation">{</span> selectar<span class="token operator">:</span> <span class="token number">120</span><span class="token punctuation">,</span> element<span class="token operator">:</span> document<span class="token punctuation">.</span>body <span class="token punctuation">}</span><span class="token punctuation">)</span>
</span></code></pre>
<ul>
<li>❌ OOPS! we made a typo, <code>selectar</code> instead of <code>selector</code> (did you spot it? 👀)</li>
<li>Now Typescript will allow this mistake, because of the line-level <code>@ts-expect-error</code></li>
<li>😭 We only wanted to ignore the <em>type</em> of the <code>selector</code> field, not the field <strong>name</strong> itself!</li>
</ul>
<h2><strong>2️⃣ Type-checking on other properties is impacted</strong> 😭</h2>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token comment">// @ts-expect-error - https://example.com/ticket/123</span>
</span><span class="code-line"><span class="token function">matches</span><span class="token punctuation">(</span><span class="token punctuation">{</span> selector<span class="token operator">:</span> <span class="token number">120</span><span class="token punctuation">,</span> element<span class="token operator">:</span> <span class="token string">'lol!'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
</span></code></pre>
<ul>
<li>❌ OOPS! <code>element</code> should have the type <code>HTMLElement</code>, but we passed a <code>string</code> by mistake!</li>
<li>😭 We only wanted to ignore value of the <code>selector</code> field, not the <em>types</em> other properties!</li>
</ul>
<h2><strong>3️⃣ Typos on other property names are allowed</strong> 😭</h2>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token comment">// @ts-expect-error</span>
</span><span class="code-line"><span class="token function">matches</span><span class="token punctuation">(</span><span class="token punctuation">{</span> selector<span class="token operator">:</span> <span class="token number">120</span><span class="token punctuation">,</span> elemant<span class="token operator">:</span> document<span class="token punctuation">.</span>body <span class="token punctuation">}</span><span class="token punctuation">)</span>
</span></code></pre>
<ul>
<li>❌ OOPS! another typo, this time we spelt <code>elemant</code> where it should be <code>element</code>.</li>
<li>Even though the type is correct in this instance, <code>HTMLElement</code>, it can still cause runtime bugs since the name is now wrong!</li>
</ul>
<h2><strong>4️⃣ Absolutely anything will be allowed as a parameter</strong> 😭</h2>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token comment">// @ts-expect-error</span>
</span><span class="code-line"><span class="token function">matches</span><span class="token punctuation">(</span><span class="token number">Infinity</span><span class="token punctuation">)</span>
</span></code></pre>
<ul>
<li>❌ OOPS! this is wrong on so many levels - the line-level <code>@ts-expect-error</code> is effectively switching off the
type-checker, risking all sorts of runtime bugs.</li>
</ul>
<h2>Localised <code>@ts-expect-error</code></h2>
<p>Hopefully you're sufficiently convinced already about the dangers of <code>@ts-expect-error</code> when applied to entire lines of code.</p>
<p>I only gave a tiny example of a really simple function, with a couple of ways of calling it. With that alone we were already
able to identify 4 places where unrelated bugs could creep in.</p>
<p>So, you might be wondering if a more localised application of the technique could work?</p>
<p>The first thing I tried was breaking object properties and function parameters onto separate lines. It seemed like a nice middle ground.</p>
<p>I was very wrong.</p>
<p>As a refresher, this is correct behaviour</p>
<div data-mdx-next="pre" data-error="true"></div>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token keyword">interface</span> <span class="token class-name">Args</span> <span class="token punctuation">{</span>
</span><span class="code-line">  selector<span class="token operator">:</span> <span class="token builtin">string</span>
</span><span class="code-line">  element<span class="token operator">:</span> HTMLElement
</span><span class="code-line"><span class="token punctuation">}</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token keyword">function</span> <span class="token function">matches</span><span class="token punctuation">(</span><span class="token punctuation">{</span> selector<span class="token punctuation">,</span> element <span class="token punctuation">}</span><span class="token operator">:</span> Args<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token function">matches</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
</span><span class="code-line">  selector<span class="token operator">:</span> <span class="token number">120</span><span class="token punctuation">,</span>
</span><span class="code-line highlight-line">            <span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span>
</span><span class="code-line highlight-line">Argument <span class="token keyword">of</span> <span class="token keyword">type</span> <span class="token string">"number"</span> <span class="token keyword">is</span> not assignable to parameter <span class="token keyword">of</span> <span class="token keyword">type</span> <span class="token string">"string"</span>
</span><span class="code-line">  element<span class="token operator">:</span> document<span class="token punctuation">.</span>body<span class="token punctuation">,</span>
</span><span class="code-line"><span class="token punctuation">}</span><span class="token punctuation">)</span>
</span></code></pre>
<ul>
<li>We're still calling the function with an object that has 2 properties, but now we've split it over 2 separate lines.</li>
<li>✅ <code>120</code> has the type <code>number</code>, where we need a <code>string</code>, so Typescript is giving a helpful error. All good.</li>
</ul>
<p>But...</p>
<h2><strong>5️⃣ It still allows a typo on the property name</strong> 😭</h2>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token function">matches</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
</span><span class="code-line">  <span class="token comment">// @ts-expect-error</span>
</span><span class="code-line">  selectar<span class="token operator">:</span> <span class="token number">120</span><span class="token punctuation">,</span>
</span><span class="code-line">  element<span class="token operator">:</span> document<span class="token punctuation">.</span>body<span class="token punctuation">,</span>
</span><span class="code-line"><span class="token punctuation">}</span><span class="token punctuation">)</span>
</span></code></pre>
<ul>
<li>❌ OOPS! As before, we only wanted to ignore the <code>type</code> of the <code>selector</code> field, but we've expanded the blast-radius
and now a simple typo on the property name will be ignored 😭</li>
</ul>
<h2><strong>6️⃣ You can't apply it to an object value only</strong> 😭</h2>
<p>It would be nice, if you could go one step further and have <code>@ts-expect-error</code> apply
to a value only, wouldn't it...</p>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token function">matches</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
</span><span class="code-line">  selector<span class="token operator">:</span>
</span><span class="code-line">    <span class="token comment">// @ts-expect-error</span>
</span><span class="code-line">    <span class="token number">120</span><span class="token punctuation">,</span>
</span><span class="code-line">  element<span class="token operator">:</span> document<span class="token punctuation">.</span>body<span class="token punctuation">,</span>
</span><span class="code-line"><span class="token punctuation">}</span><span class="token punctuation">)</span>
</span></code></pre>
<ul>
<li>Now we've split the key, <code>selector</code>, and it's value, <code>120</code>, onto separate lines, in an attempt to silence the error at
the value level only.</li>
<li>This doesn't work though 😭. The value <code>120</code> in isolation can never be 'invalid', it's only when considered as
an assignable types to the <code>selector</code> field that it can be incorrect.</li>
<li>We do get a Typescript error here, but just to tell us that the directive is unused, which brings yet more confusion.</li>
</ul>
<div data-mdx-next="pre" data-2up="left" data-error="true"></div>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token function">matches</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
</span><span class="code-line">  selector<span class="token operator">:</span>
</span><span class="code-line">    <span class="token comment">// @ts-expect-error</span>
</span><span class="code-line highlight-line">    Unused <span class="token string">'@ts-expect-error'</span> directive<span class="token punctuation">.</span>
</span><span class="code-line">    <span class="token number">120</span><span class="token punctuation">,</span>
</span><span class="code-line">  element<span class="token operator">:</span> document<span class="token punctuation">.</span>body<span class="token punctuation">,</span>
</span><span class="code-line"><span class="token punctuation">}</span><span class="token punctuation">)</span>
</span></code></pre>
<h2><strong>7️⃣ It affects subsequent parameters</strong> 😭</h2>
<p>So far we've focussed on the impact <code>@ts-expect-error</code> or <code>@ts-ignore</code> can have on a single, object parameter. As if those
examples were not enough, it gets even worse when we look at functions that take more than 1 argument.</p>
<p>Let's take a look at a 'correct' example first:</p>
<div data-mdx-next="pre" data-2up="left" data-error="true"></div>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token keyword">function</span> <span class="token function">matchesV2</span><span class="token punctuation">(</span>selector<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> kind<span class="token operator">:</span> <span class="token string">'css'</span> <span class="token operator">|</span> <span class="token string">'xpath'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token punctuation">}</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token function">matchesV2</span><span class="token punctuation">(</span><span class="token number">120</span><span class="token punctuation">,</span> <span class="token string">'css'</span><span class="token punctuation">)</span>
</span><span class="code-line highlight-line">          <span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span>
</span><span class="code-line highlight-line">Argument <span class="token keyword">of</span> <span class="token keyword">type</span> &nbsp;<span class="token class-name"><span class="token builtin">number</span></span>&nbsp; <span class="token keyword">is</span> not assignable to parameter <span class="token keyword">of</span> <span class="token keyword">type</span> &nbsp;<span class="token class-name"><span class="token builtin">string</span></span>
</span></code></pre>
<ul>
<li>✅ Now we're calling a function that accepts 2 parameters - and because the first has the type <code>string</code>, Typescript
is correctly giving us the error we expect when we try to provide <code>120</code>.</li>
</ul>
<p>So, now consider trying to 'silence' this error as before</p>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token function">matchesV2</span><span class="token punctuation">(</span>
</span><span class="code-line">  <span class="token comment">// @ts-expect-error</span>
</span><span class="code-line">  <span class="token number">120</span><span class="token punctuation">,</span>
</span><span class="code-line">  <span class="token string">'oops!'</span><span class="token punctuation">,</span>
</span><span class="code-line"><span class="token punctuation">)</span>
</span></code></pre>
<ul>
<li>At first, it looks ok. We want <code>@ts-expect-error</code> to only apply to the very next parameter - silencing
the error relating to giving <code>120</code> when a <code>string</code> was expected</li>
<li>But, ❌ OOPS! we've accidentally affected the second parameter too!<!-- -->
<ul>
<li><code>kind</code>, in the function signature, denotes a union type with 2 members, the literal types
<code>"css"</code> and <code>"xpath"</code>, but we're able to provide <code>"oops!"</code>.</li>
</ul>
</li>
<li>This is terrible! Having a single <code>@ts-expect-error</code> on just 1 of the parameters in the function call is
having an impact on subsequent parameters too!</li>
</ul>
<p>This gets even more confusing/error-prone when you consider that preceding parameters in this style <em>would</em> be type-checked.</p>
<p>Consider another example, this time with 3 arguments.</p>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line line-number" line="1"><span class="token keyword">function</span> <span class="token function">matchesV3</span><span class="token punctuation">(</span>input<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> selector<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> kind<span class="token operator">:</span> <span class="token string">'css'</span> <span class="token operator">|</span> <span class="token string">'xpath'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
</span><span class="code-line line-number" line="2">
</span><span class="code-line line-number" line="3"><span class="token function">matchesV3</span><span class="token punctuation">(</span>
</span><span class="code-line line-number" line="4">  <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span>
</span><span class="code-line line-number" line="5">  <span class="token comment">// @ts-expect-error</span>
</span><span class="code-line line-number" line="6">  <span class="token number">120</span><span class="token punctuation">,</span>
</span><span class="code-line line-number" line="7">  <span class="token string">'oops!'</span><span class="token punctuation">,</span>
</span><span class="code-line line-number" line="8"><span class="token punctuation">)</span>
</span></code></pre>
<ul>
<li>Notice how all 3 of the parameters in that function call are incorrect.</li>
<li>line <code>4</code>, has the type <code>{}</code>, where a string is expected. Typescript <em>will</em> check this ✅</li>
<li>line <code>5</code>, our usual example, is marked <code>@ts-expect-error</code>, Typescript will <em>allow</em> this, as expected ✅</li>
<li>line <code>6</code>, ❌ OOPS! Again the parameter given for <code>kind</code> should only be one of <code>"css"</code> or <code>"xpath"</code>, but <code>"oops!"</code> is allowed 😭</li>
</ul>
<h2>What to do instead</h2>
<p>To keep it brief, cast to <code>any</code> and move on - but do so in the most localised way possible.</p>
<p><strong>❌ Before, line-level <code>@ts-expect-error</code></strong></p>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token comment">// @ts-expect-error - fixme: example.com/ticket/123</span>
</span><span class="code-line"><span class="token function">matches</span><span class="token punctuation">(</span><span class="token punctuation">{</span> selector<span class="token operator">:</span> <span class="token number">120</span><span class="token punctuation">,</span> element<span class="token operator">:</span> document<span class="token punctuation">.</span>body <span class="token punctuation">}</span><span class="token punctuation">)</span>
</span></code></pre>
<p><strong>✅ After, localised cast to <code>any</code> in Typescript</strong></p>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token function">matches</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
</span><span class="code-line">  selector<span class="token operator">:</span> <span class="token number">120</span> <span class="token keyword">as</span> <span class="token builtin">any</span> <span class="token comment">/** fixme: example.com/ticket/123 */</span><span class="token punctuation">,</span>
</span><span class="code-line">  element<span class="token operator">:</span> document<span class="token punctuation">.</span>body<span class="token punctuation">,</span>
</span><span class="code-line"><span class="token punctuation">}</span><span class="token punctuation">)</span>
</span></code></pre>
<p><strong>✅ After, localised cast to <code>any</code> in JSDoc</strong></p>
<p>Note the surrounding <code>()</code> parens on the value - this is a requirement.</p>
<pre class="language-js"><code class="language-js code-highlight"><span class="code-line"><span class="token function">matches</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
</span><span class="code-line">  <span class="token literal-property property">selector</span><span class="token operator">:</span> <span class="token doc-comment comment">/** <span class="token keyword">@type</span> <span class="token class-name"><span class="token punctuation">{</span>any<span class="token punctuation">}</span></span> fixme: example.com/ticket/123 */</span><span class="token punctuation">(</span><span class="token number">120</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
</span><span class="code-line">  <span class="token literal-property property">element</span><span class="token operator">:</span> <span class="token dom variable">document</span><span class="token punctuation">.</span><span class="token property-access">body</span>
</span><span class="code-line"><span class="token punctuation">}</span><span class="token punctuation">)</span>
</span></code></pre>
<p>If you find it messy having type assertions and comments inline like this, you can make it
even more explicit with a re-assignment + type declaration as a separate statement.</p>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token keyword">let</span> temp<span class="token operator">:</span> <span class="token builtin">any</span> <span class="token operator">=</span> <span class="token number">120</span> <span class="token comment">/** fixme: example.com/ticket/123 */</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token comment">// now call the function as before, without the noise</span>
</span><span class="code-line"><span class="token function">matches</span><span class="token punctuation">(</span><span class="token punctuation">{</span> selector<span class="token operator">:</span> temp<span class="token punctuation">,</span> element<span class="token operator">:</span> document<span class="token punctuation">.</span>body <span class="token punctuation">}</span><span class="token punctuation">)</span>
</span></code></pre>
<p>It will work with <code>as any</code> here too:</p>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token keyword">let</span> temp <span class="token operator">=</span> <span class="token number">120</span> <span class="token keyword">as</span> <span class="token builtin">any</span> <span class="token comment">/** fixme: example.com/ticket/123 */</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token comment">// now call the function as before, without the noise</span>
</span><span class="code-line"><span class="token function">matches</span><span class="token punctuation">(</span><span class="token punctuation">{</span> selector<span class="token operator">:</span> temp<span class="token punctuation">,</span> element<span class="token operator">:</span> document<span class="token punctuation">.</span>body <span class="token punctuation">}</span><span class="token punctuation">)</span>
</span></code></pre>
<p><strong>JSDoc</strong></p>
<pre class="language-js"><code class="language-js code-highlight"><span class="code-line"><span class="token keyword">let</span> temp <span class="token operator">=</span> <span class="token doc-comment comment">/** <span class="token keyword">@type</span> <span class="token class-name"><span class="token punctuation">{</span>any<span class="token punctuation">}</span></span> fixme: example.com/ticket/123 */</span><span class="token punctuation">(</span><span class="token number">120</span><span class="token punctuation">)</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token comment">// now call the function as before, without the noise</span>
</span><span class="code-line"><span class="token function">matches</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">selector</span><span class="token operator">:</span> temp<span class="token punctuation">,</span> <span class="token literal-property property">element</span><span class="token operator">:</span> <span class="token dom variable">document</span><span class="token punctuation">.</span><span class="token property-access">body</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
</span></code></pre>
<p>Works for multiple parameters too:</p>
<pre class="language-ts"><code class="language-diff-ts code-highlight"><span class="code-line"><span class="token keyword">function</span> <span class="token function">matchesV2</span><span class="token punctuation">(</span>selector<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> kind<span class="token operator">:</span> <span class="token string">'css'</span> <span class="token operator">|</span> <span class="token string">'xpath'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
</span><span class="code-line">
</span><span class="code-line deleted"><span class="token operator">-</span><span class="token comment">// @ts-expect-error - fixme: example.com/ticket/123</span>
</span><span class="code-line"><span class="token function">matchesV2</span><span class="token punctuation">(</span>
</span><span class="code-line deleted"><span class="token operator">-</span>  <span class="token number">120</span><span class="token punctuation">,</span>
</span><span class="code-line inserted"><span class="token operator">+</span>  <span class="token number">120</span> <span class="token keyword">as</span> <span class="token builtin">any</span> <span class="token comment">/** fixme: example.com/ticket/123 */</span><span class="token punctuation">,</span>
</span><span class="code-line">  <span class="token string">'css'</span>
</span><span class="code-line"><span class="token punctuation">)</span>
</span></code></pre>
<p><strong>JSDoc</strong></p>
<pre class="language-js"><code class="language-diff-js code-highlight"><span class="code-line"><span class="token keyword">function</span> <span class="token function">matchesV2</span><span class="token punctuation">(</span>selector<span class="token operator">:</span> string<span class="token punctuation">,</span> <span class="token literal-property property">kind</span><span class="token operator">:</span> <span class="token string">'css'</span> <span class="token operator">|</span> <span class="token string">'xpath'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token function">matchesV2</span><span class="token punctuation">(</span>
</span><span class="code-line deleted"><span class="token operator">-</span> <span class="token number">120</span><span class="token punctuation">,</span>
</span><span class="code-line inserted"><span class="token operator">+</span> <span class="token doc-comment comment">/** <span class="token keyword">@type</span> <span class="token class-name"><span class="token punctuation">{</span>any<span class="token punctuation">}</span></span> fixme: example.com/ticket/123 */</span><span class="token punctuation">(</span><span class="token number">120</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
</span><span class="code-line">  <span class="token string">'css'</span>
</span><span class="code-line"><span class="token punctuation">)</span>
</span></code></pre>
<p>All examples above share the same benefits - they all silence the Typescript errors in a
way that avoids unintended side effects 👌.</p>
<h2>Summary</h2>
<p><code>@ts-expect-error</code> was designed for test-case scenarios - where you are deliberately giving an incorrect type and what to ensure the
type-checker is aware of it. Use it for that, only.</p>
<p><code>@ts-ignore</code> does operate slightly differently in some of the examples given above - but not differently enough for me
to document it in this post. Regardless it's still absolutely full of odd behaviours and has unintended side effects, so I
highly discourage its use too.</p>
<p>Instead of these brute-force approaches, use localised casting instead.</p>
<p><code>@ts-expect-error</code> and <code>@ts-ignore</code>, in both regular Typescript and with JSDoc, should be seen as an absolute
last resort. You <strong>must</strong> have tried all other ways of casting values before ever resorting to them.</p>]]></content:encoded>
            <author>shane.osbourne@hey.com (Shane Osbourne)</author>
        </item>
        <item>
            <title><![CDATA[Destructure with care. The story of a Typescript gotcha]]></title>
            <link>https://shane-o.dev/articles/destructure-with-care</link>
            <guid>https://shane-o.dev/articles/destructure-with-care</guid>
            <pubDate>Thu, 02 Nov 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h2>A problem that even the strictest Typescript/ESLint settings cannot help with.</h2>
<p>The decision to <strong>inline</strong> types directly into method signatures vs extracting into an <strong>interface</strong> or <strong>type</strong> is a common discussion
point at the moment.</p>
<p>The <strong>inline</strong> vs <strong>interface</strong> vs <strong>type</strong> debate very much focuses on the RHS (right-hand-side) of the parameter definition - but today I'd like us
to consider a dangerous pattern on the <em>LHF</em> side instead.</p>
<p>First though, a quick refresher on those terms:</p>
<p><strong>Types inline</strong></p>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token keyword">function</span> <span class="token function">matches</span><span class="token punctuation">(</span><span class="token punctuation">{</span> element<span class="token punctuation">,</span> xpath <span class="token punctuation">}</span><span class="token operator">:</span> <span class="token punctuation">{</span> element<span class="token operator">:</span> HTMLElement<span class="token punctuation">,</span> xpath<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">boolean</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
</span></code></pre>
<p><strong>Separate Interface</strong></p>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token keyword">interface</span> <span class="token class-name">Props</span> <span class="token punctuation">{</span>
</span><span class="code-line">    element<span class="token operator">:</span> HTMLElement<span class="token punctuation">;</span>
</span><span class="code-line">    xpath<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span><span class="code-line"><span class="token keyword">function</span> <span class="token function">matches</span><span class="token punctuation">(</span><span class="token punctuation">{</span> element<span class="token punctuation">,</span> xpath <span class="token punctuation">}</span><span class="token operator">:</span> Props<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">boolean</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
</span></code></pre>
<p><strong>Separate Type</strong></p>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token keyword">type</span> <span class="token class-name">Props</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
</span><span class="code-line">    element<span class="token operator">:</span> HTMLElement<span class="token punctuation">;</span>
</span><span class="code-line">    xpath<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span><span class="code-line"><span class="token keyword">function</span> <span class="token function">matches</span><span class="token punctuation">(</span><span class="token punctuation">{</span> element<span class="token punctuation">,</span> xpath <span class="token punctuation">}</span><span class="token operator">:</span> Props<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">boolean</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
</span></code></pre>
<p>And by RHS/LHS I'm referring to the sides separated by the <code>:</code></p>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token keyword">function</span> <span class="token function">matches</span><span class="token punctuation">(</span><span class="token punctuation">{</span> element<span class="token punctuation">,</span> xpath <span class="token punctuation">}</span><span class="token operator">:</span> Props<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">boolean</span> <span class="token punctuation">{</span>
</span><span class="code-line">                 <span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span>  <span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span>
</span><span class="code-line">                 <span class="token constant">LHS</span>                 <span class="token constant">RHS</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>
<p>With that clarification out of the way, let get into it.</p>
<h3>The first implementation</h3>
<p>Our example is a function that just takes a single parameter, and applies destructuring inline.</p>
<p>Both <code>element</code> and <code>xpath</code> are required properties on the first parameter to this function, so callers must provide them
both.</p>
<div data-mdx-next="pre" data-focus="true"></div>
<pre class="language-tsx"><code class="language-tsx code-highlight"><span class="code-line"><span class="token keyword">interface</span> <span class="token class-name">Props</span> <span class="token punctuation">{</span>
</span><span class="code-line">    element<span class="token operator">:</span> <span class="token maybe-class-name">HTMLElement</span><span class="token punctuation">;</span>
</span><span class="code-line">    xpath<span class="token operator">:</span> <span class="token builtin">string</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span><span class="code-line">
</span><span class="code-line highlight-line"><span class="token keyword">function</span> <span class="token function">matches</span><span class="token punctuation">(</span><span class="token punctuation">{</span> element<span class="token punctuation">,</span> xpath <span class="token punctuation">}</span><span class="token operator">:</span> <span class="token maybe-class-name">Props</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">boolean</span> <span class="token punctuation">{</span>
</span><span class="code-line">  <span class="token keyword">return</span> <span class="token dom variable">document</span>
</span><span class="code-line">    <span class="token punctuation">.</span><span class="token method function property-access">evaluate</span><span class="token punctuation">(</span>xpath<span class="token punctuation">,</span> element<span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token maybe-class-name">XPathResult</span><span class="token punctuation">.</span><span class="token constant">BOOLEAN_TYPE</span><span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">)</span>
</span><span class="code-line">    <span class="token punctuation">.</span><span class="token property-access">booleanValue</span><span class="token punctuation">;</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>
<ul>
<li>Note how the first and only parameter here is a destructuring assignment. It's often thought of
as 'unpacking' the <code>element</code> and <code>xpath</code> properties, but as we'll see later I don't find this framing useful.</li>
</ul>
<p>Now, before we start to find bugs, let's quickly break down what parameter position
destructuring assignments actually represent "behind the scenes".</p>
<p>If we remove it from the signature and re-create the functionality directly in
the function body it helps to reveal some hidden elements</p>
<div data-mdx-next="pre" data-focus="true"></div>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token keyword">interface</span> <span class="token class-name">Props</span> <span class="token punctuation">{</span>
</span><span class="code-line">    element<span class="token operator">:</span> HTMLElement<span class="token punctuation">;</span>
</span><span class="code-line">    xpath<span class="token operator">:</span> <span class="token builtin">string</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span><span class="code-line">
</span><span class="code-line highlight-line"><span class="token keyword">function</span> <span class="token function">matches</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">boolean</span> <span class="token punctuation">{</span>
</span><span class="code-line highlight-line">  <span class="token keyword">let</span> <span class="token punctuation">{</span> element<span class="token punctuation">,</span> xpath <span class="token punctuation">}</span><span class="token operator">:</span> Props <span class="token operator">=</span> arguments<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
</span><span class="code-line">  <span class="token keyword">return</span> document
</span><span class="code-line">    <span class="token punctuation">.</span><span class="token function">evaluate</span><span class="token punctuation">(</span>xpath<span class="token punctuation">,</span> element<span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> XPathResult<span class="token punctuation">.</span><span class="token constant">BOOLEAN_TYPE</span><span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">)</span>
</span><span class="code-line">    <span class="token punctuation">.</span>booleanValue<span class="token punctuation">;</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>
<ul>
<li>We've removed everything from the function signature for clarity</li>
<li>We've introduced <code>let</code> to make it crystal clear that destructuring is an <code>assignment</code>. That is, we're creating
2 new bindings in this function's scope, <code>element</code> and <code>xpath</code></li>
</ul>
<p>We've kept type declaration <code>: Props</code> in place, to mimic what was in the signature, and <code>arguments[0]</code> just gives access
to the first parameter.</p>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token keyword">const</span> <span class="token punctuation">{</span> element<span class="token punctuation">,</span> xpath <span class="token punctuation">}</span><span class="token operator">:</span> Props <span class="token operator">=</span> arguments<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
</span></code></pre>
<ul>
<li>You can read this line now as<!-- -->
<ul>
<li>create new bindings <code>element</code> and <code>xpath</code> with types that match the corresponding types in <code>Props</code></li>
<li>for example, binding <code>element</code> has type <code>Props['element']</code>, and <code>xpath</code> has type <code>Props['xpath]</code></li>
</ul>
</li>
</ul>
<p>Nothing too exciting or surprising here. Typescript will do it's job and the new bindings can be used
safely.</p>
<h2>It all changes with defaults, though</h2>
<p>Let's evolve our function to now also support regular <code>CSS</code> selectors. We'll change <code>xpath</code> to <code>selector</code>,
and because <code>xpath</code> and <code>css</code> selectors are not interchangeable, we want a new property to help differentiate between them.</p>
<p>We'll call that new property <code>kind</code>, and we'll use a union of 2 literal types <code>"xpath" | "css"</code> as a poor man's enum.</p>
<p>More importantly though, because we already have a bunch of consumers of this function that we don't want to change,
we'll introduce a default value for <code>kind</code> and make it optional.</p>
<pre class="language-ts"><code class="language-diff-ts code-highlight"><span class="code-line"><span class="token keyword">interface</span> <span class="token class-name">Props</span> <span class="token punctuation">{</span>
</span><span class="code-line">    element<span class="token operator">:</span> HTMLElement<span class="token punctuation">;</span>
</span><span class="code-line deleted"><span class="token operator">-</span>   xpath<span class="token operator">:</span> <span class="token builtin">string</span>
</span><span class="code-line inserted"><span class="token operator">+</span>   selector<span class="token operator">:</span> <span class="token builtin">string</span>
</span><span class="code-line inserted"><span class="token operator">+</span>   kind<span class="token operator">?</span><span class="token operator">:</span> <span class="token string">'xpath'</span> <span class="token operator">|</span> <span class="token string">'css'</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span><span class="code-line">
</span><span class="code-line deleted"><span class="token operator">-</span><span class="token keyword">function</span> <span class="token function">matches</span><span class="token punctuation">(</span><span class="token punctuation">{</span> element<span class="token punctuation">,</span> xpath <span class="token punctuation">}</span><span class="token operator">:</span> Props<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">boolean</span> <span class="token punctuation">{</span>
</span><span class="code-line inserted"><span class="token operator">+</span><span class="token keyword">function</span> <span class="token function">matches</span><span class="token punctuation">(</span><span class="token punctuation">{</span> element<span class="token punctuation">,</span> selector<span class="token punctuation">,</span> kind <span class="token operator">=</span> <span class="token string">"xpath"</span> <span class="token punctuation">}</span><span class="token operator">:</span> Props<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">boolean</span> <span class="token punctuation">{</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>
<ul>
<li>Note: This is still perfectly type-safe ✅. Typescript will verify that any default value provided for <code>kind</code> has
to be assignable to <code>Props['kind']</code>, which expands to <code>"xpath" | "css"</code> in our case.</li>
</ul>
<p>Let's see what happens if we try to assign a different default value - something other than <code>"xpath" | "css"</code>. (broken onto multiple lines for clarity):</p>
<div data-mdx-next="pre" data-error="true"></div>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line line-number" line="1"><span class="token keyword">function</span> <span class="token function">matches</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
</span><span class="code-line line-number" line="2">    element<span class="token punctuation">,</span>
</span><span class="code-line line-number" line="3">    xpath<span class="token punctuation">,</span>
</span><span class="code-line line-number" line="4">    kind <span class="token operator">=</span> <span class="token string">"nope!"</span>
</span><span class="code-line line-number highlight-line" line="5">           <span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span>
</span><span class="code-line line-number highlight-line" line="6">Type <span class="token string">"nope!"</span> <span class="token keyword">is</span> not assignable to <span class="token keyword">type</span> <span class="token string">"xpath"</span> <span class="token operator">|</span> <span class="token string">"css"</span>
</span><span class="code-line line-number" line="7"><span class="token punctuation">}</span><span class="token operator">:</span> Props<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">boolean</span> <span class="token punctuation">{</span>
</span><span class="code-line line-number" line="8">
</span><span class="code-line line-number" line="9"><span class="token punctuation">}</span>
</span></code></pre>
<ul>
<li>✅ Nice! This is what we want - if we provide a default value within the destructuring like this, we want to ensure it
can only be a value assignable to the expected type. <code>"nope!"</code> fails that test, so Typescript prevents the bug!</li>
</ul>
<h2>The next evolution, cleaning up the function signature.</h2>
<p>As time goes on, more params are added and someone decides to drop the destructing from the function signature and
instead do it as the first statement in the function body</p>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token keyword">interface</span> <span class="token class-name">Props</span> <span class="token punctuation">{</span>
</span><span class="code-line">    element<span class="token operator">:</span> HTMLElement<span class="token punctuation">;</span>
</span><span class="code-line">    xpath<span class="token operator">:</span> <span class="token builtin">string</span>
</span><span class="code-line">    kind<span class="token operator">?</span><span class="token operator">:</span> <span class="token string">'css'</span> <span class="token operator">|</span> <span class="token string">'xpath'</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token keyword">function</span> <span class="token function">matches</span><span class="token punctuation">(</span>props<span class="token operator">:</span> Props<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">boolean</span> <span class="token punctuation">{</span>
</span><span class="code-line">    <span class="token keyword">const</span> <span class="token punctuation">{</span>
</span><span class="code-line">        element<span class="token punctuation">,</span>
</span><span class="code-line">        xpath<span class="token punctuation">,</span>
</span><span class="code-line">        kind <span class="token operator">=</span> <span class="token string">"xpath"</span>
</span><span class="code-line">    <span class="token punctuation">}</span> <span class="token operator">=</span> props<span class="token punctuation">;</span>
</span><span class="code-line">
</span><span class="code-line">    <span class="token comment">// snip</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>
<ul>
<li>Ahhh! a breath of fresh air - having just the parameter name <code>props</code> alongside it's type <code>Props</code> leave the function
signature in a great place. Docs can be added to the interface/type and the amount of values can grow at will without becoming
difficult to read/reason about</li>
<li>We've all seen crazy destructuring patterns that end up hogging the screen space in the function signatures - so why <em>wouldn't</em> you do this?</li>
</ul>
<h2>Well, just wait (and hold my 🍻)...</h2>
<p>There's a 'trap' you can fall into when combining types, destructuring and default values.</p>
<p>I've used Typescript since it was released, and have fallen for this on numerous occasions!</p>
<pre class="language-ts"><code class="language-diff-ts code-highlight"><span class="code-line"><span class="token keyword">interface</span> <span class="token class-name">Props</span> <span class="token punctuation">{</span>
</span><span class="code-line">    element<span class="token operator">:</span> HTMLElement<span class="token punctuation">;</span>
</span><span class="code-line">    xpath<span class="token operator">:</span> <span class="token builtin">string</span>
</span><span class="code-line">    kind<span class="token operator">?</span><span class="token operator">:</span> <span class="token string">'css'</span> <span class="token operator">|</span> <span class="token string">'xpath'</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token keyword">function</span> <span class="token function">matches</span><span class="token punctuation">(</span>props<span class="token operator">:</span> Props<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">boolean</span> <span class="token punctuation">{</span>
</span><span class="code-line">    <span class="token keyword">const</span> <span class="token punctuation">{</span>
</span><span class="code-line">        element<span class="token punctuation">,</span>
</span><span class="code-line">        xpath<span class="token punctuation">,</span>
</span><span class="code-line deleted"><span class="token operator">-</span>       kind <span class="token operator">=</span> <span class="token string">"xpath"</span>
</span><span class="code-line inserted"><span class="token operator">+</span>       kind <span class="token operator">=</span> <span class="token string">"this will not error!"</span>
</span><span class="code-line">    <span class="token punctuation">}</span> <span class="token operator">=</span> props<span class="token punctuation">;</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>
<ul>
<li>😱😱😱 This change does <strong>NOT</strong> cause a Typescript error!</li>
<li>We only wanted to allow <code>"css" | "xpath"</code>, but <code>"this will not error!"</code> is being allowed here!</li>
<li>This affects more than just literal types - but this a good example since so many programming patterns
in Typescript make heavy use of unions, and narrowing them.</li>
<li>Now, depending on how you go on to use <code>kind</code>, it may or may not be a big bug in your program. But the simple fact that
Typescript cannot prevent you setting an incorrect default value in this way concerns me!</li>
</ul>
<p>👀 So, even though <code>props</code> is marked as type <code>Props</code>, the type-safety doesn't seem to follow though to the <code>props.kind</code>
property - why?</p>
<p>Lets de-sugar the destructuring - it might help us understand how this hole in type-safety comes around.</p>
<pre class="language-tsx"><code class="language-tsx code-highlight"><span class="code-line"><span class="token keyword">interface</span> <span class="token class-name">Props</span> <span class="token punctuation">{</span>
</span><span class="code-line">    element<span class="token operator">:</span> <span class="token maybe-class-name">HTMLElement</span><span class="token punctuation">;</span>
</span><span class="code-line">    selector<span class="token operator">:</span> <span class="token builtin">string</span>
</span><span class="code-line">    kind<span class="token operator">?</span><span class="token operator">:</span> <span class="token string">'css'</span> <span class="token operator">|</span> <span class="token string">'xpath'</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token keyword">function</span> <span class="token function">matches</span><span class="token punctuation">(</span>props<span class="token operator">:</span> <span class="token maybe-class-name">Props</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">boolean</span> <span class="token punctuation">{</span>
</span><span class="code-line">    <span class="token keyword">const</span> element <span class="token operator">=</span> props<span class="token punctuation">.</span><span class="token property-access">element</span><span class="token punctuation">;</span>
</span><span class="code-line">    <span class="token keyword">const</span> selector <span class="token operator">=</span> props<span class="token punctuation">.</span><span class="token property-access">selector</span><span class="token punctuation">;</span>
</span><span class="code-line highlight-line">    <span class="token keyword">const</span> kind <span class="token operator">=</span> props<span class="token punctuation">.</span><span class="token property-access">kind</span> <span class="token operator">??</span> <span class="token string">"foooo"</span><span class="token punctuation">;</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>
<ul>
<li>This is a good-enough approximation of what the previous destructuring was doing.</li>
<li>This highlighted line is now effectively saying:<!-- -->
<ul>
<li>create a <em>new</em> binding <code>kind</code></li>
<li>with the type: <code>Props['kind'] | "foooo"</code></li>
<li>which expands out to <code>"xpath" | "css" | "foooo"</code></li>
</ul>
</li>
</ul>
<p>And that explains why Typescript won't error here - the new binding <code>kind</code> is just that - a new binding, it's not
required to have overlap with <code>Props['kind']</code> - it's just that <code>Props['kind']</code> contributes a type that <code>kind</code> <em>could</em> be assigned to.</p>
<p>So we've essentially disconnected <code>props.kind</code> from the local binding <code>kind</code>. They have a loose relationship, but not the kind
that will help us write correct programs. 😭</p>
<p>If we wanted to maintain the type-safety with this pattern, (still using this long-hand demo),
we'd have to apply type declarations for each property 🤮</p>
<pre class="language-ts"><code class="language-diff-ts code-highlight"><span class="code-line"><span class="token keyword">interface</span> <span class="token class-name">Props</span> <span class="token punctuation">{</span>
</span><span class="code-line">    element<span class="token operator">:</span> HTMLElement<span class="token punctuation">;</span>
</span><span class="code-line">    selector<span class="token operator">:</span> <span class="token builtin">string</span>
</span><span class="code-line">    kind<span class="token operator">?</span><span class="token operator">:</span> <span class="token string">'css'</span> <span class="token operator">|</span> <span class="token string">'xpath'</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token keyword">function</span> <span class="token function">matches</span><span class="token punctuation">(</span>props<span class="token operator">:</span> Props<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">boolean</span> <span class="token punctuation">{</span>
</span><span class="code-line">    <span class="token keyword">const</span> element <span class="token operator">=</span> props<span class="token punctuation">.</span>element<span class="token punctuation">;</span>
</span><span class="code-line">    <span class="token keyword">const</span> selector <span class="token operator">=</span> props<span class="token punctuation">.</span>selector<span class="token punctuation">;</span>
</span><span class="code-line deleted"><span class="token operator">-</span>   <span class="token keyword">const</span> kind <span class="token operator">=</span> props<span class="token punctuation">.</span>kind <span class="token operator">??</span> <span class="token string">"foooo"</span><span class="token punctuation">;</span>
</span><span class="code-line inserted"><span class="token operator">+</span>   <span class="token keyword">const</span> kind<span class="token operator">:</span> Props<span class="token punctuation">[</span><span class="token string">'kind'</span><span class="token punctuation">]</span> <span class="token operator">=</span> props<span class="token punctuation">.</span>kind <span class="token operator">??</span> <span class="token string">"foooo"</span><span class="token punctuation">;</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>
<ul>
<li>✅ By applying that type declaration, <code>const kind: Props['kind']</code>, Typescript will check all possible values
in the assignment and error if a type is invalid. This means <code>"fooo"</code> is now rejected.</li>
<li>The same thing applies for the other properties too, omitted here.</li>
</ul>
<p>Also, if we wanted to apply the same 'fix' to the destructuring version, it would look like this:</p>
<pre class="language-tsx"><code class="language-diff-tsx code-highlight"><span class="code-line"><span class="token keyword">interface</span> <span class="token class-name">Props</span> <span class="token punctuation">{</span>
</span><span class="code-line">    element<span class="token operator">:</span> <span class="token maybe-class-name">HTMLElement</span><span class="token punctuation">;</span>
</span><span class="code-line">    xpath<span class="token operator">:</span> <span class="token builtin">string</span>
</span><span class="code-line">    kind<span class="token operator">:</span> <span class="token string">'xpath'</span> <span class="token operator">|</span> <span class="token string">'css'</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token keyword">function</span> <span class="token function">matches</span><span class="token punctuation">(</span>props<span class="token operator">:</span> <span class="token maybe-class-name">Props</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">boolean</span> <span class="token punctuation">{</span>
</span><span class="code-line">    <span class="token keyword">const</span> <span class="token punctuation">{</span>
</span><span class="code-line">        element<span class="token punctuation">,</span>
</span><span class="code-line">        xpath<span class="token punctuation">,</span>
</span><span class="code-line">        kind <span class="token operator">=</span> <span class="token string">"foooo"</span>
</span><span class="code-line deleted"><span class="token operator">-</span>    <span class="token punctuation">}</span> <span class="token operator">=</span> props<span class="token punctuation">;</span>
</span><span class="code-line inserted"><span class="token operator">+</span>    <span class="token punctuation">}</span><span class="token operator">:</span> <span class="token maybe-class-name">Props</span> <span class="token operator">=</span> props<span class="token punctuation">;</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>
<ul>
<li>Here we're re-adding the full type declaration back in, directly after the closing <code>}</code> from the destructuring,
making it safe again.</li>
</ul>
<p>This whole thing is becoming rather unsatisfactory though - it's so easy to forget to add these declarations,
and no amount of strict Typescript or ESLint settings can help us here 😭</p>
<h2>Summary:</h2>
<p>This is <strong>not</strong> just limited to function signatures - that's a really common case, but the following
snippet is equally dangerous/error-prone.</p>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token keyword">interface</span> <span class="token class-name">Props</span> <span class="token punctuation">{</span>
</span><span class="code-line">    xpath<span class="token operator">:</span> <span class="token builtin">string</span>
</span><span class="code-line">    kind<span class="token operator">?</span><span class="token operator">:</span> <span class="token string">'xpath'</span> <span class="token operator">|</span> <span class="token string">'css'</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token keyword">const</span> props<span class="token operator">:</span> Props <span class="token operator">=</span> <span class="token punctuation">{</span> kind<span class="token operator">:</span> <span class="token string">"css"</span><span class="token punctuation">,</span> xpath<span class="token operator">:</span> <span class="token string">"..."</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token keyword">const</span> <span class="token punctuation">{</span>
</span><span class="code-line highlight-line">    kind <span class="token operator">=</span> <span class="token string">"a"</span>
</span><span class="code-line"><span class="token punctuation">}</span> <span class="token operator">=</span> props<span class="token punctuation">;</span>
</span></code></pre>
<ul>
<li>🧨 As before, Typescript cannot help you here - this 'type mistake' can be perfectly explained with the long-hand de-sugaring
as shown above.</li>
<li>Remember this line translates to: 'create a new binding <code>kind</code> with type <code>"xpath" | "css" | "a"</code>' - which is
not what you're probably intending.</li>
</ul>
<p>This is not a 'bug' in Typescript, or a problem with JavaScript really - I just view it as an easy mistake to make, something
to watch out for.</p>
<p>Going forward I'm personally going to use the inline-destructing pattern more - even though I don't like noisy
function signatures. The ability to prevent an entire class of type-safety bugs is just too appealing.</p>
<p>Regardless of which strategy you choose, having a better understanding of the semantics of
destructuring assignments could help you identify/fix bugs sooner 🤞</p>
<p>Finally, the next time you encounter people discussing what happens on the RHS of that <code>:</code>, remember that the LHS is far more
important for program correctness, so don't leave it out of the conversation! 👋</p>]]></content:encoded>
            <author>shane.osbourne@hey.com (Shane Osbourne)</author>
        </item>
        <item>
            <title><![CDATA[3 steps towards a faster TRU (Time to Reviewable URL)]]></title>
            <link>https://shane-o.dev/articles/introducing-tru</link>
            <guid>https://shane-o.dev/articles/introducing-tru</guid>
            <pubDate>Thu, 02 Jan 2025 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[Today I am formalizing TRU - a new metric for Frontend teams that shows how quickly you can
produce reviewable work. By definition, it's also a good measurement of
how quickly your team can iterate on ideas and bugfixes too.<!-- -->
<p>TRU (<strong>T</strong>ime to <strong>R</strong>eviewable <strong>U</strong>RL) is the amount of time it takes for a code change to be reflected
onto a public URL.</p>
<blockquote>
<p><strong>Note:</strong> Not producing reviewable URLs yet? Don't worry, there's still some valuable information in this post
that will help lead you there.</p>
</blockquote>
<h2>How to measure TRU?</h2>
<p>Your TRU score is measured in minutes/seconds. It's a timer that is triggered by a code change such as a pull request,
and it ends when that change can be reviewed by others.</p>
<p><strong>Example</strong>: if your CI process follows the common pattern of performing some static analysis,
then building your app, followed by tests, it's not uncommon to see a timeline like this:</p>
<div style="min-height:200px"></div>
<blockquote>
<p>Note: These numbers are <strong>very</strong> conservative,
I've seen much higher ones in real applications, but we'll use this rounded down '10 minutes in CI' as a way
to frame our improvements.</p>
</blockquote>
<p>The green block at the end of the pipeline is where a public URL is made available to others,
therefore, in this setup: <strong>TRU = 10+ min</strong>.</p>
<p>Converting that fact (of deploying to a public URL in 10+ min) into a measurable metric (TRU) helps start a conversation
about how to improve it.</p>
<h2>So, what's a good TRU?</h2>
<p>The 10+ min example above is <strong>not</strong> a good TRU. It might be acceptable as a first step - for example
if you're only just embarking on the concept of Preview URLs, but in reality we all know of CI processes that
far exceed 10+ min.</p>
<p>We need to aim higher, for a TRU of <strong>less than 1 minute</strong></p>
<p>Getting to this point would be a game-changing improvement to your organization.
Going from opened PR to a public URL in less than 1 minute, creates entirely different workflows.</p>
<p>The following advice in this post is admittedly easier to enact on greenfield projects, where you have more freedom to choose
your tools and processes. But, even on those projects where you think a one minute build is absurdly unrealistic, you can still
work to <em>reduce</em> your TRU using the tips and tricks I mention along the way.</p>
<div style="min-height:394px"></div>
<h2>Why is a faster TRU better though?</h2>
<p>Faster delivery, lower costs, easier maintenance and a better user experience.</p>
<p>Optimising for a faster TRU, especially in a team environment, creates a culture that values
deliverable software, deeper collaboration (through transparency) and better engineering practices overall.</p>
<ul>
<li>If your TRU is trending downwards (eg: towards faster deploys) you are quite literally speeding up every aspect of your software
delivery pipeline. Getting eyes on your work sooner than you could before, means you not only have the opportunity to catch bugs and
regressions sooner, but you can also solicit reviews from peers earlier in the project's lifecycle too. This will ultimately
lead to better quality software, delivered in less time.</li>
<li>Having a low TRU is also a strong indicator that you've fought for low-complexity - both in your application code
but also in your delivery processes as a whole. Low-complexity codebases and processes are easier to maintain and adapt
in the long term.</li>
<li>If you have a low TRU, it probably means your CI bills are lower too since it'll be doing less work for each change.</li>
</ul>
<p>The inverse of those benefits tells a bleaker, but more accurate picture based on what I've seen in Frontend-heavy
applications.</p>
<ul>
<li>If your TRU is trending upwards (eg: towards longer deploys) it normally means your team is becoming slower at
delivering changes overall. If it's taking longer to deploy a preview URL than it did before, then the changes that caused
that slowdown are directly impacting the speed in which reviews can occur. That, in turn, will cause projects to take
longer to complete than they did in the past.</li>
<li>If the main culprit of a high TRU is the build time, that tends to point to a project that has a high dependency
count along with many 'layers' that make up the application, and its associated tools. Your build/CI pipeline
is doing a lot of heavy work in this scenario, which is a symptom of not prioritizing simple tools and processes.</li>
<li>Projects in this state tend to be more challenging to maintain, adapt and upgrade due to the interwoven dependency
graphs, adding further delays.</li>
</ul>
<h2>Who benefits from a faster TRU?</h2>
<p>Managers will appreciate a faster TRU—having changes ready to review more quickly will obviously
speed up projects. You can fit more iterations and deployments into a day, and so your work will progress
more quickly.</p>
<p>Collaboration with Designers, UX and Accessibility folk will be more productive too.
If someone can review a change <strong>40 seconds</strong> after you made it, your combined efforts will pay off sooner.</p>
<p>Meanwhile for the engineers, they will feel liberated and more confident to make changes at a faster pace. They will
enjoy the burden of a long delivery pipeline being lifted, and can instead focus on making your product
better for users.</p>
<p>Speaking of which, the most important beneficiary of all will be: your users.</p>
<p>Spending engineering time on
reducing your TRU will naturally lead to lighter, more nimble software. If you actively work to lower your build and
deployment times, you'll find that it rules out a whole category of processes, tool and
dependencies automatically. Namely, the slow and bloated ones.</p>
<p>Lighter, faster software is accessible to a broader audience than the heavier, slower kind. It'll run better, for longer too, on devices of all kinds.</p>
<h2>Ok, how do we get there?</h2>
<p>We need a faster build process, with a more controlled environment and a prioritized preview deployment. Those are
the 3 steps I am highlighting in this post.</p>
<p>Tough decisions will need to be made, and some real engineering will need to occur.</p>
<p>So let's go through the steps in order of difficulty:</p>
<h3>Step 1: A fast build process</h3>
<p>Everything hinges on this first step—without it, steps 2 and 3 won't have the same compounding effect.</p>
<p>Projects that never make it to a fast TRU are most often hamstrung by how long their bundlers run for in CI,
so we need to aim for a build process that takes just a second or two.</p>
<p>Tools like <code>esbuild</code> can do an awful lot in a second or two, and they don't tend to lead you down the path
I've seen so often—where a well-meant setup can start to crawl in the build times.</p>
<p>A none-exhaustive list of rules you can apply, regardless of which specific tool you choose:</p>
<ul>
<li>Choose a modern, fast bundler - one that doesn't require a complex dependency graph of loaders/processors.</li>
<li>Separate the bundling and building from all linting and formatting</li>
<li>Favor micro libraries and frameworks that are closer to the web platform (lit, svelte, preact etc.)</li>
<li>For styling, opt for any process that results in regular CSS files and CSS classnames - loaded with &lt;link /&gt; tags</li>
<li>Use a preview "build target" as an opportunity to avoid expensive processing (more on this in Step 2)</li>
</ul>
<p>If you have the luxury of choosing tools and processes on a new application, figure out how far you can
get with <strong>just</strong> <code>esbuild</code> (or similar).</p>
<p>Ensure you are separating the generation of production assets from any type-checking, linting or formatting.
Sure, have ESLint/TypeScript running in parallel if you'd like,
but the 'build' should not be not much more than crawling through your entry points and producing
JS files on the other side.</p>
<p>Next, limit your dependencies massively, and audit them continuously. For React devs - try building on
a basic setup of just Preact, Preact Signals + CSS modules. You might just be amazed how far you can get.</p>
<ul>
<li>Note: If you go down this route - I'd recommend against an alias of <code>preact/compat</code> -&gt; <code>React</code>. I'd just <code>import preact</code> directly.
Pretending to be React gives the illusion that React-specific packages will 'just work.' I've found
issues with this approach, and by just using Preact directly I've seen fewer bugs and fewer
temptations to bring in packages that affect build times.<!-- -->
<ul>
<li>Sub-Note: Not choosing React (or aliasing with <code>preact/compat</code>) also has the nice side-effect of encouraging you to seek out libraries
that are framework-agnostic by default. Those tend to be lighter and more focussed on the web-platform as a default.</li>
</ul>
</li>
</ul>
<p>For styling, avoid any CSS-in-JS solutions. There's too much work needed to process them and tools like <code>esbuild</code> can
handle raw CSS + CSS module imports anyway. Let these tools produce a regular old CSS file, and you can load
it into your HTML just how the browsers prefer it.</p>
<pre class="language-tsx"><code class="language-tsx code-highlight"><span class="code-line"><span class="token keyword">import</span> <span class="token imports">styles</span> <span class="token keyword">from</span> <span class="token string">"./app.module.css"</span><span class="token punctuation">;</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token keyword">function</span> <span class="token function">App</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
</span><span class="code-line">  <span class="token comment">// this is fine...</span>
</span><span class="code-line">  <span class="token keyword">return</span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>styles<span class="token punctuation">.</span><span class="token property-access">root</span><span class="token punctuation">}</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">...</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>
<pre class="language-html"><code class="language-html code-highlight"><span class="code-line"><span class="token comment">&lt;!-- because you'll still end up with this, in your HTML (not JS) --&gt;</span>
</span><span class="code-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>link</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>styles.css<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
</span></code></pre>
<p>For those outside the React world, or for those looking to avoid JSX too, take a close look at <code>Svelte</code> or <code>Lit</code>.
Whichever you choose, if the build time is going to be  longer than a few seconds, then it's not really compatible
with a low TRU and should be avoided.</p>
<p>Now, regardless of which framework/bundler you choose, always consider what can be <strong>excluded</strong> from your build process.
For example, is it <strong>really</strong> necessary for all your assets to go through a bundler's pipeline? Is there anything you can
pre-process with other tools and just place in the <code>public</code>
folder like the old days?</p>
<p>What about that long, slow translation process—can that be done separately? Perhaps in the preview build (detailed next) you
could configure the application to load a translation file at runtime over fetch (from the public folder)?. That would
avoid executing a really complex build-time flow in the bundler (that often ends up including all locales—I've made that mistake before!)</p>
<p>These are just ideas—all of which in isolation might not make much difference. But, as a whole, employing this style of
engineering discipline will lead you down the path to success.</p>
<p>If you've managed to convince your team that this is a worthwhile pursuit, you're already most of
the way there, and you're standing out from the crowd. If your bundler process is just a second or two, you're never
going to feel burdened by it.</p>
<p>The next step, is to create a more Controlled Environment.</p>
<h3>Step 2: Controlled Environment</h3>
<p>A Controlled Environment is a way of running your application with pre-defined, deterministic states.</p>
<p>It could include things like mocked data. That helps to avoid UX and Design work from needing slow services that are complicated to setup (like API backends) in the preview
environment. It might even have some logic baked in to present the application in a known state for testing. No matter
which level you reach though, the key word here is <em>determinism</em>.</p>
<p>When everyone visiting a specially configured URL is experiencing the same thing, reviews and sign-off will be faster.</p>
<p>Browser-based tests can use the configured URLs too—making them faster and less flaky. It's reasonable to assume you can reduce
the length of your in-browser tests by 10x if you employ stricter rules around a controlled environment.</p>
<p>Having application states triggered by shareable URLs, as an example of a Controlled Environment, will enable more
accurate collaboration. Then re-using those URLs in automated tests gives a greater confidence that the CI process will go green eventually. (This
overlap between the Preview URL and your tests becomes crucial in Step 3, where we'll be seeking human reviews
long before the automated tests have finished).</p>
<p>As always, the following examples are just things I've seen work well - so don't get too hung up on the exact tools
or techniques—instead remain focussed on the goal of controlling your environment.</p>
<p><strong>Creating a Controlled Environment with Build Targets</strong></p>
<p>In other programming environments, the use of a 'build target' is a familiar concept. From a single codebase, often multiple
build artifacts will be produced—each tailored to the environment in which they'll run. This is often (but not always)
achieved with source-code annotations, like an attribute above a function, or some kind of special syntax.</p>
<p>You can use this kind of technique to produce a special <code>preview</code> build of your application that is pre-loaded
with knowledge of how to reproduce certain states. Controlling the external boundaries of your application is a
good practice anyway - and this technique just leans into it.</p>
<p>To make this work, we'll need basic bundler support and a single abstraction around whatever service you want to control.</p>
<p><strong>Bundler support</strong></p>
<p>Since I  mentioned <code>esbuild</code> already, I'll keep the examples specific to it. Other bundlers will have different ways of
achieving the same output.</p>
<p>So, for brevity, let's just assume your application talks to it's backend over <code>fetch</code>. To load a different implementation
in the preview build, we can use a label like <code>$PREVIEW: &lt;expr&gt;</code>, and then configure <code>esbuild</code> to strip it out of the
production builds.</p>
<pre class="language-js"><code class="language-js code-highlight"><span class="code-line"><span class="token keyword module">import</span> <span class="token imports">mockFetcher</span> <span class="token keyword module">from</span> <span class="token string">"./mocks.js"</span><span class="token punctuation">;</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token comment">// the default</span>
</span><span class="code-line"><span class="token keyword">const</span> fetcher <span class="token operator">=</span> globalThis<span class="token punctuation">.</span><span class="token property-access">fetch</span><span class="token punctuation">;</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token comment">// using a JavaScript label as something that the</span>
</span><span class="code-line"><span class="token comment">// build can choose to leave in, or strip out.</span>
</span><span class="code-line">$<span class="token constant">PREVIEW</span><span class="token operator">:</span> fetcher <span class="token operator">=</span> mockFetcher
</span></code></pre>
<p>Now we can configure <code>esbuild</code> to drop those <code>$PREVIEW</code> labels by default - this will act as the "production environment".</p>
<pre class="language-js"><code class="language-js code-highlight"><span class="code-line"><span class="token comment">// the default, for the production builds</span>
</span><span class="code-line">esbuild<span class="token punctuation">.</span><span class="token method function property-access">buildSync</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
</span><span class="code-line">   <span class="token spread operator">...</span>opts<span class="token punctuation">,</span>
</span><span class="code-line">   <span class="token literal-property property">dropLabels</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'$PREVIEW'</span><span class="token punctuation">]</span>
</span><span class="code-line"><span class="token punctuation">}</span><span class="token punctuation">)</span>
</span></code></pre>
<p>Then, to create the preview build we just omit the <code>$PREVIEW</code> label.</p>
<pre class="language-js"><code class="language-js code-highlight"><span class="code-line"><span class="token comment">// when --target=preview</span>
</span><span class="code-line">esbuild<span class="token punctuation">.</span><span class="token method function property-access">buildSync</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
</span><span class="code-line">   <span class="token spread operator">...</span>opts<span class="token punctuation">,</span>
</span><span class="code-line">   <span class="token literal-property property">dropLabels</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
</span><span class="code-line"><span class="token punctuation">}</span><span class="token punctuation">)</span>
</span><span class="code-line">
</span></code></pre>
<p>On its own, this doesn't seem too useful, but in the case of this <code>esbuild</code>-specific idea, it will also strip the
<code>import</code> statement of <code>./mocks.js</code> when unused (in the production build), meaning you have a safe entry point in
JS, where you can go further than plain mocks.</p>
<p>You can now add logic, timers, JSON files and most importantly, altering the preview behavior based on URL Search Params.</p>
<p><strong>Making use of the URL</strong></p>
<p>Now that we have an idea of what it means to create a 'preview' build - we can start to get creative about how we
respond to requests and how other side-effecting services affect our application.</p>
<p>Let's look at a practical example, where a state-based bug has been occurring if a particular API request is delayed.</p>
<p>In this workflow, we'd always start from the URL - thinking about how this particular bug can be reproduced if given a
set of parameters are present. Let's say we decide to add <code>?mock.api.delay=1000</code>. Then, inside the mock
implementation in <code>./mocks.js</code> we can detect it easily and adjust how we'd respond.</p>
<pre class="language-js"><code class="language-js code-highlight"><span class="code-line"><span class="token comment">// in mocks.js, not present in the production build</span>
</span><span class="code-line"><span class="token keyword">const</span> url <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">URL</span><span class="token punctuation">(</span><span class="token dom variable">location</span><span class="token punctuation">.</span><span class="token property-access">href</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</span><span class="code-line"><span class="token keyword">const</span> delayOverride <span class="token operator">=</span> url<span class="token punctuation">.</span><span class="token property-access">searchParams</span><span class="token punctuation">.</span><span class="token method function property-access">get</span><span class="token punctuation">(</span><span class="token string">'mock.api.delay'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token comment">// now use 'delayOverride' if it's not null to fake a slower response</span>
</span></code></pre>
<blockquote>
<p>Note: Mock Service Worker could be a good option here too, of course.</p>
</blockquote>
<p>The implementation is left up to the reader, but the bigger picture
here is that mock responses alone are sometimes not enough, and
we'll often want to add some logic too.</p>
<p>Once you get into the habit of making more and more states representable with sharable URL params like this,
you'll notice that people start sharing multiple review links when making changes, each tailored to
highlight a particular state.</p>
<p>Those I've helped to understand and adopt these practices are full of praise for the boost in productivity.</p>
<p><strong>It turns out that this is an excellent way to run UI tests too.</strong></p>
<p>I've seen testing setups of all shapes and sizes. From hour-long E2E test suites that nobody dares to touch,
to an over-reliance on unit-tests (and the fake confidence they bring) all the way through to fine-grained component
testing where everything works in isolation (but not together). My take, after all these years, is to favor in-application
testing, but with a strict rule around controlling the environment.</p>
<p>With the correct level of control in place, it's a joy to work with tools like Playwright - with tests that are somewhere
between E2E and integration. If each test fully loads the application, but it's configured to load the preview build,
then we can use the same URL search params that reviewers are using in the public URLs. However, now we're using them as test
cases.</p>
<p>This creates a really nice development cycle where your tests are fast, predictable and end up being a library of URLs
to reference later.</p>
<blockquote>
<p>Note: I'm deliberately omitting information about <em>how</em> any data/service mocking is implemented. There are lots of ways to
achieve it, each with their own benefits/tradeoffs. Instead I just want to emphasise the idea of sharing URLs with
previews and tests.</p>
</blockquote>
<p><strong>Make the preview URLs load the preview build</strong></p>
<p>Now the easy part. Once you've figured out your bundler's preferred way to create a specialized 'preview' build like the prior example,
you just need to ensure that your CI process knows when to use it. When deploying to the preview URLs, always build the
<code>"preview"</code> target. Otherwise, build as normal.</p>
<hr>
<p>If you've gotten to this point in the advice, you might already have a 1 second bundler process, along with a
special <code>preview</code> build target all working nicely together. If you do, you're well on the way to a seriously productive
project with excellent collaboration.</p>
<p>You'll just want to ensure nothing else in CI is slowing down these preview URLs from deploying...</p>
<h3>3: Prioritizing the preview deployment in CI</h3>
<p>An often overlooked optimization—don't let other checks/processes delay your preview URLs!</p>
<p>There's no bigger fan of static-analysis and tests than yours truly - both I consider to be mandatory for
any team of engineers. But after so many years of participating in this industry,
I've come to realize you can have your cake, and eat it too.</p>
<p>You can have discussions with Product Managers and Designers about a Preview URL that was created in seconds,
all whilst the 'rest' of the CI checks are occurring in the background.</p>
<p>In the end, you're not going to ship to production without those green check marks anyway, so you might as well get ahead of
the game and use that time to have people looking at your work.</p>
<blockquote>
<p>Remember, if you've followed the advice from Step 2 - <em>most</em> of your browser-based tests should be running against
the preview build too, so you can be confident that reviewers and automated tests are evaluating the same work!</p>
</blockquote>
<div><img src="/review-time2.png" style="display:block;margin:0 auto"></div>
<p><strong>Run a build + Deploy as early as possible</strong></p>
<p>In the 10+ minute example, we saw how the preview URL was only deployed when everything prior completed successfully.</p>
<p>Now, Step 3 of my advice is to free your engineers from the slow-down caused by those other processes, and let a
deployment to a preview URL happen as soon as humanly possible.</p>
<p>Because of Step 1 (a faster build process), you don't need to worry about running the build more than once. So, for the
sake of getting a really fast TRU and getting eyeballs on your work as soon as possible—why not ensure your processes
can handle a concurrent flow that only includes a build + deployment?</p>
<p>Even if your build is 10 seconds, and then your preview deployment is 35 seconds, that means a <strong>TRU of 45 seconds</strong> is an
achievable goal!</p>
<h2>Summary</h2>
<p>TRU (<strong>T</strong>ime to <strong>R</strong>eviewable <strong>U</strong>RL) is a metric that you can start tracking immediately. It's a proxy metric that
can help to expose the overall health of your software and its delivery pipeline. A team that maintains a fast TRU and continuously
looks to improve it, is likely delivering better work, more quickly, than those that do neither.</p>
<p>The 3 Steps I've presented above will get you closer to a fast TRU. It takes tough decisions and solid engineering work,
but the rewards are worth it.</p>
<p>The metric can be used to inform engineering decisions. For example, by referring to it when new tools and processes
are being proposed. Ask: 'How will this impact our Time to Reviewable URLs?'.</p>
<p>Managers and Tech Leads: Ask your Engineers if there's any low-hanging fruit that can be picked off in the name of
reducing your teams' TRU.</p>
<p>Engineers: On greenfield projects, deeply consider which tools, libraries and processes have the highest likelihood
of maintaining a low TRU in the long term. Choose lean, choose light.</p>
<p>On brownfield projects: gather your baseline metrics (how long it's taking to deploy right now) and start to paint a picture
of where the biggest delays are. Then in downtime, or during a maintenance window, you can work towards lowering your TRU
to the benefit of all involved.</p>]]></content:encoded>
            <author>shane.osbourne@hey.com (Shane Osbourne)</author>
        </item>
        <item>
            <title><![CDATA[Type Assertions vs Default Values in Typescript + JSDoc]]></title>
            <link>https://shane-o.dev/articles/jsdoc+ts</link>
            <guid>https://shane-o.dev/articles/jsdoc+ts</guid>
            <pubDate>Sun, 12 Nov 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h2>A practical example of using type assertions, despite their discouragement</h2>
<p>Typically, usage of the <code>as</code> keyword in Typescript is frowned upon, for good reason. Type-assertions represent a place
in code where you're saying that you know better then <code>tsc</code>. Sometimes you do, often you don't.</p>
<p>Others have written at length on this subject, so I don't want to enumerate the good/bad use-cases again here. Instead, I
wanted to highlight a time when it's not only a good idea, but can actually help you design better programs.</p>
<h2>Example: XState machine definition</h2>
<p>In XState, <code>context</code> is a piece of internal state that you end up accessing from various places, and <code>schema.events</code>
represents a union of possible events that this machine accepts.</p>
<p>Of all the things you might want to be 'strongly typed' in your application, internal data and incoming events should be
right at the top of the list.</p>
<p>This is why you'll often see the following pattern in use:</p>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token keyword">const</span> machine <span class="token operator">=</span> <span class="token function">createMachine</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
</span><span class="code-line">  id<span class="token operator">:</span> <span class="token string">'dialog'</span><span class="token punctuation">,</span>
</span><span class="code-line highlight-line">  context<span class="token operator">:</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token keyword">as</span> <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
</span><span class="code-line">  schema<span class="token operator">:</span> <span class="token punctuation">{</span>
</span><span class="code-line highlight-line">    events<span class="token operator">:</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token keyword">as</span>
</span><span class="code-line">      <span class="token operator">|</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> <span class="token string">"OPEN"</span> <span class="token punctuation">}</span>
</span><span class="code-line">      <span class="token operator">|</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> <span class="token string">"CLOSE"</span> <span class="token punctuation">}</span>
</span><span class="code-line">  <span class="token punctuation">}</span><span class="token punctuation">,</span>
</span><span class="code-line">  states<span class="token operator">:</span> <span class="token punctuation">{</span>
</span><span class="code-line">    <span class="token string-property property">"closed"</span><span class="token operator">:</span> <span class="token punctuation">{</span> on<span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string-property property">"OPEN"</span><span class="token operator">:</span> <span class="token string">"open"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
</span><span class="code-line">    <span class="token string-property property">"open"</span><span class="token operator">:</span> <span class="token punctuation">{</span> on<span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string-property property">"CLOSE"</span><span class="token operator">:</span> <span class="token string">"closed"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
</span><span class="code-line">  <span class="token punctuation">}</span><span class="token punctuation">,</span>
</span><span class="code-line"><span class="token punctuation">}</span><span class="token punctuation">)</span>
</span></code></pre>
<ul>
<li>It's just a simple/demo machine definition, we're not focussing on any actual functionality here.</li>
<li>Note how both properties (the highlighted lines) lines are using <code>{}</code> (empty object) as the values, followed by <code>as ...</code>,
that's the <code>type assertion</code> part. The value is just a plain old object, but we're <code>lying</code> to Typescript, and asking it
to treat <code>context</code> as though it was <code>{ id: string }</code> and <code>schema.events</code> as though it was a union of those two objects.</li>
<li>The interesting part here, is that we're doing this <em>only</em> to provide types, nothing else.<!-- -->
<ul>
<li>Consumers of this machine will be <em>forced</em> to provide <code>id: string</code> as a context property. ✅</li>
<li>Likewise with <code>schema.events</code> - this will ensure anyone sending messages to this machine can only send one of the messages
we defined. ✅</li>
</ul>
</li>
<li>So it's all about defining types, or contracts. We aim to make fault free programs and this kind of code-level documentation
that gets enforced by Typescript can help us along that path.</li>
</ul>
<h2>Trying default values instead</h2>
<p>Because type-assertions are sometimes frowned up, I wanted to take a moment to consider alternatives ways to handle
the typing of that <code>context.id</code> property in the example above.</p>
<p>Hopefully you'll see, that used in the correct places, type-assertions can make a <em>lot</em> of sense. Why? Because we can
use them as an alternative to a <strong>default</strong> value.</p>
<p>So taking an alternative path for illustrative purposes, we could model this problem by using <code>string | null</code> as the type of the <code>id</code> field instead:
(<code>undefined</code> would work nicely too, but space.)</p>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token keyword">const</span> machine <span class="token operator">=</span> <span class="token function">createMachine</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
</span><span class="code-line">  id<span class="token operator">:</span> <span class="token string">'dialog'</span><span class="token punctuation">,</span>
</span><span class="code-line highlight-line">  context<span class="token operator">:</span> <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token keyword">null</span> <span class="token punctuation">}</span> <span class="token keyword">as</span> <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token keyword">null</span> <span class="token operator">|</span> <span class="token builtin">string</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
</span><span class="code-line">  schema<span class="token operator">:</span> <span class="token punctuation">{</span>
</span><span class="code-line">    events<span class="token operator">:</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token keyword">as</span>
</span><span class="code-line">      <span class="token operator">|</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> <span class="token string">"OPEN"</span> <span class="token punctuation">}</span>
</span><span class="code-line">      <span class="token operator">|</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> <span class="token string">"CLOSE"</span> <span class="token punctuation">}</span>
</span><span class="code-line">  <span class="token punctuation">}</span><span class="token punctuation">,</span>
</span><span class="code-line">  states<span class="token operator">:</span> <span class="token punctuation">{</span>
</span><span class="code-line">    <span class="token string-property property">"closed"</span><span class="token operator">:</span> <span class="token punctuation">{</span> on<span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string-property property">"OPEN"</span><span class="token operator">:</span> <span class="token string">"open"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
</span><span class="code-line">    <span class="token string-property property">"open"</span><span class="token operator">:</span> <span class="token punctuation">{</span> on<span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string-property property">"CLOSE"</span><span class="token operator">:</span> <span class="token string">"closed"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
</span><span class="code-line">  <span class="token punctuation">}</span><span class="token punctuation">,</span>
</span><span class="code-line"><span class="token punctuation">}</span><span class="token punctuation">)</span>
</span></code></pre>
<ul>
<li>
<p>This highlighted line shows how we can set an initial, or default value for <code>id</code>, using <code>null</code> to represent the absence of the
actual <code>string</code> value.</p>
</li>
<li>
<p>The part that follows, <code> as { id: null | string }</code>, ensures that Typescript will allow us to assign a <code>string</code> value later</p>
<ul>
<li>Without this, we'd be stuck with only <code>null</code> being an assignable type</li>
</ul>
</li>
<li>
<p>This is quite a nice pattern <em>if</em> a default value makes sense to your particular problem.</p>
<ul>
<li>For instance, perhaps the <code>id</code> is actually unknown until some asynchronous operation has completed. In
that case, <code>id</code> can <em>really</em> be <code>null</code> or a <code>string</code>.</li>
</ul>
</li>
<li>
<p>If you find yourself in this kind of situation, then the way we've set this <code>id</code> field up is really accurate. It's <code>null</code>
now (the default), but can be also become a <code>string</code> later.</p>
</li>
<li>
<p>You'll get help from Typescript too. Whenever you try to use <code>id</code> as a string, you'll be reminded that it could be <code>null</code>,
so you'll have to check. Being honest like this about the nullability of a value should prevent some of the
most common Javascript bugs (accessing values that are absent). ✅</p>
</li>
</ul>
<p><strong>JSDoc</strong></p>
<p>For completeness, in JSDoc we can achieve the same thing with the following:</p>
<pre class="language-js"><code class="language-js code-highlight"><span class="code-line"><span class="token function">createMachine</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
</span><span class="code-line">  <span class="token literal-property property">id</span><span class="token operator">:</span> <span class="token string">'dialog'</span><span class="token punctuation">,</span>
</span><span class="code-line">  <span class="token literal-property property">context</span><span class="token operator">:</span> <span class="token punctuation">{</span>
</span><span class="code-line highlight-line">    <span class="token doc-comment comment">/** <span class="token keyword">@type</span> <span class="token class-name"><span class="token punctuation">{</span>string <span class="token operator">|</span> <span class="token keyword">null</span><span class="token punctuation">}</span></span> */</span>
</span><span class="code-line highlight-line">    <span class="token literal-property property">id</span><span class="token operator">:</span> <span class="token keyword null nil">null</span>
</span><span class="code-line">  <span class="token punctuation">}</span><span class="token punctuation">,</span>
</span><span class="code-line">  <span class="token literal-property property">schema</span><span class="token operator">:</span> <span class="token punctuation">{</span>
</span><span class="code-line">    <span class="token literal-property property">events</span><span class="token operator">:</span> <span class="token doc-comment comment">/** <span class="token keyword">@type</span> <span class="token class-name"><span class="token punctuation">{</span>Events<span class="token punctuation">}</span></span> */</span> <span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span>
</span><span class="code-line">  <span class="token punctuation">}</span><span class="token punctuation">,</span>
</span><span class="code-line">  <span class="token literal-property property">states</span><span class="token operator">:</span> <span class="token punctuation">{</span>
</span><span class="code-line">    <span class="token string-property property">"closed"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token literal-property property">on</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string-property property">"OPEN"</span><span class="token operator">:</span> <span class="token string">"open"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
</span><span class="code-line">    <span class="token string-property property">"open"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token literal-property property">on</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string-property property">"CLOSE"</span><span class="token operator">:</span> <span class="token string">"closed"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
</span><span class="code-line">  <span class="token punctuation">}</span><span class="token punctuation">,</span>
</span><span class="code-line"><span class="token punctuation">}</span><span class="token punctuation">)</span>
</span></code></pre>
<h2>When a default value makes no sense</h2>
<p>The previous approach, making <code>id</code>  have the type <code>string | null</code>, is fine if it's a true reflection of the way
your program is structured.</p>
<p>The flip side though, is when you <strong>know</strong> that your component or state-machine could never have come into existence
without an <code>id</code>. In that case making it <code>nullable</code> in the initial context definition is no longer accurate - it's just there
to please Typescript.</p>
<p>I've seen this (and done it myself!) on many occasions - they are plenty places in Typescript codebases where a type is
'wider' than it needs to be - a simple <code>?</code> on a property when it's not actually optional, or like in our example, providing
a default for something that actually can never actually take that value.</p>
<p>This kind of problem creates a knock-on effect too. Using <em>wider</em> types than needed, like <code>string | null</code> when just <code>string</code>
would've been enough, means that you'll have to keep <em>narrowing</em> it everytime you try to use it.</p>
<pre class="language-js"><code class="language-js code-highlight"><span class="code-line"><span class="token keyword">const</span> <span class="token punctuation">[</span>state<span class="token punctuation">,</span> send<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token maybe-class-name">MachineContext</span><span class="token punctuation">.</span><span class="token method function property-access">useActor</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
</span><span class="code-line"><span class="token keyword">const</span> id <span class="token operator">=</span> state<span class="token punctuation">.</span><span class="token property-access">context</span><span class="token punctuation">.</span><span class="token property-access">id</span><span class="token punctuation">;</span>
</span><span class="code-line"><span class="token comment">//                       ^^ null | string</span>
</span></code></pre>
<ul>
<li>Notice how the type from the initial machine definition comes through here -  <code>context.id</code> cannot be used directly, even
though we know that it can never actually be <code>null</code> due to how the rest of the program is structured.</li>
</ul>
<p>So, when I am forced to deal with this, I prefer to halt the program, considering it a completely invalid state.</p>
<pre class="language-js"><code class="language-js code-highlight"><span class="code-line"><span class="token keyword module">import</span> <span class="token imports">invariant</span> <span class="token keyword module">from</span> <span class="token string">"tiny-invariant"</span><span class="token punctuation">;</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token comment">// later</span>
</span><span class="code-line"><span class="token keyword">const</span> <span class="token punctuation">[</span>state<span class="token punctuation">,</span> send<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token maybe-class-name">MachineContext</span><span class="token punctuation">.</span><span class="token method function property-access">useActor</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
</span><span class="code-line"><span class="token keyword">const</span> id <span class="token operator">=</span> state<span class="token punctuation">.</span><span class="token property-access">context</span><span class="token punctuation">.</span><span class="token property-access">id</span><span class="token punctuation">;</span>
</span><span class="code-line highlight-line"><span class="token function">invariant</span><span class="token punctuation">(</span><span class="token keyword">typeof</span> id <span class="token operator">===</span> string<span class="token punctuation">,</span> <span class="token string">""</span><span class="token punctuation">)</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token console class-name">console</span><span class="token punctuation">.</span><span class="token method function property-access">log</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span>
</span><span class="code-line"><span class="token comment">//          ^^ string</span>
</span></code></pre>
<ul>
<li>this highlighted line checks the given condition for truthiness, and will throw an exception if it fails.</li>
<li>then, thanks to control-flow analysis, subsequent lines can now freely use <code>id</code> as a string, since Typescript knows
that's the only type assignable to it.<!-- -->
<ul>
<li>Before this check, <code>id</code> had the type <code>string | null</code>, but the check <code>typeof id === string</code>
effectively erases the possibility of it being <code>null</code>, leaving only <code>string</code> ✅</li>
</ul>
</li>
</ul>
<p>If you are not using something like <code>tiny-invariant</code>, you can cause the same effect on control-flow analysis with a
manual <code>throw</code></p>
<pre class="language-js"><code class="language-diff-js code-highlight"><span class="code-line deleted"><span class="token operator">-</span><span class="token function">invariant</span><span class="token punctuation">(</span><span class="token keyword">typeof</span> id <span class="token operator">===</span> string<span class="token punctuation">,</span> <span class="token string">"unreachable, id must be a string"</span><span class="token punctuation">)</span>
</span><span class="code-line inserted"><span class="token operator">+</span><span class="token keyword control-flow">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> id <span class="token operator">!==</span> string<span class="token punctuation">)</span> <span class="token keyword control-flow">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token string">"unreachable, id must be a string"</span><span class="token punctuation">)</span>
</span></code></pre>
<h2>Avoiding runtime validation when possible</h2>
<p>These runtime checks that only exist to please Typescript are a bit of a necessary evil - they can be used for good, but
we should try to recognise which of them are required, and which can be made redundant with a better design.</p>
<blockquote>
<p>Side note: I'm <strong>not</strong> referring to the rise in popularity of tools that verify external data - you should
be validating <em>everything</em> that comes into your application anyway, ideally using tools that have a nice integration
with Typescript, like Arktype, Zod, Valibot, etc.</p>
</blockquote>
<p>In relation to the examples given in this post, a 'better design' would be to prevent using a wider type than needed.
In short, that means getting back to the example we opened with, where a blank <code>{}</code> is used, along with a type-assertion
to force Typescript into believing our <code>context</code> has the type <code>{ id: string }</code>.</p>
<pre class="language-ts"><code class="language-diff-ts code-highlight"><span class="code-line deleted"><span class="token operator">-</span>   context<span class="token operator">:</span> <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token keyword">null</span> <span class="token punctuation">}</span> <span class="token keyword">as</span> <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token keyword">null</span> <span class="token operator">|</span> <span class="token builtin">string</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
</span><span class="code-line inserted"><span class="token operator">+</span>   context<span class="token operator">:</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token keyword">as</span> <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token builtin">string</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
</span></code></pre>
<ul>
<li>Because of the way XState works, users of this machine will need to provide a valid <code>context</code> and because we've now
removed the <code>null</code>, consumers will understand that a valid <code>id</code> is part of the contract.</li>
</ul>
<p>Now, the design of the types actually reflects the usage in code - if there's never a way that <code>id</code> can <em>actually</em> be <code>null</code>,
then we should strive to avoid it and not resort to a default value of <code>null</code> or <code>undefined</code>.</p>
<h2>Avoid colouring your code</h2>
<p>The concept of 'colouring' has been spoken about before, often about languages that support <code>async</code>/<code>await</code>. The idea is that
the moment you make a function <code>async</code>, you're now forcing the <em>caller</em> of your function to handle the fact that it
won't yield a value immediately. They'll have to use APIs or language features to 'wait' for the value, or to 'unpack' it.</p>
<p>Once you alter your code to handle someone else's latency like this, you inevitably have to alter <em>your</em> API too so that <em>your</em>
callers know they can no longer expect a result synchronously. It spreads outwards, on and on. Before you know it, functions
that have no business being <code>async</code> or knowing anything about program latency can end up being 'coloured' in this way.</p>
<p>Whilst perhaps not to the same degree, I consider the 'width' of types, or the amount of types that are assignable to a value,
to also be a form of colouring. If the value has a single assignable type, I'd consider it to be the same colour as a regular
synchronous function. In both cases, consumers don't have to deal with the additional domain knowledge - they can just use the
functionality as described on the box.</p>
<p>But, if you include too many types, like <code>string | null</code> when just <code>string</code> would have done, you're now
forcing other places in the program to have to narrow the type before usage. It's a different colour now, and consumers will
need to look up how to deal with your leaked implementation detail - worse, they may even need to
change <em>their</em> colour to suit yours!</p>
<h2>Summary</h2>
<p>Of course, I'm just using this fictional <code>id</code> field and the type <code>string | null</code> as a way to represent the type widening/narrowing
problem in a simple, concise way. But, it's the concept that's important - I'm sure you can expand this idea to cover
more complicated types that you've worked with.</p>
<p>Is there somewhere you can remove an optional property? Is there a <code>null</code> or <code>undefined</code> value
that can be removed altogether with a different program design?</p>
<p>The design of your types has a much larger impact that it first appears. If you're going to spread one colour
over another, try to ensure it's deliberate &amp; accurate. If it's just in the name of pleasing Typescript or
getting something done in a pinch, then put a <code>todo</code> on it and stick it in the tech debt category 💪.</p>
<hr>
<h2>JSDoc Code Examples for Reference</h2>
<p>Whilst the <code>as</code> keyword cannot be used in JSDoc (not valid Javascript), you can achieve the same thing.</p>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token comment">// ts</span>
</span><span class="code-line">context<span class="token operator">:</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token keyword">as</span> <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token builtin">string</span> <span class="token operator">|</span> <span class="token keyword">null</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
</span></code></pre>
<pre class="language-js"><code class="language-js code-highlight"><span class="code-line"><span class="token comment">// jsdoc</span>
</span><span class="code-line"><span class="token literal-property property">context</span><span class="token operator">:</span> <span class="token doc-comment comment">/** <span class="token keyword">@type</span> <span class="token class-name"><span class="token punctuation">{</span><span class="token punctuation">{</span>id<span class="token operator">:</span> string <span class="token operator">|</span> <span class="token keyword">null</span><span class="token punctuation">}</span><span class="token punctuation">}</span></span> */</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span>
</span></code></pre>
<ul>
<li>note how in the JSDoc example there's an extra <code>()</code> around the <code>{}</code>, this is how you get <code>@type</code> to apply directly to
the subsequent expression.</li>
</ul>
<p>Just like in Typescript, if you want to move your types into separate definitions, you can either use <code>@typedef</code> in the
same file</p>
<pre class="language-js"><code class="language-js code-highlight"><span class="code-line"><span class="token doc-comment comment">/**
</span></span><span class="code-line"><span class="token doc-comment comment"> * <span class="token keyword">@typedef</span> <span class="token class-name"><span class="token punctuation">{</span><span class="token punctuation">{</span>type<span class="token operator">:</span> <span class="token string">"OPEN"</span><span class="token punctuation">}</span> <span class="token operator">|</span> <span class="token punctuation">{</span>type<span class="token operator">:</span> <span class="token string">"CLOSE"</span><span class="token punctuation">}</span><span class="token punctuation">}</span></span> <span class="token class-name">Events</span>
</span></span><span class="code-line"><span class="token doc-comment comment"> * <span class="token keyword">@typedef</span> <span class="token class-name"><span class="token punctuation">{</span><span class="token punctuation">{</span>id<span class="token operator">:</span> string<span class="token punctuation">}</span><span class="token punctuation">}</span></span> <span class="token class-name">Context</span>
</span></span><span class="code-line"><span class="token doc-comment comment"> */</span>
</span><span class="code-line"><span class="token function">createMachine</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
</span><span class="code-line">  <span class="token literal-property property">id</span><span class="token operator">:</span> <span class="token string">'dialog'</span><span class="token punctuation">,</span>
</span><span class="code-line">  <span class="token literal-property property">context</span><span class="token operator">:</span> <span class="token doc-comment comment">/** <span class="token keyword">@type</span> <span class="token class-name"><span class="token punctuation">{</span>Context<span class="token punctuation">}</span></span> */</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
</span><span class="code-line">  <span class="token literal-property property">schema</span><span class="token operator">:</span> <span class="token punctuation">{</span>
</span><span class="code-line">    <span class="token literal-property property">events</span><span class="token operator">:</span> <span class="token doc-comment comment">/** <span class="token keyword">@type</span> <span class="token class-name"><span class="token punctuation">{</span>Events<span class="token punctuation">}</span></span> */</span> <span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span>
</span><span class="code-line">  <span class="token punctuation">}</span><span class="token punctuation">,</span>
</span><span class="code-line">  <span class="token literal-property property">states</span><span class="token operator">:</span> <span class="token punctuation">{</span>
</span><span class="code-line">    <span class="token string-property property">"closed"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token literal-property property">on</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string-property property">"OPEN"</span><span class="token operator">:</span> <span class="token string">"open"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
</span><span class="code-line">    <span class="token string-property property">"open"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token literal-property property">on</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string-property property">"CLOSE"</span><span class="token operator">:</span> <span class="token string">"closed"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
</span><span class="code-line">  <span class="token punctuation">}</span><span class="token punctuation">,</span>
</span><span class="code-line"><span class="token punctuation">}</span><span class="token punctuation">)</span>
</span></code></pre>
<p>Or, you can use a <code>.ts</code> file, just ensure you only put types it in:</p>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token comment">// types.ts</span>
</span><span class="code-line"><span class="token keyword">export</span> <span class="token keyword">type</span> <span class="token class-name">Events</span> <span class="token operator">=</span>
</span><span class="code-line">  <span class="token operator">|</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> <span class="token string">"OPEN"</span> <span class="token punctuation">}</span>
</span><span class="code-line">  <span class="token operator">|</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> <span class="token string">"CLOSE"</span> <span class="token punctuation">}</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token keyword">export</span> <span class="token keyword">interface</span> <span class="token class-name">Context</span> <span class="token punctuation">{</span>
</span><span class="code-line">  id<span class="token operator">:</span> <span class="token builtin">string</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>
<p>With that <code>types.ts</code> file, you can then import types directly</p>
<pre class="language-js"><code class="language-diff-js code-highlight"><span class="code-line"><span class="token function">createMachine</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
</span><span class="code-line">  <span class="token literal-property property">id</span><span class="token operator">:</span> <span class="token string">'dialog'</span><span class="token punctuation">,</span>
</span><span class="code-line deleted"><span class="token operator">-</span>  context<span class="token operator">:</span> <span class="token doc-comment comment">/** <span class="token keyword">@type</span> <span class="token class-name"><span class="token punctuation">{</span>Context<span class="token punctuation">}</span></span> */</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
</span><span class="code-line inserted"><span class="token operator">+</span>  context<span class="token operator">:</span> <span class="token doc-comment comment">/** <span class="token keyword">@type</span> <span class="token class-name"><span class="token punctuation">{</span><span class="token keyword">import</span><span class="token punctuation">(</span><span class="token string">"./types.ts"</span><span class="token punctuation">)</span><span class="token punctuation">.</span>Context<span class="token punctuation">}</span></span> */</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
</span><span class="code-line">  <span class="token literal-property property">schema</span><span class="token operator">:</span> <span class="token punctuation">{</span>
</span><span class="code-line deleted"><span class="token operator">-</span>   events<span class="token operator">:</span> <span class="token doc-comment comment">/** <span class="token keyword">@type</span> <span class="token class-name"><span class="token punctuation">{</span>Events<span class="token punctuation">}</span></span> */</span> <span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span>
</span><span class="code-line inserted"><span class="token operator">+</span>   events<span class="token operator">:</span> <span class="token doc-comment comment">/** <span class="token keyword">@type</span> <span class="token class-name"><span class="token punctuation">{</span><span class="token keyword">import</span><span class="token punctuation">(</span><span class="token string">"./types.ts"</span><span class="token punctuation">)</span><span class="token punctuation">.</span>Events<span class="token punctuation">}</span></span> */</span> <span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span>
</span><span class="code-line">  <span class="token punctuation">}</span><span class="token punctuation">,</span>
</span><span class="code-line">  <span class="token literal-property property">states</span><span class="token operator">:</span> <span class="token punctuation">{</span>
</span><span class="code-line">    <span class="token string-property property">"closed"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token literal-property property">on</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string-property property">"OPEN"</span><span class="token operator">:</span> <span class="token string">"open"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
</span><span class="code-line">    <span class="token string-property property">"open"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token literal-property property">on</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string-property property">"CLOSE"</span><span class="token operator">:</span> <span class="token string">"closed"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
</span><span class="code-line">  <span class="token punctuation">}</span><span class="token punctuation">,</span>
</span><span class="code-line"><span class="token punctuation">}</span><span class="token punctuation">)</span>
</span></code></pre>]]></content:encoded>
            <author>shane.osbourne@hey.com (Shane Osbourne)</author>
        </item>
        <item>
            <title><![CDATA[How to avoid a potential bug with JSDoc + @typedef]]></title>
            <link>https://shane-o.dev/articles/jsdoc-typedef</link>
            <guid>https://shane-o.dev/articles/jsdoc-typedef</guid>
            <pubDate>Mon, 01 Jan 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[With no build-step in sight, it can be tempting to put more and more type information into comments.<!-- -->
<p>Consider the following type definition:</p>
<pre class="language-js"><code class="language-js code-highlight"><span class="code-line"><span class="token doc-comment comment">/**
</span></span><span class="code-line"><span class="token doc-comment comment"> * <span class="token keyword">@typedef</span> <span class="token class-name"><span class="token punctuation">{</span><span class="token string">'css'</span> <span class="token operator">|</span> <span class="token string">'xpath'</span><span class="token punctuation">}</span></span> <span class="token class-name">SelectorKind</span>
</span></span><span class="code-line"><span class="token doc-comment comment"> */</span>
</span></code></pre>
<p>This declares a new type alias for a union of the literal types <code>'css'</code> and <code>'xpath'</code>, it can then be
used in parameter position like so:</p>
<div data-mdx-next="pre" data-error="true"></div>
<pre class="language-js"><code class="language-js code-highlight"><span class="code-line line-number" line="1"><span class="token doc-comment comment">/**
</span></span><span class="code-line line-number" line="2"><span class="token doc-comment comment"> * <span class="token keyword">@param</span> <span class="token class-name"><span class="token punctuation">{</span>SelectorKind<span class="token punctuation">}</span></span> <span class="token parameter">kind</span>
</span></span><span class="code-line line-number" line="3"><span class="token doc-comment comment"> * <span class="token keyword">@param</span> <span class="token class-name"><span class="token punctuation">{</span>string<span class="token punctuation">}</span></span> <span class="token parameter">selector</span>
</span></span><span class="code-line line-number" line="4"><span class="token doc-comment comment"> */</span>
</span><span class="code-line line-number" line="5"><span class="token keyword">function</span> <span class="token function">matches</span><span class="token punctuation">(</span><span class="token parameter">kind<span class="token punctuation">,</span> selector</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
</span><span class="code-line line-number" line="6">
</span><span class="code-line line-number" line="7"><span class="token function">matches</span><span class="token punctuation">(</span><span class="token string">'css'</span><span class="token punctuation">,</span> <span class="token string">'.heading'</span><span class="token punctuation">)</span> <span class="token comment">// ✅</span>
</span><span class="code-line line-number" line="8"><span class="token function">matches</span><span class="token punctuation">(</span><span class="token string">'xpath'</span><span class="token punctuation">,</span> <span class="token string">'.heading'</span><span class="token punctuation">)</span> <span class="token comment">// ✅</span>
</span><span class="code-line line-number" line="9"><span class="token function">matches</span><span class="token punctuation">(</span><span class="token string">'oops!'</span><span class="token punctuation">,</span> <span class="token string">'.heading'</span><span class="token punctuation">)</span> <span class="token comment">// ❌</span>
</span><span class="code-line line-number" line="10">        <span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span>
</span><span class="code-line line-number highlight-line" line="11"><span class="token maybe-class-name">Argument</span> <span class="token keyword">of</span> type <span class="token string">"oops!"</span> is not assignable to parameter <span class="token keyword">of</span> type <span class="token string">"css"</span> <span class="token operator">|</span> "xpath
</span></code></pre>
<p>In a Typescript file, the same thing would be:</p>
<div data-mdx-next="pre" data-error="true"></div>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token keyword">type</span> <span class="token class-name">SelectorKind</span> <span class="token operator">=</span> <span class="token string">'css'</span> <span class="token operator">|</span> <span class="token string">'xpath'</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token keyword">function</span> <span class="token function">matches</span><span class="token punctuation">(</span>kind<span class="token operator">:</span> SelectorKind<span class="token punctuation">,</span> selector<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
</span></code></pre>
<p>They both provide the same level of type-safety + DX (autocomplete), but the JSDoc version suffers from
a possible edge-case that I encountered for real recently 👀...</p>
<h2>All about the formatting</h2>
<p>A common formatting technique to apply when a Typescript codebase starts to grow:</p>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token keyword">type</span> <span class="token class-name">SelectorKind</span> <span class="token operator">=</span>
</span><span class="code-line">   <span class="token operator">|</span> <span class="token string">'css'</span>
</span><span class="code-line">   <span class="token operator">|</span> <span class="token string">'xpath'</span>
</span></code></pre>
<p>It's good that Typescript supports the leading <code>|</code> here, it allows these union types to grow and remain nicely
aligned 👌</p>
<p>It's even more common when listing out things like events:</p>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token keyword">type</span> <span class="token class-name">Events</span> <span class="token operator">=</span>
</span><span class="code-line">  <span class="token operator">|</span> <span class="token punctuation">{</span> kind<span class="token operator">:</span> <span class="token string">'login'</span><span class="token punctuation">;</span> user<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> password<span class="token operator">:</span> <span class="token builtin">string</span> <span class="token punctuation">}</span>
</span><span class="code-line">  <span class="token operator">|</span> <span class="token punctuation">{</span> kind<span class="token operator">:</span> <span class="token string">'logout'</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
</span></code></pre>
<p>Now, trying to apply this back to JSDoc, the first attempt might be:</p>
<pre class="language-js"><code class="language-js code-highlight"><span class="code-line line-number" line="1"><span class="token doc-comment comment">/**
</span></span><span class="code-line line-number" line="2"><span class="token doc-comment comment"> * <span class="token keyword">@typedef</span> <span class="token class-name"><span class="token punctuation">{</span>
</span></span></span><span class="code-line line-number" line="3"><span class="token doc-comment comment"><span class="token class-name"> <span class="token operator">*</span>    <span class="token operator">|</span> <span class="token string">'css'</span>
</span></span></span><span class="code-line line-number" line="4"><span class="token doc-comment comment"><span class="token class-name"> <span class="token operator">*</span>    <span class="token operator">|</span> <span class="token string">'xpath'</span>
</span></span></span><span class="code-line line-number" line="5"><span class="token doc-comment comment"><span class="token class-name"> <span class="token operator">*</span> <span class="token punctuation">}</span></span> <span class="token class-name">SelectorKind</span>
</span></span><span class="code-line line-number" line="6"><span class="token doc-comment comment"> */</span>
</span></code></pre>
<ul>
<li>❌ oops! We've just introduced a subtle bug!</li>
<li>Because the very next character following <code>* @typedef {</code> is a <strong>new line</strong>, it ends up causing <code>SelectorKind</code> to have the type
<code>any</code>!</li>
</ul>
<p>The most worrying part of this potential bug, is how it silently widens the type to <code>any</code>, whilst still keeping the
type defined in scope.</p>
<p>It means that any function that previously used <code>SelectorKind</code> before we re-formatted will still appear
to be valid, for example:</p>
<pre class="language-js"><code class="language-js code-highlight"><span class="code-line line-number" line="1"><span class="token doc-comment comment">/**
</span></span><span class="code-line line-number highlight-line" line="2"><span class="token doc-comment comment"> * <span class="token keyword">@param</span> <span class="token class-name"><span class="token punctuation">{</span>SelectorKind<span class="token punctuation">}</span></span> <span class="token parameter">kind</span>
</span></span><span class="code-line line-number" line="3"><span class="token doc-comment comment"> * <span class="token keyword">@param</span> <span class="token class-name"><span class="token punctuation">{</span>string<span class="token punctuation">}</span></span> <span class="token parameter">selector</span>
</span></span><span class="code-line line-number" line="4"><span class="token doc-comment comment"> */</span>
</span><span class="code-line line-number" line="5"><span class="token keyword">function</span> <span class="token function">matches</span><span class="token punctuation">(</span><span class="token parameter">kind<span class="token punctuation">,</span> selector</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
</span><span class="code-line line-number" line="6">
</span><span class="code-line line-number highlight-line" line="7"><span class="token function">matches</span><span class="token punctuation">(</span><span class="token string">'oops!'</span><span class="token punctuation">,</span> <span class="token string">'.heading'</span><span class="token punctuation">)</span>
</span><span class="code-line line-number" line="8"><span class="token comment">//      ^^^^^^^ this should be an error, but isn't!</span>
</span></code></pre>
<ul>
<li><strong>on line 2</strong>, this type reference is still valid - Typescript will ensure we're referring to a type it knows about.</li>
<li>but, <strong>line 7</strong> no longer causes a type-error, even though it did before 😭</li>
<li>because <code>SelectorKind</code> was accidentally widened to <code>any</code>, <code>matches</code> will now accept any argument - a random string, a number, anything!</li>
</ul>
<p>To resolve this problem, you just need to ensure you place a character from your type directly after the opening <code>{</code>, for example:</p>
<pre class="language-js"><code class="language-js code-highlight"><span class="code-line"><span class="token doc-comment comment">/**
</span></span><span class="code-line"><span class="token doc-comment comment"> * <span class="token keyword">@typedef</span> <span class="token class-name"><span class="token punctuation">{</span><span class="token operator">|</span>
</span></span></span><span class="code-line"><span class="token doc-comment comment"><span class="token class-name"><span class="token operator">*</span>       <span class="token string">'css'</span>
</span></span></span><span class="code-line"><span class="token doc-comment comment"><span class="token class-name"> <span class="token operator">*</span>    <span class="token operator">|</span> <span class="token string">'xpath'</span>
</span></span></span><span class="code-line"><span class="token doc-comment comment"><span class="token class-name"> <span class="token operator">*</span>    <span class="token operator">|</span> <span class="token string">'abc'</span>
</span></span></span><span class="code-line"><span class="token doc-comment comment"><span class="token class-name"> <span class="token operator">*</span>    <span class="token operator">|</span> <span class="token string">'def'</span>
</span></span></span><span class="code-line"><span class="token doc-comment comment"><span class="token class-name"> <span class="token operator">*</span> <span class="token punctuation">}</span></span> <span class="token class-name">SelectorKind</span>
</span></span><span class="code-line"><span class="token doc-comment comment"> */</span>
</span></code></pre>
<ul>
<li>🤢 I added a few more strings to show the value of the alignment, but now the absense of the <code>|</code> to the left of <code>css</code> just makes this
even more confusing.</li>
</ul>
<p>Here's another format:</p>
<pre class="language-js"><code class="language-js code-highlight"><span class="code-line"><span class="token doc-comment comment">/**
</span></span><span class="code-line"><span class="token doc-comment comment"> * <span class="token keyword">@typedef</span> <span class="token class-name"><span class="token punctuation">{</span><span class="token string">'css'</span>
</span></span></span><span class="code-line"><span class="token doc-comment comment"><span class="token class-name"> <span class="token operator">*</span>    <span class="token operator">|</span> <span class="token string">'xpath'</span>
</span></span></span><span class="code-line"><span class="token doc-comment comment"><span class="token class-name"> <span class="token operator">*</span>    <span class="token operator">|</span> <span class="token string">'abc'</span>
</span></span></span><span class="code-line"><span class="token doc-comment comment"><span class="token class-name"> <span class="token operator">*</span>    <span class="token operator">|</span> <span class="token string">'def'</span>
</span></span></span><span class="code-line"><span class="token doc-comment comment"><span class="token class-name"> <span class="token operator">*</span> <span class="token punctuation">}</span></span> <span class="token class-name">SelectorKind</span>
</span></span><span class="code-line"><span class="token doc-comment comment"> */</span>
</span></code></pre>
<ul>
<li>This also works, but now we've lost the alignment altogether! 😭</li>
</ul>
<p>Regardless of what <em>can</em> work though (there is likely more combinations), let's get back to the point of this post...</p>
<h2>Why this matters</h2>
<p>I think it's dangerous how a single newline character can convert a previously type-safe
implementation into one that now has an <code>any</code> hole in it.</p>
<p>My real-world scenario played out as such:</p>
<ol>
<li>I started out with a simple type, and then used it inside a function parameter.</li>
<li>Then, I added a few more variants, until the line-length got too long for my liking.</li>
<li>So, I split the type onto multiple lines for readability...</li>
<li>💥 silently, Typescript had demoted my union of string literals into <code>any</code>, essentially removing all type-checking.</li>
</ol>
<h2>Other Solutions</h2>
<h3>(1) Using Typescript files alongside <code>.js</code></h3>
<p>You can retain your no-build setup whilst still benefiting from more elegant type definitions when needed.
Just stick to JSDoc and then <code>import</code> from <code>.ts</code> files as needed.</p>
<pre class="language-js"><code class="language-js code-highlight"><span class="code-line"><span class="token doc-comment comment">/**
</span></span><span class="code-line"><span class="token doc-comment comment"> * <span class="token keyword">@param</span> <span class="token class-name"><span class="token punctuation">{</span><span class="token keyword">import</span><span class="token punctuation">(</span><span class="token string">"./types.ts"</span><span class="token punctuation">)</span><span class="token punctuation">.</span>SelectorKind<span class="token punctuation">}</span></span> <span class="token parameter">kind</span>
</span></span><span class="code-line"><span class="token doc-comment comment"> * <span class="token keyword">@param</span> <span class="token class-name"><span class="token punctuation">{</span>string<span class="token punctuation">}</span></span> <span class="token parameter">selector</span>
</span></span><span class="code-line"><span class="token doc-comment comment"> */</span>
</span><span class="code-line"><span class="token keyword">function</span> <span class="token function">matches</span><span class="token punctuation">(</span><span class="token parameter">kind<span class="token punctuation">,</span> selector</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
</span></code></pre>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token comment">// types.ts</span>
</span><span class="code-line"><span class="token keyword">export</span> <span class="token keyword">type</span> <span class="token class-name">SelectorKind</span> <span class="token operator">=</span>
</span><span class="code-line">   <span class="token operator">|</span> <span class="token string">'css'</span>
</span><span class="code-line">   <span class="token operator">|</span> <span class="token string">'xpath'</span>
</span></code></pre>
<ul>
<li>The trick with this approach is to only ever put <strong>types</strong> in your Typescript files - this prevents having
to transpile anything.</li>
<li>Now you can format your types without any of the drawbacks seen in <code>@typedef</code></li>
<li>No matter the formatting, inside a TS file this type will never be widened to <code>any</code> by mistake 😍.</li>
<li>Also, if you need to reach for more complex features, like mapped types, you can also place those in the <code>.ts</code> files too.
(something you literally cannot do in JSDoc alone)</li>
</ul>
<h3>(2) Deriving Types from JavaScript code:</h3>
<p>In simple cases, you may be able to derive these kinds of types directly from Javascript code instead. The
example in this post is deliberately small for brevity, but it would be a good example where you could drop
the <code>@typedef</code> altogether, for example:</p>
<pre class="language-js"><code class="language-js code-highlight"><span class="code-line"><span class="token keyword">const</span> <span class="token constant">SELECTOR_KINDS</span> <span class="token operator">=</span> <span class="token doc-comment comment">/** <span class="token keyword">@type</span> <span class="token class-name"><span class="token punctuation">{</span><span class="token keyword">const</span><span class="token punctuation">}</span></span> */</span> <span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">'css'</span><span class="token punctuation">,</span> <span class="token string">'xpath'</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token doc-comment comment">/**
</span></span><span class="code-line"><span class="token doc-comment comment"> * <span class="token keyword">@param</span> <span class="token class-name"><span class="token punctuation">{</span>SELECTOR_KINDS<span class="token punctuation">[</span>number<span class="token punctuation">]</span><span class="token punctuation">}</span></span> <span class="token parameter">kind</span>
</span></span><span class="code-line"><span class="token doc-comment comment"> * <span class="token keyword">@param</span> <span class="token class-name"><span class="token punctuation">{</span>string<span class="token punctuation">}</span></span> <span class="token parameter">selector</span>
</span></span><span class="code-line"><span class="token doc-comment comment"> */</span>
</span><span class="code-line"><span class="token keyword">function</span> <span class="token function">matches</span><span class="token punctuation">(</span><span class="token parameter">kind<span class="token punctuation">,</span> selector</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
</span></code></pre>
<ul>
<li>This approach has some nice properties when the types are simple (eg: lists of strings)</li>
</ul>
<p>It does however, come with its own tradeoffs: get the parens incorrect, and it'll also accidentally widen to <code>any</code></p>
<pre class="language-js"><code class="language-js code-highlight"><span class="code-line"><span class="token comment">// ✅ this has the type `readonly ["css", "xpath"]`</span>
</span><span class="code-line"><span class="token keyword">const</span> <span class="token constant">SELECTOR_KINDS</span> <span class="token operator">=</span> <span class="token doc-comment comment">/** <span class="token keyword">@type</span> <span class="token class-name"><span class="token punctuation">{</span><span class="token keyword">const</span><span class="token punctuation">}</span></span> */</span> <span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">'css'</span><span class="token punctuation">,</span> <span class="token string">'xpath'</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token comment">// ❌ this has the type `string[]`</span>
</span><span class="code-line"><span class="token keyword">const</span> <span class="token constant">SELECTOR_KINDS_2</span> <span class="token operator">=</span> <span class="token doc-comment comment">/** <span class="token keyword">@type</span> <span class="token class-name"><span class="token punctuation">{</span><span class="token keyword">const</span><span class="token punctuation">}</span></span> */</span> <span class="token punctuation">[</span><span class="token string">'css'</span><span class="token punctuation">,</span> <span class="token string">'xpath'</span><span class="token punctuation">]</span>
</span></code></pre>
<h2>Summary</h2>
<p>When working with TypeScript via JSDoc, it can be tempting to reach for patterns you've used or seen in <code>.ts</code> files from other projects.</p>
<p>If adding them inside JavaScript comments feels awkward, consider one of the solutions mentioned above.</p>
<p>A simple rule to apply is as follows: If your types become complex (like unions) and cannot be represented in a single line,
consider moving it to a .ts file or deriving it from code instead 👌.</p>]]></content:encoded>
            <author>shane.osbourne@hey.com (Shane Osbourne)</author>
        </item>
        <item>
            <title><![CDATA[Making a Rust application slower than the Node version. And then fixing it.]]></title>
            <link>https://shane-o.dev/articles/making-a-rust-application-slower-than-the-node-version-and-then-fixing-it</link>
            <guid>https://shane-o.dev/articles/making-a-rust-application-slower-than-the-node-version-and-then-fixing-it</guid>
            <pubDate>Mon, 08 Apr 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[
<p>Whilst working on a new version of <a href="https://browsersync.io">Browsersync</a> I ran into an interesting example where my
Rust version was <em>not</em> faster than the original NodeJS implementation - in fact it was 2x slower! 😱</p>
<p>In this <a href="/articles/rewrite+in+rust+faster+with+more+features">previous post</a> I outlined how the Browsersync proxy works:
it sends requests to the target url with modified headers and then buffers the response body in memory before applying
string replacements.</p>
<p>I had been following the idea of not spending too much time optimizing the Rust version whilst I was deep in development. Being
liberal with <code>.clone()</code> and trying to avoid lifetimes where possible was proving very productive. I was getting features
completed really quickly and feeling great about the overall direction.</p>
<h2>The pressures of a public demo</h2>
<p>Getting to feature parity on the <strong>proxy</strong> feature was an exciting milestone for me since it proves out a lot of the
new architectural patterns (more on this in a future post) and I was keen to make a demo 👀.</p>
<p>So, imagine my horror when I spun up a simple example and it turned out to be <em>much</em> slower than the original NodeJS implementation
I'd written years ago! 😱😱</p>
<h2>So, did I just forget to run <code>cargo build</code> with <code>--release</code> like everyone does?</h2>
<p>A common problem that people run into when benchmarking Rust programs (normally on Twitter when promoting an alternative)
is that they forget to build in release mode! Even if you've been writing Rust for years, it's easy to forget. I think
that's just because the local development story with Rust and cargo is so ergonomic that we just get so used
to running <code>cargo run</code> 🤣.</p>
<p>Regardless, in my case this alone didn't help!</p>
<h2>So what exactly <em>was</em> slow</h2>
<p>In my demo I just wanted to show a <code>localhost</code> server that proxied all requests through to a live website (in the Browsersync style).</p>
<pre><code class="code-highlight"><span class="code-line">GET localhost:3000/ -&gt; GET https://browsersync.io/
</span><span class="code-line">GET localhost:3000/css/core.css -&gt; GET https://browsersync.io/css/core.css
</span><span class="code-line">... etc
</span></code></pre>
<p>When using <a href="https://browsersync.io/">https://browsersync.io/</a> you get a total of just 19 requests (when viewed in the browser).</p>
<p>This was hardly a stress-test, so why the crazy numbers?</p>
<table><thead><tr><th></th><th align="left">baseline</th><th align="left">proxy in node js</th><th align="left">proxy in rust (slowest)</th></tr></thead><tbody><tr><td>load time</td><td align="left">250-320ms</td><td align="left">780-900ms</td><td align="left"><strong>950ms-1.2sec</strong></td></tr><tr><td>page weight</td><td align="left">368kb</td><td align="left">562kb</td><td align="left">368kb</td></tr><tr><td>compression</td><td align="left">✅</td><td align="left">❌</td><td align="left">✅</td></tr></tbody></table>
<p><em>Something</em> was wrong here. Even though I hadn't applied any specific optimisations yet, I had expected the Rust version, even in debug
mode to far out-perform the NodeJS implementation...</p>
<h2>The Importance of Connection Pooling</h2>
<p>The short version is that my implementation was creating a new HTTP client inside the <strong>main request handler</strong>! 🤣</p>
<p>So, for every single HTTP request that my application dealt with, it was spinning up a new thread pool and preparing things
behind the scenes to re-use resources where possible. Only for the handler to exit, tearing down all the side effects and then
re-creating them immediately on the next request 😱.</p>
<p>No wonder the Rust version was even slower than the NodeJS version!</p>
<h2>The problem</h2>
<p>Since I was deep in prototyping mode and was thinking more about architecture than individual handlers, I let this slip by:</p>
<p>Imagine an HTTP server being started like this:</p>
<pre class="language-rust"><code class="language-rust code-highlight"><span class="code-line"><span class="token attribute attr-name">#[tokio::main]</span>
</span><span class="code-line"><span class="token keyword">async</span> <span class="token keyword">fn</span> <span class="token function-definition function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
</span><span class="code-line">    <span class="token comment">// snip</span>
</span><span class="code-line">    <span class="token keyword">let</span> app <span class="token operator">=</span> <span class="token class-name">Router</span><span class="token punctuation">::</span><span class="token function">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">nest_service</span><span class="token punctuation">(</span><span class="token string">"/"</span><span class="token punctuation">,</span> <span class="token function">any</span><span class="token punctuation">(</span>handler<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</span><span class="code-line">    <span class="token comment">// snip</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>
<p>That's a reasonable way to allow a single service to handle all routes under <code>/</code>. The problem is what occurs within <code>handler</code></p>
<pre class="language-rust"><code class="language-rust code-highlight"><span class="code-line line-number" line="1"><span class="token keyword">pub</span> <span class="token keyword">async</span> <span class="token keyword">fn</span> <span class="token function-definition function">handler</span><span class="token punctuation">(</span>req<span class="token punctuation">:</span> <span class="token class-name">Request</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Result</span><span class="token operator">&lt;</span><span class="token class-name">Response</span><span class="token punctuation">,</span> <span class="token class-name">StatusCode</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
</span><span class="code-line line-number" line="2">
</span><span class="code-line line-number" line="3">    <span class="token keyword">let</span> https <span class="token operator">=</span> <span class="token class-name">HttpsConnector</span><span class="token punctuation">::</span><span class="token function">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</span><span class="code-line line-number" line="4">    <span class="token keyword">let</span> client <span class="token operator">=</span> <span class="token class-name">Client</span><span class="token punctuation">::</span><span class="token function">builder</span><span class="token punctuation">(</span><span class="token class-name">TokioExecutor</span><span class="token punctuation">::</span><span class="token function">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span>https<span class="token punctuation">)</span><span class="token punctuation">;</span>
</span><span class="code-line line-number" line="5">
</span><span class="code-line line-number" line="6">    <span class="token comment">// snip: modify the response object</span>
</span><span class="code-line line-number" line="7">
</span><span class="code-line line-number" line="8">    <span class="token class-name">Ok</span><span class="token punctuation">(</span>client
</span><span class="code-line line-number" line="9">        <span class="token punctuation">.</span><span class="token function">request</span><span class="token punctuation">(</span>req<span class="token punctuation">)</span>
</span><span class="code-line line-number" line="10">        <span class="token punctuation">.</span><span class="token keyword">await</span>
</span><span class="code-line line-number" line="11">        <span class="token punctuation">.</span><span class="token function">map_err</span><span class="token punctuation">(</span><span class="token closure-params"><span class="token closure-punctuation punctuation">|</span>_<span class="token closure-punctuation punctuation">|</span></span> <span class="token class-name">StatusCode</span><span class="token punctuation">::</span><span class="token constant">BAD_REQUEST</span><span class="token punctuation">)</span><span class="token operator">?</span>
</span><span class="code-line line-number" line="12">        <span class="token punctuation">.</span><span class="token function">into_response</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
</span><span class="code-line line-number" line="13"><span class="token punctuation">}</span>
</span></code></pre>
<ul>
<li>This async function, <code>handler</code> will be called for <em>every</em> request - HTML, CSS, JS, images, web sockets, everything!</li>
<li>On lines <code>3</code> and <code>4</code> we create the http client and it's <code>https</code> connector</li>
<li>But this happens over and over again with every incoming request 🙈🙈🙈</li>
</ul>
<p>I was very confident this was the cause of the poor performance 🤣 - so, it was just going to be a case of moving the
client creation code out of the handler, and coming up with a way to access the client in the handler.</p>
<h2>The solution</h2>
<h3>Step 1) ensure the client is not re-created on every request. This can be done by pulling it up into <code>main</code></h3>
<pre class="language-rust"><code class="language-diff-rust code-highlight"><span class="code-line"><span class="token attribute attr-name">#[tokio::main]</span>
</span><span class="code-line"><span class="token keyword">async</span> <span class="token keyword">fn</span> <span class="token function-definition function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
</span><span class="code-line inserted"><span class="token operator">+</span>   <span class="token keyword">let</span> https <span class="token operator">=</span> <span class="token class-name">HttpsConnector</span><span class="token punctuation">::</span><span class="token function">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</span><span class="code-line inserted"><span class="token operator">+</span>   <span class="token keyword">let</span> client <span class="token operator">=</span> <span class="token class-name">Client</span><span class="token punctuation">::</span><span class="token function">builder</span><span class="token punctuation">(</span><span class="token class-name">TokioExecutor</span><span class="token punctuation">::</span><span class="token function">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span>https<span class="token punctuation">)</span><span class="token punctuation">;</span>
</span><span class="code-line">    <span class="token keyword">let</span> app <span class="token operator">=</span> <span class="token class-name">Router</span><span class="token punctuation">::</span><span class="token function">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">nest_service</span><span class="token punctuation">(</span><span class="token string">"/"</span><span class="token punctuation">,</span> <span class="token function">any</span><span class="token punctuation">(</span>handler<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</span><span class="code-line">    <span class="token comment">// snip</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>
<h3>Step 2) Next, we need to access that client from within the handler.</h3>
<p>Luckily <a href="https://github.com/tokio-rs/axum">Axum</a> has a really nice way to do this, known as extractors. Extractors allow
a type-safe way of accessing various useful things from within your handler functions.</p>
<pre class="language-rust"><code class="language-rust code-highlight"><span class="code-line"><span class="token keyword">async</span> <span class="token keyword">fn</span> <span class="token function-definition function">handler</span><span class="token punctuation">(</span><span class="token class-name">State</span><span class="token punctuation">(</span>client<span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token class-name">State</span><span class="token operator">&lt;</span><span class="token class-name">Client</span><span class="token operator">&gt;</span><span class="token punctuation">,</span> req<span class="token punctuation">:</span> <span class="token class-name">Request</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Result</span><span class="token operator">&lt;</span><span class="token class-name">Response</span><span class="token punctuation">,</span> <span class="token class-name">StatusCode</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
</span><span class="code-line">   <span class="token comment">// implementation as before.</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>
<ul>
<li>Notice how we added a new parameter, <code>State(client): State&lt;Client&gt;</code></li>
<li>This uses the <code>State</code> extractor along with the fact that parameters in Rust can also be patterns - this allows direct
access to the element inside the tuple struct (<code>client</code> in this case).</li>
</ul>
<h3>Step 3) Provide state to the Router</h3>
<p>If we tried to run the code at this point we'd get an error stating that our handler
cannot be used with the Router configured in <code>main</code>.</p>
<p>This is the type-safe part - each handler contributes to the overall type of the outer Router - so to satify
the type checker we need to call <code>.with_state(...)</code> with a type that satisfies all extractors.</p>
<p>We only have 1 of our own (we use <code>Request</code> too, but that's supported already), so it's just a case of making this small
change:</p>
<pre class="language-rust"><code class="language-diff-rust code-highlight"><span class="code-line"><span class="token attribute attr-name">#[tokio::main]</span>
</span><span class="code-line"><span class="token keyword">async</span> <span class="token keyword">fn</span> <span class="token function-definition function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
</span><span class="code-line">    <span class="token keyword">let</span> https <span class="token operator">=</span> <span class="token class-name">HttpsConnector</span><span class="token punctuation">::</span><span class="token function">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</span><span class="code-line">    <span class="token keyword">let</span> client <span class="token operator">=</span> <span class="token class-name">Client</span><span class="token punctuation">::</span><span class="token function">builder</span><span class="token punctuation">(</span><span class="token class-name">TokioExecutor</span><span class="token punctuation">::</span><span class="token function">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span>https<span class="token punctuation">)</span><span class="token punctuation">;</span>
</span><span class="code-line deleted"><span class="token operator">-</span>   <span class="token keyword">let</span> app <span class="token operator">=</span> <span class="token class-name">Router</span><span class="token punctuation">::</span><span class="token function">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">nest_service</span><span class="token punctuation">(</span><span class="token string">"/"</span><span class="token punctuation">,</span> <span class="token function">any</span><span class="token punctuation">(</span>handler<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</span><span class="code-line inserted"><span class="token operator">+</span>   <span class="token keyword">let</span> app <span class="token operator">=</span> <span class="token class-name">Router</span><span class="token punctuation">::</span><span class="token function">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">nest_service</span><span class="token punctuation">(</span><span class="token string">"/"</span><span class="token punctuation">,</span> <span class="token function">any</span><span class="token punctuation">(</span>handler<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">with_state</span><span class="token punctuation">(</span>client<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</span><span class="code-line">    <span class="token comment">// snip</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>
<p>With that change in place, <code>http connection pooling</code> will work as expected, and the numbers start to look a lot better :)</p>
<table><thead><tr><th></th><th align="left">baseline</th><th align="left">proxy in rust</th><th align="left">proxy in rust, no pooling</th></tr></thead><tbody><tr><td>load time</td><td align="left">250-320ms</td><td align="left"><strong>250-320ms</strong></td><td align="left">950ms-1.2sec</td></tr><tr><td>page weight</td><td align="left">368kb</td><td align="left">368kb</td><td align="left">368kb</td></tr><tr><td>compression</td><td align="left">✅</td><td align="left">✅</td><td align="left">✅</td></tr></tbody></table>
<h2>Summary</h2>
<p>The mistake highlighted in this post was a silly one 🙈, and was easily fixed - but it does show how easy it is to crash
performance when dealing with network bound workloads.</p>
<p>And there I was, thinking I just needed that simple <code>--release</code> flag 🤣</p>
<h2>Notes:</h2>
<ul>
<li>I omitted some types in snippets above for brevity - have a look at the working demo I made here for details:
<a href="https://github.com/shakyShane/t-stream/blob/main/https-proxy/src/main.rs">https://github.com/shakyShane/t-stream/blob/main/https-proxy/src/main.rs</a></li>
<li>There are other perf things I looked at too, like swapping <code>Mutex</code>'s for <code>RwLock</code>'s, but for the type of tool I'm building
those are not going to produce much of a difference :)</li>
</ul>]]></content:encoded>
            <author>shane.osbourne@hey.com (Shane Osbourne)</author>
        </item>
        <item>
            <title><![CDATA[Using Cargo workspaces with a `napi-rs` project]]></title>
            <link>https://shane-o.dev/articles/napi-rs-workspace</link>
            <guid>https://shane-o.dev/articles/napi-rs-workspace</guid>
            <pubDate>Tue, 07 May 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h2>Using workspaces to manage crates and dependencies within a default `napi-rs` project.</h2>
<p>Creating a new project with <a href="https://napi.rs">https://napi.rs</a> is as easy as:</p>
<pre class="language-shell"><code class="language-shell code-highlight"><span class="code-line"><span class="token function">npm</span> <span class="token function">install</span> <span class="token parameter variable">-g</span> @napi-rs/cli
</span></code></pre>
<p>Followed by...</p>
<pre class="language-shell"><code class="language-shell code-highlight"><span class="code-line">napi new
</span></code></pre>
<p>... which is great. But I wanted to keep the project within a <a href="https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html">Cargo Workspace</a>
so I made the following changes (using the project name <code>bslive</code>, which stands for <a href="https://browsersync.io">Browsersync</a> Live, in case you wondered)</p>
<h2>Step 1</h2>
<p>Create a new directory at the root level of the project</p>
<pre class="language-shell"><code class="language-shell code-highlight"><span class="code-line"><span class="token function">mkdir</span> bslive
</span></code></pre>
<h2>Step 2</h2>
<p>Move the following files/folders</p>
<pre><code class="code-highlight"><span class="code-line">src/lib.rs
</span><span class="code-line">build.rs
</span><span class="code-line">Cargo.toml
</span></code></pre>
<p><strong>into</strong></p>
<pre><code class="code-highlight"><span class="code-line">bslive/src/lib.rs
</span><span class="code-line">bslive/build.rs
</span><span class="code-line">bslive/Cargo.toml
</span></code></pre>
<p>Should look something like:</p>
<div style="text-align:center"><img src="/cargo-workspace.png" style="display:inline-block"></div>
<h2>Step 3</h2>
<p>Now create a <em>new</em> <code>Cargo.toml</code> in the root of the project, to replace the one you just moved</p>
<p><strong><code>Cargo.toml</code></strong></p>
<pre class="language-toml"><code class="language-toml code-highlight"><span class="code-line"><span class="token key property">workspace.members</span> <span class="token punctuation">=</span> <span class="token punctuation">[</span><span class="token string">"bslive"</span><span class="token punctuation">]</span>
</span><span class="code-line"><span class="token key property">workspace.resolver</span> <span class="token punctuation">=</span> <span class="token string">"2"</span>
</span></code></pre>
<h2>Step 4</h2>
<p>The last is to tell <code>napi</code> where to build from (since it defaults to the root of the project).</p>
<p>In the <code>package.json</code> file, find the <code>scripts</code> section and change both <code>build</code> and <code>build:debug</code> to include the
flag <code>--cargo-name &lt;name&gt;</code></p>
<p><strong><code>package.json</code></strong></p>
<pre class="language-json"><code class="language-diff-json code-highlight"><span class="code-line deleted">-   <span class="token property">"build"</span><span class="token operator">:</span> <span class="token string">"napi build --platform --release"</span><span class="token punctuation">,</span>
</span><span class="code-line inserted">+   <span class="token property">"build"</span><span class="token operator">:</span> <span class="token string">"napi build --cargo-name bslive --platform --release"</span><span class="token punctuation">,</span>
</span></code></pre>
<pre class="language-json"><code class="language-diff-json code-highlight"><span class="code-line deleted">-   <span class="token property">"build:debug"</span><span class="token operator">:</span> <span class="token string">"napi build --platform"</span><span class="token punctuation">,</span>
</span><span class="code-line inserted">+   <span class="token property">"build:debug"</span><span class="token operator">:</span> <span class="token string">"napi build --cargo-name bslive --platform"</span><span class="token punctuation">,</span>
</span></code></pre>
<h2>Done!</h2>
<p>Now you can create lots of separate crates in the root of the project (or under a common subdirectory, like <code>crates</code>)
and the regular workspace improvements will apply :)</p>]]></content:encoded>
            <author>shane.osbourne@hey.com (Shane Osbourne)</author>
        </item>
        <item>
            <title><![CDATA[Re-Writing in Rust == faster with more features]]></title>
            <link>https://shane-o.dev/articles/rewrite+in+rust+faster+with+more+features</link>
            <guid>https://shane-o.dev/articles/rewrite+in+rust+faster+with+more+features</guid>
            <pubDate>Sun, 07 Apr 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[Making a feature faster than the node equivalent, whilst adding more capabilities<!-- -->
<p>At over 3.2m downloads a month from NPM, <a href="https://browsersync.io">https://browsersync.io</a> is still heavily used in the industry. As I continue to
modernise the implementation (by working on a Rust port) I came across an interesting case study
that became up to 5x faster in a real-world setting, whilst also allowing me to add additional features.</p>
<h2>Browsersync's proxy option</h2>
<p>One of my favourite features in Browsersync is the ability to run a <code>localhost</code> server, but proxy certain requests
through to a 3rd party domain.</p>
<pre class="language-md"><code class="language-md code-highlight"><span class="code-line">browserSync.init({
</span><span class="code-line">   proxy: "https://example.com"
</span><span class="code-line">});
</span></code></pre>
<p>This will give you back a localhost address that you use to make requests to, like <code>localhost:3000</code>. The NodeJS implementation
will then send each request on to the configured proxy target (<code>"https://example.com"</code> in this example), and return the
response as normal.</p>
<h2>Sounds easy so far...</h2>
<p>Actually, it's a bit more complicated than that 🤣. Browsersync wants more control over the response, so that it
can replace any links in the markup and inject an HTML snippet.</p>
<p>To do this in node is a bit of pain, and as I've come to realise, very, very slow too.</p>
<h3>How the current proxy works</h3>
<p>When a request comes in, for something like <code>localhost:3000/index.html</code>, Browsersync needs to forward that onto the
proxy target. But before it can do so, it has to mess around with a couple of things.</p>
<ul>
<li>it appends a header to the outgoing request that effectively disables any compression</li>
<li>it monkey patches the stream methods such that it can buffer the response body in memory</li>
<li>once the response is fully buffered, string replacements are performed</li>
</ul>
<p>Outgoing requests get transformed like this:</p>
<pre class="language-http"><code class="language-http code-highlight"><span class="code-line">GET localhost:3000/index.html
</span><span class="code-line"><span class="token header"><span class="token header-name keyword">Accept-Encoding</span><span class="token punctuation">:</span> <span class="token header-value">gzip, deflate, br, zstd</span></span>
</span><span class="code-line">
</span><span class="code-line"># becomes...
</span><span class="code-line">
</span><span class="code-line">GET https://example.com/index.html
</span><span class="code-line"><span class="token header"><span class="token header-name keyword">Accept-Encoding</span><span class="token punctuation">:</span> <span class="token header-value">identity</span></span>
</span></code></pre>
<p>and incoming responses have the equivalent of this applied (of course, it's not exactly this, but you see the point):</p>
<pre class="language-js"><code class="language-js code-highlight"><span class="code-line"><span class="token keyword">const</span> buffered_body <span class="token operator">=</span> <span class="token keyword control-flow">await</span> req<span class="token punctuation">.</span><span class="token method function property-access">get</span><span class="token punctuation">(</span><span class="token string">"example.com"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</span><span class="code-line"><span class="token keyword">const</span> altered_html <span class="token operator">=</span> buffered_body<span class="token punctuation">.</span><span class="token method function property-access">replaceAll</span><span class="token punctuation">(</span><span class="token string">"example.com"</span><span class="token punctuation">,</span> <span class="token string">"localhost:3000"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</span><span class="code-line">res<span class="token punctuation">.</span><span class="token method function property-access">write</span><span class="token punctuation">(</span>altered_html<span class="token punctuation">)</span>
</span></code></pre>
<p>It's a really cool feature, because you can combine it with local static assets, optionally overriding individual
requests. Imagine a remotely hosted Wordpress site, and you just want to tinker with the CSS without having to set up
an entire environment? Those are the kinds of weird and wonderful workflows that our community use Browsersync for 😍</p>
<h2>Was it fast though?</h2>
<p>Part of the reason for sending <code>Accept-Encoding: identity</code> in the outgoing request, is to force the 3rd party to <em>not</em>
send a gzip/brottli response. It makes the body a bit larger, but it means that we don't need to do the decompression
on the node layer, so in theory the proxy method should be fairly fast.</p>
<p>In real world scenarios though, we see that the proxy introduces a noticeable slowdown.</p>
<p>We can use the Browsersync homepage as a nice, simple example, because it's served from Netlify's CDNs and
the response bodies are all encoding with brottli.</p>
<p>If we run the following command, we can take a look at some simple metrics</p>
<pre class="language-sh"><code class="language-sh code-highlight"><span class="code-line">browser-sync https//browsersync.io
</span></code></pre>
<p>Now we can access <code>localhost:3000</code> and all requests will be forwarded onto <code>https//browsersync.io</code>, but
with the modifications mentioned above.</p>
<table><thead><tr><th></th><th align="left">baseline<br><code>https://browsersync.io</code></th><th>proxy in node js<br><code>http://localhost</code></th></tr></thead><tbody><tr><td>load time</td><td align="left">250-320ms</td><td><strong>780-900ms</strong></td></tr><tr><td>page weight</td><td align="left">368kb</td><td><strong>562kb</strong></td></tr><tr><td>compression</td><td align="left">✅</td><td>❌</td></tr></tbody></table>
<p>The page weight is much larger since we're opting out of compression for the reasons mentioned above, but it's striking
to me just how much slower the overall page load time is. 😭</p>
<h2>Enter the Rust-Rewrite</h2>
<p>Even though it's still an ongoing effort, I have the Rust implementation far enough along now to be able to make initial comparisons.
The short version is that the new Rust based proxy introduces no noticeable overhead, whilst also supporting compression on both
ends of the proxy 💪</p>
<table><thead><tr><th></th><th align="left">baseline</th><th align="left">proxy in rust</th><th align="left">proxy in node js</th></tr></thead><tbody><tr><td>load time</td><td align="left">250-320ms</td><td align="left">250-320ms</td><td align="left"><strong>780-900ms</strong></td></tr><tr><td>page weight</td><td align="left">368kb</td><td align="left">368kb</td><td align="left"><strong>562kb</strong></td></tr><tr><td>compression</td><td align="left">✅</td><td align="left">✅</td><td align="left">❌</td></tr></tbody></table>
<p>So the rust version is much, much faster and is easier to apply compression/decompression where needed.</p>
<p>This is just the tip of the iceberg though, since the composition model in the <a href="https://docs.rs/axum/latest/axum/">axum</a> and <a href="https://docs.rs/tower/latest/tower/">tower</a> ecosystem is so strong
that it doesn't require the monkey-patching of any request/response stream writers like it does in node 😍</p>
<h2>Looking forward</h2>
<p>Although it's fun to jump on the 're-write it Rust' bandwagon, there are some other reasons improving the proxy
element of Browsersync is so important.</p>
<p>If you have a close-to-zero-cost local development proxy, then you can start applying more modifications
to the requests/responses without the fear that it's going to grind to a halt.</p>
<p>Some of the recent examples I've been playing with whilst developing the proxy is recording and then playing back streaming
responses from LLM providers 👌 💻</p>]]></content:encoded>
            <author>shane.osbourne@hey.com (Shane Osbourne)</author>
        </item>
        <item>
            <title><![CDATA[Shrinking a Rust binary output from 18mb to 7mb]]></title>
            <link>https://shane-o.dev/articles/rust-binary-size</link>
            <guid>https://shane-o.dev/articles/rust-binary-size</guid>
            <pubDate>Tue, 05 Nov 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h2>Simple tweaks to the options in the release profile can dramatically reduce the footprint of the resulting binary</h2>
<p>After spotting an <strong>18mb</strong> binary lurking in the <code>node_modules</code> folder of a project I'd released with <code>napi-rs</code>, I was
curious about how much I could reduce it.</p>
<p>I was convinced of two things:</p>
<ul>
<li>First, that I'd only be able to trim a megabyte here or there with a combo of various release options.</li>
<li>Second, that any larger wins would come from considering which 3rd-party crates I'm using and swapping them out for lighter weight ones.</li>
</ul>
<p>In the end, I was so wrong on the first part, that I thought it was worth sharing in this post. If I end up tweaking 3rd party dependencies
to further reduce binary size, I'll be sure to document that process too.</p>
<h2>Journey</h2>
<p>I was reading this post <a href="https://tech.dreamleaves.org/trimming-down-a-rust-binary-in-half/">Trimming down a rust binary in half</a>
, but it seemed like swapping out dependencies was a much bigger task than I wanted to take on right now. The post eventually links
out to <a href="https://github.com/johnthagen/min-sized-rust">min-sized-rust</a> though—so I started copy/pasting options from there seeing where it led me 🤣.</p>
<h2>Impact</h2>
<p>Well, with zero changes to dependencies, I cut 11mb from the binary size! I've documented each option I added and what difference it made to the output size 🤌</p>
<p>In each case, I'm just adding another line within the <code>[profile.release]</code> section of Cargo.toml, and re-running this:</p>
<pre class="language-shell"><code class="language-shell code-highlight"><span class="code-line"><span class="token function">cargo</span> build <span class="token parameter variable">--release</span>
</span></code></pre>
<blockquote>
<p>Note: If you're using <code>napi-rs</code>, these same optimizations will apply when you run the relevant build command ✅</p>
</blockquote>
<table><thead><tr><th></th><th>Cargo Options Applied</th><th>Binary Size</th><th>Difference</th></tr></thead><tbody><tr><td>1</td><td>None</td><td>18mb</td><td>-</td></tr><tr><td>2</td><td><code>[profile.release]</code><br><code>strip = true</code></td><td>14mb</td><td>-4mb</td></tr><tr><td>3</td><td><code>[profile.release]</code><br><code>strip = true</code><br> <code>opt-level = "z"</code></td><td>12mb</td><td>-2mb</td></tr><tr><td>4</td><td><code>[profile.release]</code><br><code>strip = true</code><br> <code>opt-level = "z"</code><br> <code>lto = true</code></td><td>8.1mb</td><td>-3.9mb</td></tr><tr><td>5</td><td><code>[profile.release]</code><br><code>strip = true</code><br> <code>opt-level = "z"</code><br> <code>lto = true</code><br> <code>codegen-units = 1</code></td><td>7.9mb</td><td>-0.2mb</td></tr><tr><td>6</td><td><code>[profile.release]</code><br><code>strip = true</code><br> <code>opt-level = "z"</code><br> <code>lto = true</code><br> <code>codegen-units = 1</code><br> <code>panic = "abort"</code></td><td>✅ <strong>7.0mb</strong></td><td>-0.9mb</td></tr></tbody></table>
<h2>Result - 11mb smaller overall!</h2>
<p>Now, when I publish my program to <code>npm</code>, users are only downloading a 7mb binary, compared to an 18mb one 🎉. Of course, I can further
shrink this—I'm not exactly happy with it being 7mb, but I did find it interesting that with zero code/dependency changes I was still
able to shrink the size so much!</p>
<p><strong>Note:</strong> In my use-case, I'm optimizing for size over performance (with <code>opt-level = "z"</code>) since I'm building a development server where
raw-performance is not the primary concern</p>
<h2>Copy/Paste</h2>
<pre class="language-toml"><code class="language-toml code-highlight"><span class="code-line"><span class="token punctuation">[</span><span class="token table class-name">profile.release</span><span class="token punctuation">]</span>
</span><span class="code-line"><span class="token key property">strip</span> <span class="token punctuation">=</span> <span class="token boolean">true</span>
</span><span class="code-line"><span class="token key property">opt-level</span> <span class="token punctuation">=</span> <span class="token string">"z"</span>
</span><span class="code-line"><span class="token key property">lto</span> <span class="token punctuation">=</span> <span class="token boolean">true</span>
</span><span class="code-line"><span class="token key property">codegen-units</span> <span class="token punctuation">=</span> <span class="token number">1</span>
</span><span class="code-line"><span class="token key property">panic</span> <span class="token punctuation">=</span> <span class="token string">"abort"</span>
</span></code></pre>
<h2>Legend: Explanation of Cargo Options (this part is from ChatGPT)</h2>
<table><thead><tr><th>Option</th><th>Explanation</th></tr></thead><tbody><tr><td><code>strip = true</code></td><td>Removes all debug symbols from the binary, which reduces the size by eliminating unused metadata.</td></tr><tr><td><code>opt-level = "z"</code></td><td>Optimizes for binary size instead of performance, producing a smaller binary at the cost of runtime speed.</td></tr><tr><td><code>lto = true</code></td><td>Enables Link Time Optimization, which performs cross-module optimization at the linking stage, reducing size and improving performance.</td></tr><tr><td><code>codegen-units = 1</code></td><td>Limits the compiler to use a single code generation unit, allowing for better optimization across the entire crate at the expense of compilation time.</td></tr><tr><td><code>panic = "abort"</code></td><td>Configures the binary to abort on panic instead of unwinding, saving space by not including unwinding code.</td></tr></tbody></table>]]></content:encoded>
            <author>shane.osbourne@hey.com (Shane Osbourne)</author>
        </item>
        <item>
            <title><![CDATA[Typescript and JSDoc, shared names for Types + Values]]></title>
            <link>https://shane-o.dev/articles/shared-names-types-values</link>
            <guid>https://shane-o.dev/articles/shared-names-types-values</guid>
            <pubDate>Fri, 22 Sep 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>(written by a human, no AI content)</p>
<p>I saw this pattern recently, taking advantage of the fact that Typescript doesn't mind if you re-use the same name for a value <em>and</em> a type.</p>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token keyword">import</span> <span class="token operator">*</span> <span class="token keyword">as</span> z <span class="token keyword">from</span> <span class="token string">"zod"</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token comment">// Define a schema</span>
</span><span class="code-line"><span class="token keyword">export</span> <span class="token keyword">const</span> User <span class="token operator">=</span> z<span class="token punctuation">.</span><span class="token function">object</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
</span><span class="code-line">    name<span class="token operator">:</span> z<span class="token punctuation">.</span><span class="token function">string</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
</span><span class="code-line"><span class="token punctuation">}</span><span class="token punctuation">)</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token comment">// export an inferred type for use elsewhere</span>
</span><span class="code-line"><span class="token comment">// but, using the same name, `User` 🤯</span>
</span><span class="code-line"><span class="token keyword">export</span> <span class="token keyword">type</span> <span class="token class-name">User</span> <span class="token operator">=</span> z<span class="token punctuation">.</span>infer<span class="token operator">&lt;</span><span class="token keyword">typeof</span> User<span class="token operator">&gt;</span>
</span></code></pre>
<p>So we have...</p>
<ul>
<li><code>export const User ...</code></li>
<li><code>export type User...</code></li>
</ul>
<p>... it might seem odd, but Typescript is more than happy with this - after all, types and values are distinct! So, providing that you're happy with any potential confusion in the future , it's a pattern you can use right away!</p>
<p>Why would you though? Well, it might make more sense in the context of a consumer - consider the following in a separate module:</p>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token keyword">import</span> <span class="token punctuation">{</span> User <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"./zod"</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token keyword">function</span> <span class="token function">signIn</span><span class="token punctuation">(</span>user<span class="token operator">:</span> User<span class="token punctuation">)</span> <span class="token punctuation">{</span>
</span><span class="code-line">    <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>user<span class="token punctuation">)</span><span class="token punctuation">;</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>
<p>Here we have a single import: <code>User</code>. We know the module exports both a <strong>type</strong> and a <strong>value</strong> with that name - but when seen in type parameter position like this, Typescript will just use the <em>type</em> <code>User</code> without issue. A naming 'collision' just doesn't occur.</p>
<p>In that very same file, we could also do:</p>
<pre class="language-ts"><code class="language-ts code-highlight"><span class="code-line"><span class="token comment">// same file, different context</span>
</span><span class="code-line"><span class="token keyword">const</span> parsed <span class="token operator">=</span> User<span class="token punctuation">.</span><span class="token function">safeParse</span><span class="token punctuation">(</span><span class="token punctuation">{</span> name<span class="token operator">:</span> <span class="token string">"Shane"</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
</span></code></pre>
<p>which is nice - because now we're just in regular JavaScript territory here, using the <em>value</em> called <code>User</code>. Typescript understands that we're not in a type position, so everything 'just works' and we didn't need to think of a separate name for it, like <code>UserSchema</code> or similar. 🥰</p>
<h2>JSDoc</h2>
<p>All of the examples above were in Typescript - but if you're using JSDoc this works almost identically.</p>
<p>First, instead of exporting both of these...</p>
<ul>
<li><code>export const User ...</code></li>
<li><code>export type User ...</code></li>
</ul>
<p>we'll need to replace the second one - since <code>export type ...</code> is only supported in Typescript.</p>
<p>To do this, albeit in a less-than-ideal way, we can use a <code>@typedef</code></p>
<pre class="language-js"><code class="language-js code-highlight"><span class="code-line"><span class="token keyword module">import</span> <span class="token imports"><span class="token operator">*</span> <span class="token keyword module">as</span> z</span> <span class="token keyword module">from</span> <span class="token string">"zod"</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token keyword module">export</span> <span class="token keyword">const</span> <span class="token maybe-class-name">User</span> <span class="token operator">=</span> z<span class="token punctuation">.</span><span class="token method function property-access">object</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
</span><span class="code-line">    <span class="token literal-property property">name</span><span class="token operator">:</span> z<span class="token punctuation">.</span><span class="token method function property-access">string</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
</span><span class="code-line"><span class="token punctuation">}</span><span class="token punctuation">)</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token doc-comment comment">/**
</span></span><span class="code-line"><span class="token doc-comment comment"> * <span class="token keyword">@typedef</span> <span class="token class-name"><span class="token punctuation">{</span><span class="token keyword">import</span><span class="token punctuation">(</span><span class="token string">"zod"</span><span class="token punctuation">)</span><span class="token punctuation">.</span>infer<span class="token punctuation">&lt;</span><span class="token keyword">typeof</span> User<span class="token punctuation">&gt;</span><span class="token punctuation">}</span></span> <span class="token class-name">User</span>
</span></span><span class="code-line"><span class="token doc-comment comment"> */</span>
</span></code></pre>
<p>Using <code>infer</code> from Zod, we're achieving the exact same inference as we did in Typescript - it's just that it's inside a comment this time instead!🥹.</p>
<p><code>@typedef</code> isn't ideal for all situations (for another time...), but in this case it has exactly the effect we're looking for.</p>
<p>Considering a consumer in JS, as we did with the Typescript example, it would look like this:</p>
<pre class="language-javascript"><code class="language-javascript code-highlight"><span class="code-line"><span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> <span class="token maybe-class-name">User</span> <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">"./jsdoc-zod"</span><span class="token punctuation">;</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token doc-comment comment">/**
</span></span><span class="code-line"><span class="token doc-comment comment"> * <span class="token keyword">@param</span> <span class="token class-name"><span class="token punctuation">{</span>User<span class="token punctuation">}</span></span> <span class="token parameter">user</span>
</span></span><span class="code-line"><span class="token doc-comment comment"> */</span>
</span><span class="code-line"><span class="token keyword">function</span> <span class="token function">signIn</span><span class="token punctuation">(</span><span class="token parameter">user</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
</span><span class="code-line">    <span class="token console class-name">console</span><span class="token punctuation">.</span><span class="token method function property-access">log</span><span class="token punctuation">(</span>user<span class="token punctuation">)</span><span class="token punctuation">;</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>
<p>Again, note we're only importing <code>User</code> - which is both a type <em>and</em> a value in the other module. Typescript still has no issue understanding that within a <code>@param</code> block, we are referring to the type and not the value.</p>
<p>That means the previous "in the same file" example works just as it did before, presented here as a single snippet:</p>
<pre class="language-js"><code class="language-js code-highlight"><span class="code-line"><span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> <span class="token maybe-class-name">User</span> <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">"./jsdoc-zod"</span><span class="token punctuation">;</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token doc-comment comment">/**
</span></span><span class="code-line"><span class="token doc-comment comment"> * <span class="token keyword">@param</span> <span class="token class-name"><span class="token punctuation">{</span>User<span class="token punctuation">}</span></span> <span class="token parameter">user</span> - works as expected in type position 🥰
</span></span><span class="code-line"><span class="token doc-comment comment"> */</span>
</span><span class="code-line"><span class="token keyword">function</span> <span class="token function">signIn</span><span class="token punctuation">(</span><span class="token parameter">user</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
</span><span class="code-line">    <span class="token console class-name">console</span><span class="token punctuation">.</span><span class="token method function property-access">log</span><span class="token punctuation">(</span>user<span class="token punctuation">)</span><span class="token punctuation">;</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token comment">// works as expected as a value too 🥰</span>
</span><span class="code-line"><span class="token keyword">const</span> parsed <span class="token operator">=</span> <span class="token maybe-class-name">User</span><span class="token punctuation">.</span><span class="token method function property-access">safeParse</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">"shane"</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
</span></code></pre>
<h2>Conclusion</h2>
<p>It's becoming table-stakes for any schema parsing library like Zod to support rich type inference. Having types derived from runtime validation code is a powerful pattern, one that I can see growing in popularity.</p>
<p>This post highlights how you can use a single name for a value and a type - and how it can be done in JSDoc along with Typescript.</p>]]></content:encoded>
            <author>shane.osbourne@hey.com (Shane Osbourne)</author>
        </item>
        <item>
            <title><![CDATA[Test]]></title>
            <link>https://shane-o.dev/articles/test</link>
            <guid>https://shane-o.dev/articles/test</guid>
            <pubDate>Fri, 22 Sep 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<div data-pull="true" data-mdx-next="pre"></div>
<pre class="language-tsx"><code class="language-tsx code-highlight"><span class="code-line"><span class="token keyword">import</span> <span class="token punctuation">{</span> <span class="token keyword">type</span> <span class="token class-name">Metadata</span> <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'next'</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token keyword">import</span> <span class="token imports"><span class="token punctuation">{</span> <span class="token maybe-class-name">Providers</span> <span class="token punctuation">}</span></span> <span class="token keyword">from</span> <span class="token string">'@/app/providers'</span>
</span><span class="code-line"><span class="token keyword">import</span> <span class="token imports"><span class="token punctuation">{</span> <span class="token maybe-class-name">Layout</span> <span class="token punctuation">}</span></span> <span class="token keyword">from</span> <span class="token string">'@/components/Layout'</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token keyword">import</span> <span class="token string">'@/styles/tailwind.css'</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token keyword">export</span> <span class="token keyword">const</span> metadata<span class="token operator">:</span> <span class="token maybe-class-name">Metadata</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
</span><span class="code-line">  title<span class="token operator">:</span> <span class="token punctuation">{</span>
</span><span class="code-line">    template<span class="token operator">:</span> <span class="token string">'%s - Shane Osbourne'</span><span class="token punctuation">,</span>
</span><span class="code-line">    <span class="token keyword">default</span><span class="token operator">:</span>
</span><span class="code-line">      <span class="token string">'Shane Osbourne - Software designer, founder, and amateur astronaut'</span><span class="token punctuation">,</span>
</span><span class="code-line">  <span class="token punctuation">}</span><span class="token punctuation">,</span>
</span><span class="code-line">  description<span class="token operator">:</span> <span class="token string">'I’m Shane, a software engineer from Nottinghamshire, England'</span><span class="token punctuation">,</span>
</span><span class="code-line">  alternates<span class="token operator">:</span> <span class="token punctuation">{</span>
</span><span class="code-line highlight-line">    types<span class="token operator">:</span> <span class="token punctuation">{</span>
</span><span class="code-line highlight-line">      <span class="token string-property property">'application/rss+xml'</span><span class="token operator">:</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">NEXT_PUBLIC_SITE_URL</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">/feed.xml</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span>
</span><span class="code-line highlight-line">    <span class="token punctuation">}</span><span class="token punctuation">,</span>
</span><span class="code-line">  <span class="token punctuation">}</span><span class="token punctuation">,</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">RootLayout</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
</span><span class="code-line">  children<span class="token punctuation">,</span>
</span><span class="code-line"><span class="token punctuation">}</span><span class="token operator">:</span> <span class="token punctuation">{</span>
</span><span class="code-line">  children<span class="token operator">:</span> <span class="token maybe-class-name">React</span><span class="token punctuation">.</span><span class="token property-access"><span class="token maybe-class-name">ReactNode</span></span>
</span><span class="code-line"><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
</span><span class="code-line">  <span class="token keyword">return</span> <span class="token punctuation">(</span>
</span><span class="code-line">    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>h-full antialiased<span class="token punctuation">"</span></span> <span class="token attr-name">suppressHydrationWarning</span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
</span></span><span class="code-line"><span class="token plain-text">      </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>flex h-full bg-zinc-50 dark:bg-black<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
</span></span><span class="code-line"><span class="token plain-text">        </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Providers</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
</span></span><span class="code-line"><span class="token plain-text">          </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>flex w-full<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
</span></span><span class="code-line"><span class="token plain-text">            </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Layout</span></span><span class="token punctuation">&gt;</span></span><span class="token punctuation">{</span>children<span class="token punctuation">}</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">Layout</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
</span></span><span class="code-line"><span class="token plain-text">          </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
</span></span><span class="code-line"><span class="token plain-text">        </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">Providers</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
</span></span><span class="code-line"><span class="token plain-text">      </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
</span></span><span class="code-line"><span class="token plain-text">    </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>html</span><span class="token punctuation">&gt;</span></span>
</span><span class="code-line">  <span class="token punctuation">)</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>]]></content:encoded>
            <author>shane.osbourne@hey.com (Shane Osbourne)</author>
        </item>
        <item>
            <title><![CDATA[Unwrap a value with the question mark operator in Rust]]></title>
            <link>https://shane-o.dev/articles/unwrap-a-value</link>
            <guid>https://shane-o.dev/articles/unwrap-a-value</guid>
            <pubDate>Fri, 02 Sep 2022 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>What's wrong with the following function in Rust?</p>
<pre class="language-rust"><code class="language-rust code-highlight"><span class="code-line"><span class="token keyword">fn</span> <span class="token function-definition function">measure_cargo_toml</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token keyword">usize</span> <span class="token punctuation">{</span>
</span><span class="code-line">    <span class="token keyword">let</span> toml <span class="token operator">=</span> <span class="token namespace">std<span class="token punctuation">::</span>fs<span class="token punctuation">::</span></span><span class="token function">read_to_string</span><span class="token punctuation">(</span><span class="token string">"Cargo.toml"</span><span class="token punctuation">)</span><span class="token operator">?</span><span class="token punctuation">;</span>
</span><span class="code-line">    toml<span class="token punctuation">.</span><span class="token function">len</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>
<p>Putting whether this is an optimal way to read a file's length to one side for a moment,
let's take a look into why this causes a compile-time error.</p>
<p><code>std::fs::read_to_string("Cargo.toml")</code> returns <code>Result&lt;String, std::io::Error&gt;</code> and
we tried to use the question mark operator <code>?</code> to 'unwrap' the <code>String</code> part that we really care about.</p>
<p>We're faced with the following error however:</p>
<pre><code class="code-highlight"><span class="code-line">cannot use the `?` operator in a function that returns `()`
</span><span class="code-line">this function should return `Result` or `Option` to accept `?`
</span></code></pre>
<p>This is suggesting that the <code>?</code> is doing a bit more than simply 'unwrapping' a value -
why's it mentioning the return type at all?</p>
<p>To understand, let's try to <em>unwrap</em> that <code>String</code> value a couple of other ways...</p>
<h3><code>if let</code> patterns</h3>
<p>If we only ever cared about the success case, then technically we could have done the following instead:</p>
<pre class="language-rust"><code class="language-rust code-highlight"><span class="code-line line-number" line="1"><span class="token keyword">fn</span> <span class="token function-definition function">measure_cargo_toml</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token keyword">usize</span> <span class="token punctuation">{</span>
</span><span class="code-line line-number highlight-line" line="2">    <span class="token keyword">if</span> <span class="token keyword">let</span> <span class="token class-name">Ok</span><span class="token punctuation">(</span>toml<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token namespace">std<span class="token punctuation">::</span>fs<span class="token punctuation">::</span></span><span class="token function">read_to_string</span><span class="token punctuation">(</span><span class="token string">"Cargo.toml"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
</span><span class="code-line line-number" line="3">        toml<span class="token punctuation">.</span><span class="token function">len</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
</span><span class="code-line line-number" line="4">    <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
</span><span class="code-line line-number highlight-line" line="5">        <span class="token number">0</span>
</span><span class="code-line line-number" line="6">    <span class="token punctuation">}</span>
</span><span class="code-line line-number" line="7"><span class="token punctuation">}</span>
</span></code></pre>
<p>Notes:</p>
<ul>
<li>(line <strong>2</strong>): we're performing a pattern-match on the return value from <code>read_to_string</code> here. The code within the
<code>if</code> block will only execute if the pattern <code>Ok(toml)</code> is a match against the result.</li>
<li>(line <strong>5</strong>) It looks like our design is breaking down here. To keep the type checker happy we're having to
use a 'default' value of <code>0</code> when the file reading fails. This is far from ideal since <code>0</code> could actually be a valid value,
but more so because it doesn't correctly encode what our function is capable of.</li>
</ul>
<p>Rust can help us here though, we can use the type system to encode more meaning into our function signatures -
in this case the presence vs absence of a value can be communicated through the use of the <code>Option</code> enum.</p>
<p>If we change our return type to <code>Option&lt;usize&gt;</code>, then we can return a <code>None</code> in place of the <code>0</code> and
callers of this function will have a better understanding about how this works.</p>
<pre class="language-rust"><code class="language-rust code-highlight"><span class="code-line line-number" line="1"><span class="token keyword">fn</span> <span class="token function-definition function">measure_cargo_toml</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Option</span><span class="token operator">&lt;</span><span class="token keyword">usize</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
</span><span class="code-line line-number" line="2">    <span class="token keyword">if</span> <span class="token keyword">let</span> <span class="token class-name">Ok</span><span class="token punctuation">(</span>toml<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token namespace">std<span class="token punctuation">::</span>fs<span class="token punctuation">::</span></span><span class="token function">read_to_string</span><span class="token punctuation">(</span><span class="token string">"Cargo.toml"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
</span><span class="code-line line-number" line="3">        <span class="token class-name">Some</span><span class="token punctuation">(</span>toml<span class="token punctuation">.</span><span class="token function">len</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
</span><span class="code-line line-number" line="4">    <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
</span><span class="code-line line-number" line="5">        <span class="token class-name">None</span>
</span><span class="code-line line-number" line="6">    <span class="token punctuation">}</span>
</span><span class="code-line line-number" line="7"><span class="token punctuation">}</span>
</span></code></pre>
<p>Notes:</p>
<ul>
<li>(line <strong>1</strong>) we changed the return type from <code>usize</code> to <code>Option&lt;usize&gt;</code></li>
<li>(line <strong>3</strong>) because of that, we now need to wrap our return value in the <code>Some</code> variant</li>
<li>(line <strong>5</strong>) <code>None</code> here makes our program more explicit than the previous 'default' value of <code>0</code></li>
</ul>
<h2>That's a lot of boilerplate though...</h2>
<p>Converting from <code>Result&lt;T, _&gt;</code> to an <code>Option&lt;T&gt;</code> is a fairly a common operation, and
the standard library comes with a <code>.ok()</code> method to help us reduce a bit of boilerplate:</p>
<pre class="language-rust"><code class="language-rust code-highlight"><span class="code-line line-number" line="1"><span class="token keyword">pub</span> <span class="token keyword">fn</span> <span class="token function-definition function">measure_cargo_toml</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Option</span><span class="token operator">&lt;</span><span class="token keyword">usize</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
</span><span class="code-line line-number" line="2">    <span class="token namespace">std<span class="token punctuation">::</span>fs<span class="token punctuation">::</span></span><span class="token function">read_to_string</span><span class="token punctuation">(</span><span class="token string">"Cargo.toml"</span><span class="token punctuation">)</span> <span class="token comment">// Result&lt;String, io::Error&gt;</span>
</span><span class="code-line line-number highlight-line" line="3">        <span class="token punctuation">.</span><span class="token function">ok</span><span class="token punctuation">(</span><span class="token punctuation">)</span>                             <span class="token comment">// Option&lt;String&gt;</span>
</span><span class="code-line line-number highlight-line" line="4">        <span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token closure-params"><span class="token closure-punctuation punctuation">|</span>toml<span class="token closure-punctuation punctuation">|</span></span> toml<span class="token punctuation">.</span><span class="token function">len</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>           <span class="token comment">// Option&lt;usize&gt;</span>
</span><span class="code-line line-number" line="5"><span class="token punctuation">}</span>
</span></code></pre>
<p>Notes:</p>
<ul>
<li>Calling <code>.ok()</code> gives us <code>Option&lt;String&gt;</code> or <code>None</code> depending on whether <code>read_to_string</code> was successful -
it's just a convenience method and allows us to call <code>.map()</code> to change the inner value, if it exists.</li>
</ul>
<p>This is a pretty slick example, it's one of the reasons I enjoy Rust so much 🦀</p>
<hr>
<p>But wait a minute - that's ignoring any errors that might occur when reading the file - what if we wanted
to handle that error by passing it on to callers, or even just logging out the error?</p>
<p>To do this, we need to think about our design again. Currently, our signature is this:</p>
<pre class="language-rust"><code class="language-rust code-highlight"><span class="code-line"><span class="token keyword">pub</span> <span class="token keyword">fn</span> <span class="token function-definition function">measure_cargo_toml</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Option</span><span class="token operator">&lt;</span><span class="token keyword">usize</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
</span><span class="code-line">    <span class="token comment">// snip</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>
<p>We can produce a <code>usize</code> value, or not - that's it. Our design doesn't incorporate error
handling at all. So let's change that.</p>
<p>We can alter our return type to be the following instead:</p>
<pre class="language-rust"><code class="language-diff-rust code-highlight"><span class="code-line deleted"><span class="token operator">-</span><span class="token keyword">pub</span> <span class="token keyword">fn</span> <span class="token function-definition function">measure_cargo_toml</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Option</span><span class="token operator">&lt;</span><span class="token keyword">usize</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
</span><span class="code-line inserted"><span class="token operator">+</span><span class="token keyword">pub</span> <span class="token keyword">fn</span> <span class="token function-definition function">measure_cargo_toml</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Result</span><span class="token operator">&lt;</span><span class="token keyword">usize</span><span class="token punctuation">,</span> <span class="token namespace">std<span class="token punctuation">::</span>io<span class="token punctuation">::</span></span><span class="token class-name">Error</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
</span><span class="code-line">    <span class="token comment">// snip</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>
<p>With this change, callers of our function will now be forced to handle the fact that our function can fail.
They may decide (as we did before) to ignore the error and use a default value, but at least this design
gives them that choice. <code>Option&lt;usize&gt;</code> loses all information if something goes wrong, and we don't want that.</p>
<p>So, to update our implementation to forward any errors, we can do the following instead:</p>
<p><strong>The re-wrapping method</strong></p>
<div data-mdx-next="pre" data-focus="true"></div>
<pre class="language-rust"><code class="language-rust code-highlight"><span class="code-line"><span class="token keyword">pub</span> <span class="token keyword">fn</span> <span class="token function-definition function">measure_cargo_toml</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Result</span><span class="token operator">&lt;</span><span class="token keyword">usize</span><span class="token punctuation">,</span> <span class="token namespace">std<span class="token punctuation">::</span>io<span class="token punctuation">::</span></span><span class="token class-name">Error</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
</span><span class="code-line">    <span class="token keyword">match</span> <span class="token namespace">std<span class="token punctuation">::</span>fs<span class="token punctuation">::</span></span><span class="token function">read_to_string</span><span class="token punctuation">(</span><span class="token string">"Cargo.toml"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
</span><span class="code-line highlight-line">        <span class="token class-name">Ok</span><span class="token punctuation">(</span>toml<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token class-name">Ok</span><span class="token punctuation">(</span>toml<span class="token punctuation">.</span><span class="token function">len</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
</span><span class="code-line highlight-line">        <span class="token class-name">Err</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token class-name">Err</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span>
</span><span class="code-line">    <span class="token punctuation">}</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>
<ul>
<li>
<p>When we match on the <code>Ok</code> variant, we get a local binding to the value contained inside, named <code>toml</code> here, then because <em>our</em> return
type is also a <code>Result</code>, we need to <code>Ok</code>-wrap the value we get from calling <code>toml.len()</code></p>
</li>
<li>
<p>For the error case we're just re-wrapping the error since it matches our type <code>std::io::Error</code></p>
</li>
</ul>
<p>That's quite a bit of syntax and ceremony for what is essentially a 'change the value inside the box' operation though - but fear not,
Rust has yet more convenience methods to help with situations like this.</p>
<p><strong>The <code>.map()</code> method on <code>Result</code></strong></p>
<p>We previously saw <code>.map</code> being used to alter the value inside a <code>Option</code> - well it turns out
that we can perform the same type of operation on Result too.</p>
<pre class="language-rust"><code class="language-rust code-highlight"><span class="code-line"><span class="token keyword">pub</span> <span class="token keyword">fn</span> <span class="token function-definition function">measure_cargo_toml</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Result</span><span class="token operator">&lt;</span><span class="token keyword">usize</span><span class="token punctuation">,</span> <span class="token namespace">std<span class="token punctuation">::</span>io<span class="token punctuation">::</span></span><span class="token class-name">Error</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
</span><span class="code-line">    <span class="token keyword">let</span> result <span class="token operator">=</span> <span class="token namespace">std<span class="token punctuation">::</span>fs<span class="token punctuation">::</span></span><span class="token function">read_to_string</span><span class="token punctuation">(</span><span class="token string">"Cargo.toml"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</span><span class="code-line">    result<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token closure-params"><span class="token closure-punctuation punctuation">|</span>s<span class="token closure-punctuation punctuation">|</span></span> s<span class="token punctuation">.</span><span class="token function">len</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>
<ul>
<li><code>read_to_string</code> returns <code>Result&lt;String, std::io::Error&gt;</code> - that's <em>very</em> close to what we want. The error part
matches our signature, but the value part does not.</li>
<li>To change <em>just</em> the value then, without explicitly unwrapping it, we
can use the <code>.map</code> method as seen here. This will only execute the closure on the value type if the result is of the <code>Ok</code> variant.
It's like opening a box, checking that everything inside is ok, then replacing the value and closing the box again.</li>
</ul>
<p>So that's a couple of techniques that we can use to handle possible errors and read and return different values
where there are none.</p>
<h2>Understanding use cases for the <code>?</code> operator</h2>
<p>So far though, our implementations have only contained 2 operations</p>
<ul>
<li>read a file from disk into a string</li>
<li>return the amount of bytes that make up that string.</li>
</ul>
<p>Because of this, we've been able to write a couple of different solutions that didn't require much
more than 'reaching into a box' to change a value.</p>
<p>However, there are situations where this 'unwrapping' and 're-wrapping' of values is either tedious or just
completely overkill for the task at hand.</p>
<p>For example, if we changed our requirement to instead return the combined length from <code>Cargo.toml</code> + <code>Cargo.lock</code>, then we might
end up with a solution such as:</p>
<pre class="language-rust"><code class="language-rust code-highlight"><span class="code-line"><span class="token keyword">pub</span> <span class="token keyword">fn</span> <span class="token function-definition function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Result</span><span class="token operator">&lt;</span><span class="token keyword">usize</span><span class="token punctuation">,</span> <span class="token namespace">std<span class="token punctuation">::</span>io<span class="token punctuation">::</span></span><span class="token class-name">Error</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
</span><span class="code-line">    <span class="token keyword">let</span> toml <span class="token operator">=</span> <span class="token function">read_to_string</span><span class="token punctuation">(</span><span class="token string">"Cargo.toml"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</span><span class="code-line">    <span class="token keyword">let</span> lock <span class="token operator">=</span> <span class="token function">read_to_string</span><span class="token punctuation">(</span><span class="token string">"Cargo.lock"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</span><span class="code-line">    <span class="token keyword">let</span> <span class="token keyword">mut</span> count <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
</span><span class="code-line">    <span class="token keyword">match</span> toml <span class="token punctuation">{</span>
</span><span class="code-line">        <span class="token class-name">Ok</span><span class="token punctuation">(</span><span class="token keyword">str</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> count <span class="token operator">+=</span> <span class="token keyword">str</span><span class="token punctuation">.</span><span class="token function">len</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
</span><span class="code-line highlight-line">        <span class="token class-name">Err</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">return</span> <span class="token class-name">Err</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span>
</span><span class="code-line">    <span class="token punctuation">}</span>
</span><span class="code-line">    <span class="token keyword">match</span> lock <span class="token punctuation">{</span>
</span><span class="code-line">        <span class="token class-name">Ok</span><span class="token punctuation">(</span><span class="token keyword">str</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> count <span class="token operator">+=</span> <span class="token keyword">str</span><span class="token punctuation">.</span><span class="token function">len</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
</span><span class="code-line highlight-line">        <span class="token class-name">Err</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">return</span> <span class="token class-name">Err</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span>
</span><span class="code-line">    <span class="token punctuation">}</span>
</span><span class="code-line">    <span class="token class-name">Ok</span><span class="token punctuation">(</span>count<span class="token punctuation">)</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>
<ul>
<li>Notice how we need to check each result independently so that we can return early if either
operation fails. We don't want to continue reading the second file if the first one has produced an error!</li>
</ul>
<p>We can of course remove some of that duplication too using a <code>for in</code> loop:</p>
<pre class="language-rust"><code class="language-rust code-highlight"><span class="code-line"><span class="token keyword">pub</span> <span class="token keyword">fn</span> <span class="token function-definition function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Result</span><span class="token operator">&lt;</span><span class="token keyword">usize</span><span class="token punctuation">,</span> <span class="token namespace">std<span class="token punctuation">::</span>io<span class="token punctuation">::</span></span><span class="token class-name">Error</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
</span><span class="code-line">    <span class="token keyword">let</span> toml <span class="token operator">=</span> <span class="token function">read_to_string</span><span class="token punctuation">(</span><span class="token string">"Cargo.toml"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</span><span class="code-line">    <span class="token keyword">let</span> lock <span class="token operator">=</span> <span class="token function">read_to_string</span><span class="token punctuation">(</span><span class="token string">"Cargo.lock"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</span><span class="code-line">    <span class="token keyword">let</span> <span class="token keyword">mut</span> count <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
</span><span class="code-line">    <span class="token keyword">for</span> result <span class="token keyword">in</span> <span class="token punctuation">[</span>toml<span class="token punctuation">,</span> lock<span class="token punctuation">]</span> <span class="token punctuation">{</span>
</span><span class="code-line">        <span class="token keyword">match</span> result <span class="token punctuation">{</span>
</span><span class="code-line">            <span class="token class-name">Ok</span><span class="token punctuation">(</span><span class="token keyword">str</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> count <span class="token operator">+=</span> <span class="token keyword">str</span><span class="token punctuation">.</span><span class="token function">len</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
</span><span class="code-line highlight-line">            <span class="token class-name">Err</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">return</span> <span class="token class-name">Err</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span>
</span><span class="code-line">        <span class="token punctuation">}</span>
</span><span class="code-line">    <span class="token punctuation">}</span>
</span><span class="code-line">    <span class="token class-name">Ok</span><span class="token punctuation">(</span>count<span class="token punctuation">)</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>
<ul>
<li>This is the key part, - we're still returning <em>early</em> with an error should one occur.</li>
</ul>
<p>Or, if you prefer a more functional approach, we can remove the loop and mutable variables with <code>try_fold</code>:</p>
<div data-mdx-next="pre" data-hl="9" data-focus="true"></div>
<pre class="language-rust"><code class="language-rust code-highlight"><span class="code-line line-number" line="1"><span class="token keyword">pub</span> <span class="token keyword">fn</span> <span class="token function-definition function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Result</span><span class="token operator">&lt;</span><span class="token keyword">usize</span><span class="token punctuation">,</span> <span class="token namespace">std<span class="token punctuation">::</span>io<span class="token punctuation">::</span></span><span class="token class-name">Error</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
</span><span class="code-line line-number" line="2">    <span class="token keyword">let</span> paths <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"Cargo.toml"</span><span class="token punctuation">,</span> <span class="token string">"Cargo.lock"</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
</span><span class="code-line line-number" line="3">    paths
</span><span class="code-line line-number" line="4">        <span class="token punctuation">.</span><span class="token function">iter</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
</span><span class="code-line line-number" line="5">        <span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token closure-params"><span class="token closure-punctuation punctuation">|</span>path<span class="token closure-punctuation punctuation">|</span></span> <span class="token namespace">std<span class="token punctuation">::</span>fs<span class="token punctuation">::</span></span><span class="token function">read_to_string</span><span class="token punctuation">(</span>path<span class="token punctuation">)</span><span class="token punctuation">)</span>
</span><span class="code-line line-number highlight-line" line="6">        <span class="token punctuation">.</span><span class="token function">try_fold</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token closure-params"><span class="token closure-punctuation punctuation">|</span>acc<span class="token punctuation">,</span> item<span class="token closure-punctuation punctuation">|</span></span> <span class="token punctuation">{</span>
</span><span class="code-line line-number highlight-line" line="7">            <span class="token keyword">match</span> item <span class="token punctuation">{</span>
</span><span class="code-line line-number highlight-line" line="8">                <span class="token class-name">Ok</span><span class="token punctuation">(</span>string<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token class-name">Ok</span><span class="token punctuation">(</span>acc <span class="token operator">+</span> string<span class="token punctuation">.</span><span class="token function">len</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
</span><span class="code-line line-number highlight-line" line="9">                <span class="token class-name">Err</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token class-name">Err</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span>
</span><span class="code-line line-number highlight-line" line="10">            <span class="token punctuation">}</span>
</span><span class="code-line line-number highlight-line" line="11">        <span class="token punctuation">}</span><span class="token punctuation">)</span>
</span><span class="code-line line-number" line="12"><span class="token punctuation">}</span>
</span></code></pre>
<ul>
<li><code>try_fold</code> allows us to reduce a collection down to a single value, but with the added advantage of supporting early returns.
It works by continuing to call this closure every time the previous iteration returns an <code>Ok</code>.</li>
<li>For us that means that <strong>line 9</strong> would forward any errors coming from <code>read_to_string</code> - causing the <code>try_fold</code> to
stop iterating and return the error. That error it returns matches our function signature, so we can keep everything in a nice neat package
with no external variables to mutate.</li>
</ul>
<p>We can go one step further here too - notice that inside the <code>try_fold</code> closure we're doing
the re-wrapping technique mentioned before. Well since we're forwarding any error as-is we can simplify
this down to another <code>.map</code> call.</p>
<div data-mdx-next="pre" data-focus="true"></div>
<pre class="language-rust"><code class="language-diff-rust code-highlight"><span class="code-line"><span class="token keyword">pub</span> <span class="token keyword">fn</span> <span class="token function-definition function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Result</span><span class="token operator">&lt;</span><span class="token keyword">usize</span><span class="token punctuation">,</span> <span class="token namespace">std<span class="token punctuation">::</span>io<span class="token punctuation">::</span></span><span class="token class-name">Error</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
</span><span class="code-line">    <span class="token keyword">let</span> paths <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"Cargo.toml"</span><span class="token punctuation">,</span> <span class="token string">"Cargo.lock"</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
</span><span class="code-line">    paths
</span><span class="code-line">        <span class="token punctuation">.</span><span class="token function">iter</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
</span><span class="code-line">        <span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token closure-params"><span class="token closure-punctuation punctuation">|</span>path<span class="token closure-punctuation punctuation">|</span></span> <span class="token namespace">std<span class="token punctuation">::</span>fs<span class="token punctuation">::</span></span><span class="token function">read_to_string</span><span class="token punctuation">(</span>path<span class="token punctuation">)</span><span class="token punctuation">)</span>
</span><span class="code-line">        <span class="token punctuation">.</span><span class="token function">try_fold</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token closure-params"><span class="token closure-punctuation punctuation">|</span>acc<span class="token punctuation">,</span> item<span class="token closure-punctuation punctuation">|</span></span> <span class="token punctuation">{</span>
</span><span class="code-line highlight-line">            item<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token closure-params"><span class="token closure-punctuation punctuation">|</span>string<span class="token closure-punctuation punctuation">|</span></span> acc <span class="token operator">+</span> string<span class="token punctuation">.</span><span class="token function">len</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
</span><span class="code-line">        <span class="token punctuation">}</span><span class="token punctuation">)</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>
<ul>
<li>Here the type of <code>item</code> is <code>Result&lt;String, io::Error&gt;</code>, so if there's an error the closure given to
<code>.map</code> will not be executed - the error will be forwarded instead. That will cause the <code>try_fold</code> to exit early which in turn will cause our outer
function to also return.</li>
</ul>
<h2>But...</h2>
<p>Back to basics for a second: in the examples above, we've taken the requirement of "read 2 files from disk and sum their
byte lengths" and we've ended up with a generic solution that can work with any amount of files.</p>
<p>Whether we choose a <code>for x in xs</code> loop, or a chain of iterator methods, we've still leap-frogged from simple -&gt; complex in a heartbeat - is there
any middle-ground to explore?</p>
<p><strong>Enter <code>?</code></strong></p>
<p>The core issue we're having here is the ergonomics around reaching into a <code>Result</code> type. Because <code>read_from_string</code> forces us
to deal with the fact that it can fail, it means we can't just access the values safely without a bit of syntax ceremony...</p>
<p>... but that's exactly what <code>?</code> (the question mark operator) is here to solve.</p>
<p>If we laser-focus in on <em>just</em> solving the 2-file problem, our solution could be as simple as:</p>
<pre class="language-rust"><code class="language-rust code-highlight"><span class="code-line"><span class="token keyword">use</span> <span class="token namespace">std<span class="token punctuation">::</span>fs<span class="token punctuation">::</span></span>read_to_string<span class="token punctuation">;</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token keyword">fn</span> <span class="token function-definition function">measure_cargo_files</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Result</span><span class="token operator">&lt;</span><span class="token keyword">usize</span><span class="token punctuation">,</span> <span class="token namespace">std<span class="token punctuation">::</span>io<span class="token punctuation">::</span></span><span class="token class-name">Error</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
</span><span class="code-line highlight-line">    <span class="token keyword">let</span> toml <span class="token operator">=</span> <span class="token function">read_to_string</span><span class="token punctuation">(</span><span class="token string">"Cargo.toml"</span><span class="token punctuation">)</span><span class="token operator">?</span><span class="token punctuation">;</span>
</span><span class="code-line highlight-line">    <span class="token keyword">let</span> lock <span class="token operator">=</span> <span class="token function">read_to_string</span><span class="token punctuation">(</span><span class="token string">"Cargo.lock"</span><span class="token punctuation">)</span><span class="token operator">?</span><span class="token punctuation">;</span>
</span><span class="code-line">    <span class="token class-name">Ok</span><span class="token punctuation">(</span>toml<span class="token punctuation">.</span><span class="token function">len</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> lock<span class="token punctuation">.</span><span class="token function">len</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>
<ul>
<li>notice how on both of these lines, we add a <code>?</code> directly after the <code>read_to_string()</code> call. This will 'unwrap' the
value (if it was successful). So the <code>toml</code> and <code>lock</code> bindings here are both of type
<code>String</code> - they have been 'unwrapped'. If any of those file-reads were to fail though, we'd return early with the error. 👌</li>
</ul>
<h2>How it works: The error types line up!</h2>
<p>This may seem like magic, but it's really just a case of our function signature having a return type that's
suitable for all places where we've used <code>?</code>.</p>
<p>So, our return type is:</p>
<pre class="language-rust"><code class="language-rust code-highlight"><span class="code-line"><span class="token class-name">Result</span><span class="token operator">&lt;</span><span class="token keyword">usize</span><span class="token punctuation">,</span> <span class="token namespace">std<span class="token punctuation">::</span>io<span class="token punctuation">::</span></span><span class="token class-name">Error</span><span class="token operator">&gt;</span>
</span></code></pre>
<p>whilst the return type of <code>read_to_string()</code> is</p>
<pre class="language-rust"><code class="language-rust code-highlight"><span class="code-line"><span class="token class-name">Result</span><span class="token operator">&lt;</span><span class="token class-name">String</span><span class="token punctuation">,</span> <span class="token namespace">std<span class="token punctuation">::</span>io<span class="token punctuation">::</span></span><span class="token class-name">Error</span><span class="token operator">&gt;</span>
</span></code></pre>
<p>The types of the values actually differ - Our return type has <code>usize</code> for the <code>Ok</code> case whereas <code>read_to_string</code> has <code>String</code>. But for the
<code>?</code> operator to work it's only the <code>Err</code> part that needs to line up - and those do! 😎</p>
<p>The Rust compiler will analyze all uses of <code>?</code> within a function body and determine if each of them is suitable
for a possible 'early return' in your function.</p>
<p>A de-sugared version of <code>?</code> might look something like this:</p>
<pre class="language-rust"><code class="language-rust code-highlight"><span class="code-line"><span class="token keyword">use</span> <span class="token namespace">std<span class="token punctuation">::</span>fs<span class="token punctuation">::</span></span>read_to_string<span class="token punctuation">;</span>
</span><span class="code-line">
</span><span class="code-line"><span class="token keyword">fn</span> <span class="token function-definition function">measure_cargo_files</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Result</span><span class="token operator">&lt;</span><span class="token keyword">usize</span><span class="token punctuation">,</span> <span class="token namespace">std<span class="token punctuation">::</span>io<span class="token punctuation">::</span></span><span class="token class-name">Error</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
</span><span class="code-line">    <span class="token keyword">let</span> toml <span class="token operator">=</span> <span class="token keyword">match</span> <span class="token function">read_to_string</span><span class="token punctuation">(</span><span class="token string">"Cargo.toml"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
</span><span class="code-line">        <span class="token class-name">Ok</span><span class="token punctuation">(</span>toml<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> toml<span class="token punctuation">,</span>
</span><span class="code-line highlight-line">        <span class="token class-name">Err</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">return</span> <span class="token class-name">Err</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span>
</span><span class="code-line">    <span class="token punctuation">}</span><span class="token punctuation">;</span>
</span><span class="code-line">    <span class="token comment">// snip</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>
<ul>
<li>yep! the <code>?</code> is just de-sugaring to an early-return like this, not so magical after all!</li>
</ul>
<p>So that's it. The <code>?</code> operator can be thought of as <code>unwrap or return early</code> -&gt; with the <code>return early</code> bit
being the most important part here. Every time you try to use <code>?</code> you absolutely must consider the
context of what an early return would mean.</p>
<p>That can differ greatly based on where you're using <code>?</code> - something we'll cover in more detail in part 2.</p>
<h2>Part 2...</h2>
<p>This first post was just a primer to get you thinking of what using <code>?</code> really means and why it's useful. It's fundamental
Rust 🦀 knowledge that you need to have so that we can discuss the many, many more use cases in depth in part 2.</p>
<p>In part 2, we'll cover:</p>
<ul>
<li>using <code>?</code> in async blocks</li>
<li>using <code>?</code> in closures</li>
<li>how <code>?</code> causes <code>err.into()</code> to be called - allowing automatic conversion between error types</li>
</ul>
<p>See you then 👋</p>]]></content:encoded>
            <author>shane.osbourne@hey.com (Shane Osbourne)</author>
        </item>
        <item>
            <title><![CDATA[X-State, Service re-entry]]></title>
            <link>https://shane-o.dev/articles/xstate-service-re-entry</link>
            <guid>https://shane-o.dev/articles/xstate-service-re-entry</guid>
            <pubDate>Wed, 01 Nov 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aliquam aperiam blanditiis, cumque dicta distinctio dolor eaque eos incidunt inventore minus provident quasi quia quibusdam quidem quos sequi tempora unde voluptatibus?</p>
<div data-mdx-next="pre" data-pull="true"></div>
<pre class="language-js"><code class="language-js code-highlight"><span class="code-line"><span class="token dom variable">document</span><span class="token punctuation">.</span><span class="token method function property-access">querySelector</span><span class="token punctuation">(</span><span class="token string">'[name=room]'</span><span class="token punctuation">)</span><span class="token operator">?.</span><span class="token method function property-access">addEventListener</span><span class="token punctuation">(</span><span class="token string">'change'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
</span><span class="code-line">  <span class="token function">invariant</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span><span class="token property-access">target</span> <span class="token keyword">instanceof</span> <span class="token class-name">HTMLSelectElement</span><span class="token punctuation">)</span>
</span><span class="code-line">  service<span class="token punctuation">.</span><span class="token method function property-access">send</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">'room.join'</span><span class="token punctuation">,</span> <span class="token literal-property property">id</span><span class="token operator">:</span> e<span class="token punctuation">.</span><span class="token property-access">target</span><span class="token punctuation">.</span><span class="token property-access">value</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
</span><span class="code-line"><span class="token punctuation">}</span><span class="token punctuation">)</span>
</span></code></pre>]]></content:encoded>
            <author>shane.osbourne@hey.com (Shane Osbourne)</author>
        </item>
    </channel>
</rss>