<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Wes</title>
    <description>The latest articles on Forem by Wes (@wes_goulet).</description>
    <link>https://forem.com/wes_goulet</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F218754%2Fdb1e114d-f16a-4ead-bdcb-d96e24287c7a.png</url>
      <title>Forem: Wes</title>
      <link>https://forem.com/wes_goulet</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/wes_goulet"/>
    <language>en</language>
    <item>
      <title>Conditional Styles with CSS :has</title>
      <dc:creator>Wes</dc:creator>
      <pubDate>Sun, 10 Mar 2024 00:00:00 +0000</pubDate>
      <link>https://forem.com/wes_goulet/conditional-styles-with-css-has-2fk8</link>
      <guid>https://forem.com/wes_goulet/conditional-styles-with-css-has-2fk8</guid>
      <description>&lt;p&gt;I was setting up a simple landing page for &lt;a href="https://fandwagon.com/"&gt;Fandwagon&lt;/a&gt; and just wanted some text to be strike-through when the user checks a checkbox. So far my landing page has no need for any JavaScript, and thankfully I can keep it that way and still get conditional styles with the &lt;code&gt;:has()&lt;/code&gt; pseudo-class.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/wes_goulet/embed/rNbMpYg?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;First, the HTML - a simple form with a checkbox.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;form&amp;gt;
  &amp;lt;p&amp;gt;
    Want to know when it's ready?
    &amp;lt;span
      &amp;gt;I'll send you one email when it's ready, then delete your email address
      forever.&amp;lt;/span
    &amp;gt;
  &amp;lt;/p&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;input type="checkbox" switch name="subscribe" id="subscribe" /&amp;gt;
    &amp;lt;label for="subscribe"&amp;gt;Check this box to subscribe to updates&amp;lt;/label&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the CSS for the conditional strike-through&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#example-form:has(input:checked) span {
  text-decoration: line-through;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Breaking down the selector
&lt;/h2&gt;

&lt;p&gt;The first part &lt;code&gt;#example-form:has(input:checked)&lt;/code&gt; is basically saying "find the element with id of &lt;code&gt;example-form&lt;/code&gt; that &lt;em&gt;has&lt;/em&gt; a child &lt;code&gt;input&lt;/code&gt; element that is checked".&lt;/p&gt;

&lt;p&gt;&lt;code&gt;:has&lt;/code&gt; is basically a very-flexible "parent selector".&lt;/p&gt;

&lt;p&gt;Once we've selected the parent that contains the checked &lt;code&gt;input&lt;/code&gt; then we select the descendent &lt;code&gt;span&lt;/code&gt; element and apply the &lt;code&gt;text-decoration: line-through&lt;/code&gt; style.&lt;/p&gt;

&lt;p&gt;💥 Done! No JavaScript needed!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can get pretty far in conditional styling by combining &lt;code&gt;:has&lt;/code&gt; and &lt;code&gt;:checked&lt;/code&gt; pseudo-classes. If you have any cool example of combining the two then send me an email, I'd love to see!&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>css</category>
      <category>tip</category>
    </item>
    <item>
      <title>Don't use TypeScript enum</title>
      <dc:creator>Wes</dc:creator>
      <pubDate>Sun, 03 Dec 2023 00:00:00 +0000</pubDate>
      <link>https://forem.com/wes_goulet/dont-use-typescript-enum-1774</link>
      <guid>https://forem.com/wes_goulet/dont-use-typescript-enum-1774</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Trying my hand at a &lt;a href="https://chriscoyier.net/2023/10/02/dan-mall-answers-to-common-design-questions/"&gt;zero-nuance take&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Don't do this
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;Shape&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Sphere&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Rectangle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Triangle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ball&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Shape&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sphere&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Do this instead
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Shape&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sphere&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Rectangle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Triangle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ball&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sphere&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Seems I'm not good at "zero-nuance", I feel compelled to explain the why behind this take... at least I'll keep it brief.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Enums are not part of the JavaScript language, so the TypeScript compiler generates different runtime code when you use enums. You aren't writing "JavaScript with types" any more. You are writing a different language that transpiles to JS. Don't do that. Keep it simple.&lt;/p&gt;

&lt;p&gt;TypeScript is great for type checking as a dev-time benefit: it's additive, you can strip off the types and you still have JavaScript. You can even add types to JavaScript files with JSDoc comments - no need for a build step to strip off types, ship/debug the code you write, even better!&lt;/p&gt;

&lt;h3&gt;
  
  
  Related Posts
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://goulet.dev/posts/how-to-write-ts-interfaces-in-jsdoc/"&gt;How to Write TypeScript Interfaces in JSDoc Comments&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://goulet.dev/posts/type-guard-in-jsdoc/"&gt;Type Guards in Javascript Using JSDoc Comments&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>enums</category>
    </item>
    <item>
      <title>Scoped CSS Styles with Declarative Shadow DOM</title>
      <dc:creator>Wes</dc:creator>
      <pubDate>Thu, 27 Jul 2023 00:00:00 +0000</pubDate>
      <link>https://forem.com/wes_goulet/scoped-css-styles-with-declarative-shadow-dom-4p80</link>
      <guid>https://forem.com/wes_goulet/scoped-css-styles-with-declarative-shadow-dom-4p80</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--n_xh3pSU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://goulet.dev/posts/scoped-styles-dsd/codepen.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--n_xh3pSU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://goulet.dev/posts/scoped-styles-dsd/codepen.png" alt="Codepen showing Declarative Shadow DOM" width="800" height="466"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've always thought of the Shadow DOM as something to use for custom elements only... but it can be useful for non-custom elements as well!&lt;/p&gt;

&lt;p&gt;I was reading &lt;a href="https://medium.com/@eisenbergeffect/a-few-dom-reminders-2a0f18e40804"&gt;this excellent post&lt;/a&gt; by Rob Eisenberg and came across the part about composing shadow DOM and realized that you can use &lt;a href="https://developer.chrome.com/articles/declarative-shadow-dom/"&gt;Declarative Shadow DOM&lt;/a&gt; to scope CSS to a particular element without any JS. And without any framework/dependencies. 💪&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;shadowrootmode=&lt;/span&gt;&lt;span class="s"&gt;"open"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
      &lt;span class="nd"&gt;:host&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;red&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
  This paragraph is scoped
&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see a live example at &lt;a href="https://codepen.io/wes_goulet/pen/wvQEJod"&gt;this Codepen&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;One thing to note is that as of July 2023 Firefox has not implemented Declarative Shadow DOM yet (see &lt;a href="https://caniuse.com/mdn-html_elements_template_shadowrootmode"&gt;caniuse&lt;/a&gt;), but a &lt;a href="https://developer.chrome.com/articles/declarative-shadow-dom/#polyfill"&gt;polyfill&lt;/a&gt; is possible. Firefox &lt;a href="https://caniuse.com/mdn-api_element_attachshadow"&gt;does currently support&lt;/a&gt; the imperative &lt;code&gt;attachShadow&lt;/code&gt; API.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Imperative Shadow DOM
&lt;/h2&gt;

&lt;p&gt;Alternately, you can use the imperative &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow"&gt;&lt;code&gt;attachShadow&lt;/code&gt; JS API&lt;/a&gt; to accomplish the same thing. It is supported in all modern browsers, however it's more verbose, requires JS, and is not as SSR-friendly as Declarative Shadow DOM. For an example of using &lt;code&gt;attachShadow&lt;/code&gt; check out &lt;a href="https://codepen.io/wes_goulet/pen/NWELmoy"&gt;this codepen&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The imperative Shadow DOM works well when writing &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM"&gt;custom elements&lt;/a&gt; (like &lt;a href="https://github.com/wes-goulet/side-drawer"&gt;side-drawer&lt;/a&gt;) and declarative Shadow DOM works well when styling a non-custom element (like &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;).&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>dom</category>
      <category>css</category>
    </item>
    <item>
      <title>theme-color meta tag vs manifest theme_color</title>
      <dc:creator>Wes</dc:creator>
      <pubDate>Sun, 25 Jun 2023 00:00:00 +0000</pubDate>
      <link>https://forem.com/wes_goulet/theme-color-meta-tag-vs-manifest-themecolor-50ii</link>
      <guid>https://forem.com/wes_goulet/theme-color-meta-tag-vs-manifest-themecolor-50ii</guid>
      <description>&lt;h2&gt;
  
  
  &lt;code&gt;theme-color&lt;/code&gt; meta tag
&lt;/h2&gt;

&lt;p&gt;You can set your site's theme color with the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta/name/theme-color"&gt;&lt;code&gt;theme-color&lt;/code&gt; meta tag&lt;/a&gt; in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of your site:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"theme-color"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"#4285f4"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Browsers will use this color for certain browser UI elements (like the address bar) when the user visits your site.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If your theme color causes contrast issues it might not be used by some browsers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;theme_color&lt;/code&gt; in web app manifest
&lt;/h2&gt;

&lt;p&gt;You can also set a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Manifest/theme_color"&gt;&lt;code&gt;theme_color&lt;/code&gt; in your site's web app manifest&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"My Site"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"theme_color"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#4285f4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"background_color"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#fff"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The OS will use this color for certain UI elements when the user installs your site as a PWA. For example, this will set the color of your app's title bar on Windows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Both
&lt;/h2&gt;

&lt;p&gt;In my experience, desktop chromium-based browsers (Chrome, Edge, etc) will use the &lt;code&gt;theme-color&lt;/code&gt; meta tag over the &lt;code&gt;theme_color&lt;/code&gt; in the manifest when setting the color the of the app title bar.&lt;/p&gt;

&lt;p&gt;So which should you use? Use both and set them to the same value (as &lt;a href="https://web.dev/add-manifest/#theme-color"&gt;mentioned on web.dev&lt;/a&gt;).&lt;/p&gt;

</description>
      <category>pwa</category>
      <category>web</category>
      <category>design</category>
    </item>
    <item>
      <title>There is an App for That (But Shouldn't Be)</title>
      <dc:creator>Wes</dc:creator>
      <pubDate>Sun, 04 Jun 2023 00:00:00 +0000</pubDate>
      <link>https://forem.com/wes_goulet/there-is-an-app-for-that-but-shouldnt-be-4723</link>
      <guid>https://forem.com/wes_goulet/there-is-an-app-for-that-but-shouldnt-be-4723</guid>
      <description>&lt;p&gt;I wish there were fewer low-touch apps on the App Store, and more high-quality mobile sites on the open web.&lt;/p&gt;

&lt;p&gt;I saw an advertisement on ADP's website for their benefits mobile app. With that app you can compare plan options, add dependents, and select benefits during open enrollment. All things you would do once a year (maybe a few times if you have some life changes in a given year).&lt;/p&gt;

&lt;p&gt;So why download an app that you will use once a year? Why does a company make an app with such presumably small usage? Is it so they can say "We have an app in the App Store"?&lt;/p&gt;

&lt;p&gt;Ditch the burden of building and maintaining a native app. Make your existing website responsive/mobile-friendly and your users will be better served for it.&lt;/p&gt;

</description>
      <category>opinion</category>
      <category>apple</category>
      <category>ios</category>
    </item>
    <item>
      <title>How to Serialize Errors in Javascript</title>
      <dc:creator>Wes</dc:creator>
      <pubDate>Sun, 30 Apr 2023 00:00:00 +0000</pubDate>
      <link>https://forem.com/wes_goulet/how-to-serialize-errors-in-javascript-406i</link>
      <guid>https://forem.com/wes_goulet/how-to-serialize-errors-in-javascript-406i</guid>
      <description>&lt;h2&gt;
  
  
  Don't use a simple &lt;code&gt;JSON.stringify&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;If you want to serialize an Error in Javascript you might be tempted to simply use &lt;code&gt;JSON.stringify&lt;/code&gt;, but that won't be very helpful.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Something went wrong&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;serialized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// serialized -&amp;gt; '{}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Normalize the error
&lt;/h2&gt;

&lt;p&gt;Instead what I like to do is "normalize" the error (in case it's not actually an &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error"&gt;&lt;code&gt;Error&lt;/code&gt; object&lt;/a&gt;) and then use the error message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * @param {unknown} error
 * @returns {Error}
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;normalizeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// else turn this unknown thing into a string&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// NOW I CAN CATCH ERRORS...&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Something went wrong&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;normalized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;normalizeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;serialized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;normalized&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// serialized -&amp;gt; "Something went wrong"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// AND ANYTHING ELSE&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Who throws a string? ME!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;normalized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;normalizeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;serialized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;normalized&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// serialized -&amp;gt; "Who throws a string? ME!"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Alternative: Use overloaded &lt;code&gt;JSON.stringify&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;If you &lt;em&gt;know&lt;/em&gt; you are dealing with an &lt;code&gt;Error&lt;/code&gt; object (so you don't need to normalize it) then you can use &lt;code&gt;JSON.stringify&lt;/code&gt; with a second argument to tell JSON stringify which properties to enumerate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Something went wrong&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;serialized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getOwnPropertyNames&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="c1"&gt;// serialized -&amp;gt; '{"stack":"Error: Something went wrong\\n at REPL9:1:13\\n at Script.runInThisContext (node:vm:131:12)\\n at REPLServer.defaultEval (node:repl:522:29)\\n at bound (node:domain:416:15)\\n at REPLServer.runBound [as eval] (node:domain:427:12)\\n at REPLServer.onLine (node:repl:844:10)\\n at REPLServer.emit (node:events:390:22)\\n at REPLServer.EventEmitter.emit (node:domain:470:12)\\n at REPLServer.Interface._onLine (node:readline:418:10)\\n at REPLServer.Interface._line (node:readline:763:8)","message":"Something went wrong"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you don't want the stack in there you can just list an array of the properties you want:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Something went wrong&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;serialized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="c1"&gt;// serialized -&amp;gt; '{"message":"Something went wrong", "name": "Error"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>javascript</category>
      <category>errors</category>
    </item>
    <item>
      <title>Netlify Functions in Javascript with Type-Checking</title>
      <dc:creator>Wes</dc:creator>
      <pubDate>Sat, 11 Mar 2023 00:00:00 +0000</pubDate>
      <link>https://forem.com/wes_goulet/netlify-functions-in-javascript-with-type-checking-1cbm</link>
      <guid>https://forem.com/wes_goulet/netlify-functions-in-javascript-with-type-checking-1cbm</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--n5-x9vs_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://goulet.dev/posts/netlify-functions-js-typing/function-in-js-with-typing.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--n5-x9vs_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://goulet.dev/posts/netlify-functions-js-typing/function-in-js-with-typing.png" alt="JS Function with strong-typing" width="609" height="529"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Shout out to &lt;a href="https://eduardoboucas.com/"&gt;Eduardo&lt;/a&gt; for pointing out that netlify already publishes an npm package with types (I don't need to write my own &lt;code&gt;.d.ts&lt;/code&gt; file) 💪&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Short Version
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/** @type {import("@netlify/functions").Handler} */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// all these properties are typed&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;httpMethod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// response is typed&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Long Version
&lt;/h2&gt;

&lt;p&gt;I really like Netlify Functions. ❤️&lt;/p&gt;

&lt;p&gt;I really like &lt;del&gt;TypeScript&lt;/del&gt; modern Javascript with type-checking. 👍&lt;/p&gt;

&lt;p&gt;The appeal of &lt;a href="https://docs.netlify.com/functions/overview/"&gt;Netlify Functions&lt;/a&gt; is how easy it is to setup: make a file, get a new api endpoint.&lt;/p&gt;

&lt;p&gt;Writing code in &lt;code&gt;.ts&lt;/code&gt; files makes something that was easy become not quite as easy.&lt;/p&gt;

&lt;p&gt;Not quite as easy to get started (step 1: add a tsconfig, step 2: figure out what goes into tsconfig).&lt;/p&gt;

&lt;p&gt;Not quite as easy to debug ("Why did the debugger just jump down there, are my source maps messed up?").&lt;/p&gt;

&lt;p&gt;It's not too bad, but any config/build ceremony that slows down development should be scrutinized.&lt;/p&gt;

&lt;p&gt;I find writing &lt;code&gt;.js&lt;/code&gt; files (actually &lt;code&gt;.mjs&lt;/code&gt; files, it's 2023, let's embrace ESM) with a combination of &lt;code&gt;.d.ts&lt;/code&gt; declaration files and jsdoc comments to be the sweet spot of getting type-checking without any Typescript config or build steps. 🚀&lt;/p&gt;

&lt;p&gt;Here is how I setup my Netlify Functions projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Install &lt;code&gt;@netlify/functions&lt;/code&gt; npm package
&lt;/h2&gt;

&lt;p&gt;Netlify publishes an NPM package with types for their Functions, add that to your package.json.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i -D @netlify/functions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: JSDoc &lt;code&gt;@type&lt;/code&gt; the handler
&lt;/h2&gt;

&lt;p&gt;Now in your Function's JS file you can use a jsdoc comment to apply typing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/** @type {import("@netlify/functions").Handler} */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// all these properties are typed&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;httpMethod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// response is typed&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it! 🎉&lt;/p&gt;

</description>
      <category>jsdoc</category>
      <category>netlify</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Enabling Emoji Shortcodes in Eleventy Markdown Files</title>
      <dc:creator>Wes</dc:creator>
      <pubDate>Wed, 24 Nov 2021 00:00:00 +0000</pubDate>
      <link>https://forem.com/wes_goulet/enabling-emoji-shortcodes-in-eleventy-markdown-files-3ahd</link>
      <guid>https://forem.com/wes_goulet/enabling-emoji-shortcodes-in-eleventy-markdown-files-3ahd</guid>
      <description>&lt;p&gt;In moving my blog from hugo to eleventy I noticed my emoji shortcodes (ie: writing &lt;code&gt;:fire:&lt;/code&gt; and having the 🔥 emoji show up in the generated page) weren't working. 😦&lt;/p&gt;

&lt;p&gt;Some quick internet searching didn't turn up a direct result (thus this blog post), but I did stumble upon the answer in the &lt;a href="https://www.11ty.dev/docs/languages/markdown/#add-your-own-plugins"&gt;eleventy docs around markdown&lt;/a&gt;. We need to tell eleventy to use the markdown-it-emoji plugin with a couple simple steps.&lt;/p&gt;

&lt;p&gt;Step 1: Install markdown-it and the markdown-it-emoji plugin&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; markdown-itnpm i &lt;span class="nt"&gt;-D&lt;/span&gt; markdown-it-emoji
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 2: Update &lt;code&gt;.eleventy.js&lt;/code&gt; config file and register the emoji plugin&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eleventyConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// setup markdown to use emoji plugin&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;markdownIt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;markdown-it&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;markdownItEmoji&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;markdown-it-emoji&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;markdownLib&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;markdownIt&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;markdownItEmoji&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;eleventyConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setLibrary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;md&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;markdownLib&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now emoji shortcodes work! 💪 🔥 💥&lt;/p&gt;

</description>
      <category>11ty</category>
      <category>emoji</category>
      <category>markdown</category>
    </item>
    <item>
      <title>How to Write TypeScript Interfaces in JSDoc Comments</title>
      <dc:creator>Wes</dc:creator>
      <pubDate>Sun, 08 Aug 2021 05:19:17 +0000</pubDate>
      <link>https://forem.com/wes_goulet/how-to-write-typescript-interfaces-in-jsdoc-comments-8ni</link>
      <guid>https://forem.com/wes_goulet/how-to-write-typescript-interfaces-in-jsdoc-comments-8ni</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Updated 2023-07-23 to clarify that jsdoc comments support &lt;strong&gt;object types&lt;/strong&gt;, not &lt;strong&gt;interfaces&lt;/strong&gt;. Thanks to &lt;a href="https://twitter.com/mattpocockuk/status/1682059208412344326?s=20"&gt;Matt Pocock&lt;/a&gt; for the pointer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I like writing web apps without any build step, just vanilla &lt;code&gt;.js&lt;/code&gt; files.  But I still like the type-checking that TypeScript provides.  Thankfully &lt;a href="https://www.typescriptlang.org/docs/handbook/type-checking-javascript-files.html"&gt;TypeScript supports type-checking &lt;code&gt;.js&lt;/code&gt; files via JSDoc comments&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But how do you write out interfaces without a &lt;code&gt;.ts&lt;/code&gt; file? The tl;dr is you can write an object type in a JSDoc comment, or you can write an interface in a &lt;code&gt;.d.ts&lt;/code&gt; file and import that into your &lt;code&gt;.js&lt;/code&gt; file. Let's dig into each option.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interfaces in a .ts file
&lt;/h2&gt;

&lt;p&gt;First, let's look at an example in a &lt;code&gt;.ts&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Address&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;street&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Customer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sting&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Option 1: Object Types in a JSDoc comment
&lt;/h2&gt;

&lt;p&gt;An &lt;a href="https://www.typescriptlang.org/docs/handbook/2/objects.htm"&gt;object type&lt;/a&gt; isn't exactly an interface, but for most use cases it behaves the same way.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The main difference is interfaces support &lt;a href="https://www.typescriptlang.org/docs/handbook/declaration-merging.html"&gt;declaration merging&lt;/a&gt; but object types do not.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Writing those same interfaces in a &lt;code&gt;.js&lt;/code&gt; file would look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * @typedef Address
 * @prop {string} street The street
 * @prop {string} city The City
 * @prop {number} zip The zip code
 *
 * @typedef Customer
 * @prop {string} name The Customer's name
 * @prop {string} email The Customer's email
 * @prop {Address} address The Customer's address
 */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then you can apply that interface to an object using the &lt;code&gt;@type&lt;/code&gt; annotation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/** @type {Customer} */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;theCustomer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Boom 💥 now you've got type-checking and intellisense on your data model right in a vanilla &lt;code&gt;.js&lt;/code&gt; file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 2: import interfaces from a .d.ts file
&lt;/h2&gt;

&lt;p&gt;The other option to define interfaces is to put them in a &lt;code&gt;.d.ts&lt;/code&gt; file and import that into you &lt;code&gt;.js&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// models.d.ts&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Address&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;street&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Customer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sting&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then in your &lt;code&gt;.js&lt;/code&gt; file you can import those types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * @typedef { import("./models").Customer } Customer
 */&lt;/span&gt;

 &lt;span class="cm"&gt;/** @type {Customer} */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;theCustomer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is my preferred approach as I find writing types in &lt;code&gt;.d.ts&lt;/code&gt; files to be more terse than writing types out in jsdoc comments. But I like having both options available to use interchangeably.&lt;/p&gt;

&lt;h3&gt;
  
  
  Related Posts
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://goulet.dev/posts/type-guard-in-jsdoc/"&gt;Type Guards in Javascript Using JSDoc Comments&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Hopefully you found this post helpful, if you have any questions you can find me &lt;a href="https://twitter.com/wes_goulet"&gt;on Twitter&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>typescript</category>
      <category>jsdoc</category>
      <category>javascript</category>
      <category>interfaces</category>
    </item>
    <item>
      <title>Detect if a PWA is Visible with the Page Visibility API</title>
      <dc:creator>Wes</dc:creator>
      <pubDate>Sat, 07 Nov 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/wes_goulet/detect-if-a-pwa-is-visible-with-the-page-visibility-api-1k3a</link>
      <guid>https://forem.com/wes_goulet/detect-if-a-pwa-is-visible-with-the-page-visibility-api-1k3a</guid>
      <description>&lt;p&gt;Need to figure out when your PWA comes to the foreground? The modern web has you covered with the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API"&gt;Page Visibility API&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;visibilitychange&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visibilityState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hidden&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// your PWA is now in the background&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;visible&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// your PWA is now in the foreground&lt;/span&gt;
    &lt;span class="nf"&gt;refreshData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: this API isn't specific to PWAs, it works for non-PWA websites/webapps as well.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Most documentation talks about how this event fires when switching tabs, but this also fires when a PWA is foregrounded/backgrounded.&lt;/p&gt;

&lt;p&gt;Try it out yourself by going to &lt;a href="https://visibility-api.netlify.app/"&gt;https://visibility-api.netlify.app&lt;/a&gt; and adding it to your home screen.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Hopefully you found this post helpful, if you have any questions you can find me &lt;a href="https://twitter.com/wes_goulet"&gt;on Twitter&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>development</category>
      <category>web</category>
      <category>pwa</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Type Guards in Javascript Using JSDoc Comments</title>
      <dc:creator>Wes</dc:creator>
      <pubDate>Sat, 10 Oct 2020 21:02:50 +0000</pubDate>
      <link>https://forem.com/wes_goulet/type-guards-in-javascript-using-jsdoc-comments-36l2</link>
      <guid>https://forem.com/wes_goulet/type-guards-in-javascript-using-jsdoc-comments-36l2</guid>
      <description>&lt;p&gt;In Typescript you can write &lt;a href="https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-guards-and-differentiating-types"&gt;type guards&lt;/a&gt; to filter down a union type to a single type.  For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// user-defined type guard&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isFish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Fish&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;Bird&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;pet&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;Fish&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;swim&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Fish&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;Bird&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getPet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// at this point you either have a Fish or Bird&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isFish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// at this point you (and tsc and intellisense) know you have a Fish&lt;/span&gt;
    &lt;span class="nx"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;swim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// at this point you (and tsc and intellisense) know you have a Bird&lt;/span&gt;
    &lt;span class="nx"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fly&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  JSDoc Type-Checking Version
&lt;/h2&gt;

&lt;p&gt;What if you write your code in Javascript and use &lt;a href="https://goulet.dev/posts/build-a-pwa-without-a-build-step/#type-checking"&gt;JSDoc comments for type-checking and intellisense&lt;/a&gt;?  You can still write and use type guards!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/** @typedef {{swim: () =&amp;gt; void}} Fish */&lt;/span&gt;
&lt;span class="cm"&gt;/** @typedef {{fly: () =&amp;gt; void}} Bird */&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * @param {Fish | Bird} pet
 * @returns {pet is Fish}
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isFish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;swim&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/** @type {Fish | Bird} */&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;pet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getPet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// at this point "pet" is either a Fish or Bird&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isFish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// at this point you (and tsc and intellisense) know you have a Fish&lt;/span&gt;
  &lt;span class="nx"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;swim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// at this point you (and tsc and intellisense) know you have a Bird&lt;/span&gt;
  &lt;span class="nx"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fly&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Hopefully you found this post helpful, if you have any questions you can find me &lt;a href="https://twitter.com/wes_goulet"&gt;on Twitter&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>typescript</category>
      <category>jsdoc</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>iOS 14 PWA Improvements</title>
      <dc:creator>Wes</dc:creator>
      <pubDate>Sat, 11 Jul 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/wes_goulet/ios-14-pwa-improvements-1o3e</link>
      <guid>https://forem.com/wes_goulet/ios-14-pwa-improvements-1o3e</guid>
      <description>&lt;p&gt;I recently installed the iOS 14 Beta 2 looking to see what improvements are in store for PWAs.  Unfortunately, there aren't really any PWA-specific improvements (ie: features related to service workers or the experience of standalone web apps pinned to the home screen).&lt;/p&gt;

&lt;p&gt;The good news is there are some important standards/features being implemented in Safari that a PWA (or any website/webapp) can use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Improvements
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Face ID and Touch ID for the Web
&lt;/h4&gt;

&lt;p&gt;Safari's implementation of the webauthn API will support Face ID and Touch ID, so now a PWA can have that same seemless authentication experience that native apps have.&lt;/p&gt;

&lt;h4&gt;
  
  
  Resize Observer
&lt;/h4&gt;

&lt;p&gt;Useful for building responsive elements, ResizeObserver can now be used on iOS without any polyfill, resulting in a smaller PWA bundle size. 🎉&lt;/p&gt;

&lt;h4&gt;
  
  
  CSS Shadow Parts
&lt;/h4&gt;

&lt;p&gt;I use web components in my PWAs so this improvement is exciting to me.  This standard helps with styling web components.  Now that this standard is implemented in all major evergreen browsers (Chrome, Edge, Firefox, Safari) I hope to see more web components leveraging shadow parts for style customization.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;CSS Shadow Parts are actually already implemented in iOS 13.1&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  HTML &lt;code&gt;enterkeyhint&lt;/code&gt; attribute
&lt;/h4&gt;

&lt;p&gt;Native apps have been able to set the enter key label on the virtual keyboard for a while, but now PWAs can do the same using the &lt;code&gt;enterkeyhint&lt;/code&gt; attribute.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5499jdi80d6jn2zz2rcv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5499jdi80d6jn2zz2rcv.png" alt="enterkeyhint example" width="761" height="267"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  WebP Image Format Support
&lt;/h4&gt;

&lt;p&gt;WebP is a modern image format that supports both lossless and lossy compression.  WebP images are smaller than PNGs and JPEGs, resulting in a smaller PWA bundle size. 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  PWA-Specific Features Still Not Implemented
&lt;/h2&gt;

&lt;p&gt;Unfortunately, there are some PWA features that still don't work on iOS 14.  This isn't an exhaustive list, just a couple of things I've noticed so far.&lt;/p&gt;

&lt;h4&gt;
  
  
  Background Audio
&lt;/h4&gt;

&lt;p&gt;Like last year, the first thing I checked when I installed iOS 14 was background audio because &lt;a href="https://backgroundnoise.app"&gt;some PWAs&lt;/a&gt; really need it.  This time around I was hopeful because there was some activity earlier this year on &lt;a href="https://bugs.webkit.org/show_bug.cgi?id=198277"&gt;the webkit bug&lt;/a&gt;.  Unfortunately, background audio still stops as soon as you leave the PWA.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Do me a favor and go comment on &lt;a href="https://bugs.webkit.org/show_bug.cgi?id=198277"&gt;the webkit bug&lt;/a&gt; or &lt;a href="https://twitter.com/wes_goulet/status/1280873047700828160"&gt;retweet this&lt;/a&gt; to help get this bug prioritized by the Webkit team.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Manifest.json Icon Ignored
&lt;/h4&gt;

&lt;p&gt;The standard way to define icons for a PWA is to set the &lt;a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/icons"&gt;icons object in manifest.json&lt;/a&gt;.  Unfortunately iOS ignores that so if you want your PWA icon to show up properly on the user's home screen you will still need to add the following to your HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"apple-touch-icon"&lt;/span&gt; &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"192x192"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"./logo.png"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's easy enough, but it would be nice if you only had to declare your icons in one place and it just works for Android &lt;em&gt;and&lt;/em&gt; iOS (and Windows and Linux, etc, etc).&lt;/p&gt;

&lt;h4&gt;
  
  
  Push Notifications
&lt;/h4&gt;

&lt;p&gt;It's hard for a PWA to rival a native app in terms of user experience without something as basic a push notifications, so it's disappointing to see this still isn't implemented on iOS 14.&lt;/p&gt;

&lt;h4&gt;
  
  
  BeforeInstallPromptEvent API
&lt;/h4&gt;

&lt;p&gt;This API allows web apps to programmatically pin the web app to the user's home screen as a PWA.  This is a non-standard API so it's unlikely for Apple to implement it, &lt;strong&gt;but it would be nice if there were a programmatic way to add to homescreen&lt;/strong&gt;.  Having a button the user can tap to install the current site as a PWA is a lot nicer experience than asking the user to dig into the share menu in Safari.  Also, "Add to Home Screen" is only available in Safari, not in 3rd party browsers.&lt;/p&gt;

&lt;h4&gt;
  
  
  PWAs in App Library
&lt;/h4&gt;

&lt;p&gt;iOS 14 introduces the concept of App Libraries to automatically group apps together.  What I've noticed so far is any PWAs I add to my home screen don't show up in the "Recently Added" or "Suggestions" app libraries.  In fact, all I've used so far since installing iOS 14 are the Twitter and Background Noise PWAs and yet they don't show up anywhere in my App Library.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fzvtgzrwy809khcvz3xzc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fzvtgzrwy809khcvz3xzc.png" alt="No PWAs in App Library" width="462" height="1000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It would be nice if PWAs got the same treatment as native apps for such a visible OS feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapup
&lt;/h2&gt;

&lt;p&gt;There are still some major roadblocks on iOS that prevent PWAs from having parity with native apps when it comes to user experience and OS integration.  Developing PWAs for iOS can feel pretty frustrating when compared to Android and Windows.  However, I appreciate that Safari is steadily implementing more web standards.  I'll try and keep this post updated with my findings as new iOS 14 betas get released.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Follow me &lt;a href="https://twitter.com/wes_goulet"&gt;on Twitter&lt;/a&gt; for more writings about PWAs, web components, mobile development, and adventures in product development.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/videos/play/wwdc2020/10663"&gt;WWDC Web Developers Session&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/videos/play/wwdc2020/10670"&gt;WWDC Face ID and Touch ID for the web&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.google.com/speed/webp"&gt;WebP Image Format&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>pwa</category>
      <category>ios</category>
      <category>web</category>
      <category>development</category>
    </item>
  </channel>
</rss>
