<?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>DEV Community: Anthony Frehner</title>
    <description>The latest articles on DEV Community by Anthony Frehner (@frehner).</description>
    <link>https://dev.to/frehner</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%2F318259%2F5eb6381d-65ad-4644-9949-fcc9bf9d6f32.jpeg</url>
      <title>DEV Community: Anthony Frehner</title>
      <link>https://dev.to/frehner</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/frehner"/>
    <language>en</language>
    <item>
      <title>Composing JavaScript Decorators</title>
      <dc:creator>Anthony Frehner</dc:creator>
      <pubDate>Wed, 10 Jul 2024 01:33:34 +0000</pubDate>
      <link>https://dev.to/frehner/composing-javascript-decorators-2o38</link>
      <guid>https://dev.to/frehner/composing-javascript-decorators-2o38</guid>
      <description>&lt;p&gt;A walkthrough and best practices guide on how to compose JavaScript decorators that use auto-accessors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;p&gt;Consider skipping straight to Best Practices!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Context and Specification&lt;/li&gt;
&lt;li&gt;Preface&lt;/li&gt;
&lt;li&gt;
Composing Decorators

&lt;ul&gt;
&lt;li&gt;Non-Functional Chaining Example&lt;/li&gt;
&lt;li&gt;Writing Composable Decorators&lt;/li&gt;
&lt;li&gt;Order Matters&lt;/li&gt;
&lt;li&gt;Best Practices&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Context and Specification
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/tc39/proposal-decorators" rel="noopener noreferrer"&gt;Decorators Proposal on GitHub&lt;/a&gt; and &lt;a href="https://dev.to/frehner/javascript-decorators-and-auto-accessors-437i"&gt;my previous article on decorators&lt;/a&gt; already do a great job of breaking down the basic use-cases of decorators. My goal isn't to recreate those examples there, but instead to highlight some lesser-known features and interactions. Additionally, I'll highlight how to compose or chain multiple decorators on a single class property.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preface
&lt;/h2&gt;

&lt;p&gt;Each code sample will come with a link to an interactive &lt;a href="https://babeljs.io/repl#?browsers=defaults%2C%20not%20ie%2011%2C%20not%20ie_mob%2011&amp;amp;build=&amp;amp;builtIns=false&amp;amp;corejs=3.21&amp;amp;spec=false&amp;amp;loose=false&amp;amp;code_lz=Q&amp;amp;debug=false&amp;amp;forceAllTransforms=false&amp;amp;modules=false&amp;amp;shippedProposals=false&amp;amp;circleciRepo=&amp;amp;evaluate=true&amp;amp;fileSize=false&amp;amp;timeTravel=false&amp;amp;sourceType=module&amp;amp;lineWrap=false&amp;amp;presets=env%2Creact%2Cstage-2&amp;amp;prettier=false&amp;amp;targets=&amp;amp;version=7.24.7&amp;amp;externalPlugins=&amp;amp;assumptions=%7B%7D" rel="noopener noreferrer"&gt;Babel REPL playground&lt;/a&gt;, so you can try it for yourself without needing to set up a polyfill or spin up a repo. The "Evaluate" option in the top left (under &lt;code&gt;Settings&lt;/code&gt;) should be checked in all my examples, which means that you will be able to see the code, edit it, open your browser's dev console, and see the logs / results there. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You don't need to pay attention to the transpiled code on the right-hand side of the Babel REPL&lt;/strong&gt;, unless you want to dig into the polyfill for decorators. The left-hand side of the Babel REPL is where you can edit and write code to try out for yourself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To emphasize, your developer tools' console should show console logs. If it doesn't, make sure that &lt;code&gt;Evaluate&lt;/code&gt; is checked in the top left.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Composing Decorators
&lt;/h2&gt;

&lt;p&gt;One important use-case that the proposal doesn't show is how to compose (or combine multiple) decorators on a single property. This is a powerful tool which helps keep your decorators clean and reusable, so let's dig into it.&lt;/p&gt;

&lt;p&gt;In my &lt;a href="https://dev.to/frehner/javascript-decorators-and-auto-accessors-0-temp-slug-979005?preview=a74f965083163194f3db492b71b3b55162692b20ef9cc16fa970ea901af13a31d52d998dad6ecc7df2a94c63a58e4ed8d62a040f08f7e3b630edd6c0"&gt;previous article on decorators&lt;/a&gt;, it's was annoying to have to manually &lt;code&gt;console.log&lt;/code&gt; after changing the value, so let's make a decorator that does that automatically for us - all while keeping the &lt;code&gt;evensOrOdds&lt;/code&gt; decorator there, too!&lt;/p&gt;

&lt;h3&gt;
  
  
  Non-Functional Chaining Example
&lt;/h3&gt;

&lt;p&gt;My first impression when working with chaining decorators is that it would be as simple as adding the decorator and things would "Just Work". Unfortunately, that isn't the case; in this example below, I've added the &lt;code&gt;logOnSet&lt;/code&gt; decorator before and after a property in order to demonstrate the issue. &lt;a href="https://babeljs.io/repl#?browsers=defaults%2C%20not%20ie%2011%2C%20not%20ie_mob%2011&amp;amp;build=&amp;amp;builtIns=false&amp;amp;corejs=3.21&amp;amp;spec=false&amp;amp;loose=false&amp;amp;code_lz=GYVwdgxgLglg9mABAUwG7LAZwPICdsAmBmAFAgDYCeAoulogLyJS4jICUiA3gFCKK5kUELiShIsBIgLIIcXAEMo8kqgXk2AGkRywUZAA8onXvwCQ5IYhh7ko9QDkQAWwBGdxogAMfRGcHCoty-_PwA5kIkJiGh_kIiSDb69uRObnYx_AC-mpmImJFq5NGhsbqYUIhgLp5p7riq6ux5ZjDAJDCYDgoOJNXO7CWlsQD0I9IIAOSVBZVQABbIiEVs1sDWUJOYVXCVClUu9XnmAQnWtil1GcN-WS1tfTUApIgATIgAhAxMZGBUtBhtgB-byIABciAAjINgmY4acgkk7GBHIdrsMzHcbq0LijUmjcJ4iscBPFEbjUelcHksbdfHc7jxxNB4EhyHAwtgwABlSIAB0EwBgBk8kwUIGU7LCEVwkyGCLE4BZUhkckUygaK2Q2l0-iMQ34CuCN1mjWKxpuOgQmDglgAdFKSALkEKDNois1LaTAkhiTdadl6TxGRByApMNsALKUADCYYjFoAAmhAXhCMQSCw2J7EImpVzeVASJMU2AALRSmVguW-BQQCDICPyRDOGh0K7U_i-PMcguRSZwIgVjlVmv8ZN0HD4IikYDqAo5usNpuE1vpjvBng8cpzZDOPmeMDIADuiGjcfDpGa5VtyAdHOLpYhMjnIHIlS1k20-n3dtbALADtmh4H8-T_NsMA7UVFnIdlJm3a1b3vMJHzoCEFGAZJmFwSgbDCZg4HyKwbCKGACGWdQ2C_Zg9zA_92wJYDQPAgCoKYABmBCsCQx0SzQxAMKwlhcLAfDlCIuZCMHcj-nqajmPoyDGK3BSIMAglPAAFi4m17V4p8BMwjxZlgUSCJQOgDipeTaJYhiqWAsZEDLFzXLclytxvPSHwHIhnxdcV3wojRkBs381yIICVNsiKCDYxBJhguCdJ4nzpPQozCWEvDzNmc5SPIz9vxiyh12UkCSrKqlPE4ry7149LDKEnCcvEvLxOkqy5OK8LSsi8rVKq-otJS7yUN8ggMqwkzWsI0sursMK6L6uLGIAbiAA&amp;amp;debug=false&amp;amp;forceAllTransforms=false&amp;amp;modules=false&amp;amp;shippedProposals=false&amp;amp;circleciRepo=&amp;amp;evaluate=true&amp;amp;fileSize=false&amp;amp;timeTravel=false&amp;amp;sourceType=module&amp;amp;lineWrap=false&amp;amp;presets=env%2Creact%2Cstage-2&amp;amp;prettier=false&amp;amp;targets=&amp;amp;version=7.24.7&amp;amp;externalPlugins=&amp;amp;assumptions=%7B%7D" rel="noopener noreferrer"&gt;Babel REPL&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="c1"&gt;// THIS DOES NOT WORK CORRECTLY, DO NOT USE&lt;/span&gt;

&lt;span class="c1"&gt;// code collapsed from previous examples&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;evensOrOdds&lt;/span&gt;&lt;span class="p"&gt;(){}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;logOnSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;autologger&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;decorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&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="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;val&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;val&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;evensOrOdds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;logOnSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;even-logger:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;accessor&lt;/span&gt; &lt;span class="nx"&gt;myEvenNumber&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;logOnSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;odd-logger:&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="nd"&gt;evensOrOdds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;accessor&lt;/span&gt; &lt;span class="nx"&gt;myOddNumber&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you open the Babel REPL and look at the console logs, you may notice a couple of things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;even-logger&lt;/code&gt; never shows up 🤔&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;myOddNumber&lt;/code&gt; is always &lt;code&gt;0&lt;/code&gt;, even though the &lt;code&gt;odd-logger&lt;/code&gt; shows that there's a different value that's trying to be set 🤔&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Despite the order we place the decorators in, nothing seems to be working as we expect it to. What's going on?&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing Composable Decorators
&lt;/h3&gt;

&lt;p&gt;There's an important line in the decorators proposal that affects how multiple decorators work:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Accessor decorators receive the original underlying getter/setter function as the first value, and can optionally return a new getter/setter function to replace it. Like method decorators, this new function is placed on the prototype in place of the original (or on the class for static accessors), and if any other type of value is returned, an error will be thrown.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In other words, the last-applied decorator's setter/getter methods win out, and none of the other decorators setters/getters are applied.&lt;/p&gt;

&lt;p&gt;Since only one setter/getter is ever applied, how would we chain decorators?&lt;/p&gt;

&lt;p&gt;Fortunately, our decorator is passed the previous decorator's setter/getter methods (or the native getter/setters if there are no others), so we can call them ourselves! Let's update the &lt;code&gt;logOnSet&lt;/code&gt; decorator to do this, using the &lt;code&gt;value&lt;/code&gt; parameter that the decorator is passed: &lt;a href="https://babeljs.io/repl#?browsers=defaults%2C%20not%20ie%2011%2C%20not%20ie_mob%2011&amp;amp;build=&amp;amp;builtIns=false&amp;amp;corejs=3.21&amp;amp;spec=false&amp;amp;loose=false&amp;amp;code_lz=GYVwdgxgLglg9mABAUwG7LAZwPICdsAmBmAFAgDYCeAoulogLyJS4jICUiA3gFCKK5kUELiShIsBIgLIIcXAEMo8kqgXk2AGkRywUZAA8onXvwCQ5IYhh7ko9QDkQAWwBGdxogAMfRGcHCoty-_PwA5kIkJiGh_kIiSDb69uRObnYx_AC-mpmImJFq5NGhsbqYUIhgLp5p7riq6ux5ZjDAJDCYDgoOJNXO7CWlsQD0I9IIAOSVBZVQABbIiEVs1sDWUJOYVXCVClUu9XnmAQnWtil1GcN-WS1tfTUApIgATIgAhAxMZGBUtBhtgB-byIABciAAjINgmY4acgkk7GBHIdrsMzHcbq0LijUmjcJ4iscBPFEbjUelcHksbdfHc7jxxNB4EhyHAwtgwABlSIAB0EwBgBk8kwUIGU7LCEVwkyGCLE4BZUhkckUygaK2Q2l0-iMQ34CuCN1mjWKxpuOgQFUQArQ8BAmF5UGSADV1KsmFqAHSzb0QdTkEgLTraIqcIEg4mWq1YOCWb1Skh2oUGbR21AOp1CN0ejgko0ZrPO3MaZA0mK0hk8HgQcgKTDbACylAAwvXGxaAAJoQF4QjEYOsfP8LtSrnOkiTXtgAC0UplYLlvgUEAgyEb8kQzhodCu1P4vjHHInkUmcCI845i-Xo5nOHwRFIwHUBWa_FX683hJ3A_3PEZco5mQZw-U8MBkAAd0QFt2wbUhmnKeNkETDkpxnCEZBfEByEqLVJm0fRQO9HcATAfdmh4Ii-RI3cMH3UVFnIdlJlra1kNQsJ0LoCEFGAZJmFwSgbDCZg4HyKwbCKGACGWPMCOYECaNIvcCUo6jaLIhimAAZjYuMEyTaceMQPiBJYYSwFE5QJLmcSL1k_p6gUjSVPotSa1cujyIJTwABZ9MwDijIw0z-I8WZYCssSUDoA4qRcpTNNUqlKLGRBZ0yrLstnGskMMtDzyITDkGw3C5LLRLiN_IgKM8pKaoIbTEEmJiWMC4LCoc3jwsJCyRJi2Zzmk2T8MIhrKD_DyqImqaqU8PT8pQozurC8yhIGmyhpshz4uc8bqsm2rpq8ub6n8jqCq4oqCB6gTIs28SZz2uwquUo6mrUgBuIA&amp;amp;debug=false&amp;amp;forceAllTransforms=false&amp;amp;modules=false&amp;amp;shippedProposals=false&amp;amp;circleciRepo=&amp;amp;evaluate=true&amp;amp;fileSize=false&amp;amp;timeTravel=false&amp;amp;sourceType=module&amp;amp;lineWrap=false&amp;amp;presets=env%2Creact%2Cstage-2&amp;amp;prettier=false&amp;amp;targets=&amp;amp;version=7.24.7&amp;amp;externalPlugins=&amp;amp;assumptions=%7B%7D" rel="noopener noreferrer"&gt;Babel REPL&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;logOnSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;autologger&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;decorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&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="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&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;previousSetterValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;previousSetterValue&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;previousSetterValue&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That fixes the &lt;code&gt;myOddNumber&lt;/code&gt; property and logger, but &lt;code&gt;myEvenNumber&lt;/code&gt; still isn't working correctly; we need to do the same thing in the &lt;code&gt;evensOrOdds&lt;/code&gt; decorator, too! &lt;a href="https://babeljs.io/repl#?browsers=defaults%2C%20not%20ie%2011%2C%20not%20ie_mob%2011&amp;amp;build=&amp;amp;builtIns=false&amp;amp;corejs=3.21&amp;amp;spec=false&amp;amp;loose=false&amp;amp;code_lz=GYVwdgxgLglg9mABAUwG7LAZwPICdsAmBmAFAgDYCeAoulogLyJS4jICUiA3gFCKK5kUELiShIsBIgLIIcXAEMo8kqgXk2AGkRywUZAA8onXvwCQ5IYhh7ko9QDkQAWwBGdxogAMfRGcHCoty-_PwA5kIkJiGh_AD0cdZgqHAA1sjMABYZAA6CqPAgmIgRUPq42q4gUIgUlAJCIkhwIohq5DAEiGAu7rgxse1sAHSlwxDq5CRQmTCY7AN-AU1J5WCOvXaLAL6ai5iR7dGxsbqYNXlohZgAykLlAGrqbJ5DyMMHUOOT07OY2kdEAB-IFtdSLMxnGo9ZyeJxuOwkS4FFq3e52J4aDgQmDAEhzBwKBwkGHsY4ncwJaQIADkNU-WQyb2swGsUBpxTAcBqCm6m36FPMyyCNjWGwRApOZm2OLxMMQAFJEAAmRAAQgYTDIYCotAwxVBXkQAC5EABGMnBMzW4VIUV2dbkeF9CEyilme32J3817gwUNQJ22xe51bCluvwRmUynjiaDwJDkOBhbBgO5QJGCYAwAyeGkKapwJNhCK4Gnk22IOOSJAyOSKZS4VTPZDaXT6Izk_iV0wnT7N8hdk5QxDI67px4t31Yj5Cb7kKYzOYA9ScEFg8iLfhnIvvYuZ5DZgzaMeoicYlsLf2V09Fc-4TFsHYxKM8GMQcgKTDFACylAAwp-37BPwAACaD6nghDENMrDYmBxapumJA0hBYAALTFqWxrlr4CgQBAyDfvIiDODQdChpKvigYhaaRDScBEJhybYbhYFoTg-BEKQwDqAcV6IPhhHEbgpGUNBlFvjwPBUjuljjJYChNgsPAjvozg5J4YDIAA7ogf6AV-pALHJe7JihaGmjIvEgOQNRvDS2jqTkwxkXqYCUSpzmueRGCUXm2QLnANKqQgmC7sM-6oXQpoKMA5TMLglA2GEzBwIgDI2O0nQbmwjnMMgGk-e5nnSd5bkUT6TAAMyhVgEVRZZgnxR4LDJWAqXKBlVhdYxXQwn0-Xlb5Hn8l5hUuRVflVYgAAsdXhfJjUxc1CWfLAHVpSgdB8hKQ0TcVlUSipVLoWd50XdJpmReZDFEFZh4FnZuXIPtRVkRJY1lQdH1EP5TA0oFSYhddUV9bFLWiW1KVbZlyTqDlDlOT94l_V9PDDZ9EqeLVoO3eDq2tUlMNdQyvVELtg3I-9qMEKVGMo1jfSePNeNhChBNxWt9wk-laGU3Yb2TbTnkANxAA&amp;amp;debug=false&amp;amp;forceAllTransforms=false&amp;amp;modules=false&amp;amp;shippedProposals=false&amp;amp;circleciRepo=&amp;amp;evaluate=true&amp;amp;fileSize=false&amp;amp;timeTravel=false&amp;amp;sourceType=module&amp;amp;lineWrap=false&amp;amp;presets=env%2Creact%2Cstage-2&amp;amp;prettier=false&amp;amp;targets=&amp;amp;version=7.24.7&amp;amp;externalPlugins=&amp;amp;assumptions=%7B%7D" rel="noopener noreferrer"&gt;Babel REPL&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;evensOrOdds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onlyEvens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;decorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&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="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;internalNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// invoke the previous getter, but only return our valid number&lt;/span&gt;
        &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&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;internalNumber&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&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;previousSetterValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;previousSetterValue&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="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// don't set the value if it's not a number&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;internalNumber&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;num&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onlyEvens&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&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;internalNumber&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;internalNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;internalNumber&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note how we're calling the previous getter/setter with &lt;code&gt;.call(this, val)&lt;/code&gt;, which helps ensure that the &lt;code&gt;this&lt;/code&gt; value is consistent. Importantly, we're also using the nullish coalescing operator &lt;code&gt;?? val&lt;/code&gt; at the end to ensure that, if the previous setter/getter doesn't return anything, we keep the original &lt;code&gt;val&lt;/code&gt; and use that.&lt;/p&gt;

&lt;h3&gt;
  
  
  Order Matters
&lt;/h3&gt;

&lt;p&gt;Now that it's all working, let's take another close look at the logs. You may note that the &lt;code&gt;even-logger&lt;/code&gt; logs the value &lt;em&gt;before&lt;/em&gt; it is validated by our &lt;code&gt;evensOrOdds&lt;/code&gt; decorator, while the &lt;code&gt;odd-logger&lt;/code&gt; logs the value &lt;em&gt;after&lt;/em&gt; the value has been validated.&lt;/p&gt;

&lt;p&gt;It's important to note that the order of our chained decorators is different for each property:&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="c1"&gt;// weird ordering on decorators&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;evensOrOdds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;logOnSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;even-logger:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;accessor&lt;/span&gt; &lt;span class="nx"&gt;myEvenNumber&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;logOnSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;odd-logger:&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="nd"&gt;evensOrOdds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;accessor&lt;/span&gt; &lt;span class="nx"&gt;myOddNumber&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Decorators are executed from bottom-to-top, just like normal JavaScript functions are executed right-to-left: &lt;code&gt;last(first())&lt;/code&gt;. We want our logger to be logging values &lt;em&gt;after&lt;/em&gt; validation, so let's reorder the decorators on &lt;code&gt;myEvenNumber&lt;/code&gt; (and finally remove all the other console logs): &lt;a href="https://babeljs.io/repl#?browsers=defaults%2C%20not%20ie%2011%2C%20not%20ie_mob%2011&amp;amp;build=&amp;amp;builtIns=false&amp;amp;corejs=3.21&amp;amp;spec=false&amp;amp;loose=false&amp;amp;code_lz=GYVwdgxgLglg9mABAUwG7LAZwPICdsAmBmAFAgDYCeAoulogLyJS4jICUiA3gFCKK5kUELiShIsBIgLIIcXAEMo8kqgXk2AGkRywUZAA8onXvwCQ5IYhh7ko9QDkQAWwBGdxogAMfRGcHCoty-_PwA5kIkJiGh_AD0cdZgqHAA1sjMABYZAA6CqPAgmIgRUPq42q4gUIgUlAJCIkhwIohq5DAEiGAu7rgxse1sAHSlwxDq5CRQmTCY7AN-AU1J5WCOvXaLAL6ai5iR7dGxsbqYNXlohZgAykLlAGrqbJ5DyMMHUOOT07OY2kdEAB-IFtdSLMxnGo9ZyeJxuOwkS4FFq3e52J4aDgQmDAEhzBwKBwkGHsY4ncwJaQIADkNU-WQyb2swGsUBpxTAcBqCm6m36FPMyyCNjWGwRApOZm2OLxMMQAFJEAAmRAAQgYTDIYCotAwxVBXkQAC5EABGMnBMzW4VIUV2dbkeF9CEyilme32J3817gwUNQJ22xe51bCluvwRmUynjiaDwJDkOBhbBgO5QJGCYAwAyeGkKapwJNhCK4Gnk22IOOSJAyOSKZS4VTPZDaXT6Izk_iV0wnT7N8hdk5QxDI67px4t31Yj5Cb7kKYzOYA9ScEFg8iLfhnIvvYuZ5DZgzaMeoicYlsLf2V09Fc-4TFsHYxKM8GMQcgKTDFACylAAwp-37BPwAACxapumJA0mgGAALTFqWxrlr4oGwVgeCEMQ0ysNi_AKBAEDIN-8iIM4NB0KGkqoRBaaRDScBEAhyZIShYHoTg-BEKQwDqAcV6IARREkbgZGUFhVFvjwPA7pY4yWAoTYLDJCDnMwyDODknhgMgADuiB_oBX6kAssl7sm0HoaaMi8SA5A1G8NLaPomnDORepgFRykuTkbkURgVF5tkC5wDS0k-X5HmBUwADM4Uab57mUT6TAACzSVScFZdlOXSWZwz7gxRDWYeBb2RubBOeprnkRJ_LeQlfl1RKQXICFYU8BFtVENFiBxZ1jXdQQvWpUAA&amp;amp;debug=false&amp;amp;forceAllTransforms=false&amp;amp;modules=false&amp;amp;shippedProposals=false&amp;amp;circleciRepo=&amp;amp;evaluate=true&amp;amp;fileSize=false&amp;amp;timeTravel=false&amp;amp;sourceType=module&amp;amp;lineWrap=false&amp;amp;presets=env%2Creact%2Cstage-2&amp;amp;prettier=false&amp;amp;targets=&amp;amp;version=7.24.7&amp;amp;externalPlugins=&amp;amp;assumptions=%7B%7D" rel="noopener noreferrer"&gt;Babel REPL&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="c1"&gt;// better ordering on decorators&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;logOnSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;even-logger:&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="nd"&gt;evensOrOdds&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="nx"&gt;accessor&lt;/span&gt; &lt;span class="nx"&gt;myEvenNumber&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;logOnSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;odd-logger:&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="nd"&gt;evensOrOdds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;accessor&lt;/span&gt; &lt;span class="nx"&gt;myOddNumber&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those logs look better, and we've only had to write our console log logic once, too!&lt;/p&gt;

&lt;h3&gt;
  
  
  Best Practices
&lt;/h3&gt;

&lt;p&gt;A TLDR of the "Writing Composable Decorators" section.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Call the the previous decorator's setter/getter:  &lt;code&gt;value.set.call(this, val)&lt;/code&gt; to enable chaining&lt;/li&gt;
&lt;li&gt;Fall back to the original value if the previous decorator's setter/getter doesn't return anything: &lt;code&gt;value.set.call(this, val) ?? val&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Return a value in your setter/getter, so that other decorators can easily chain it: &lt;code&gt;return internalNumber&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Ensure the order is correct - decorators execute from bottom-to-top!&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>JavaScript Decorators and Auto-Accessors</title>
      <dc:creator>Anthony Frehner</dc:creator>
      <pubDate>Wed, 10 Jul 2024 01:30:30 +0000</pubDate>
      <link>https://dev.to/frehner/javascript-decorators-and-auto-accessors-437i</link>
      <guid>https://dev.to/frehner/javascript-decorators-and-auto-accessors-437i</guid>
      <description>&lt;p&gt;A walkthrough of how to create JavaScript decorators and how using auto-accessors helps improve your developer experience. &lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Context and Specification&lt;/li&gt;
&lt;li&gt;Preface&lt;/li&gt;
&lt;li&gt;Auto-Accessors&lt;/li&gt;
&lt;li&gt;
Creating Decorators

&lt;ul&gt;
&lt;li&gt;A Simple Decorator&lt;/li&gt;
&lt;li&gt;Validation With Decorators&lt;/li&gt;
&lt;li&gt;Decorator Options&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Metadata&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Context and Specification
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/tc39/proposal-decorators" rel="noopener noreferrer"&gt;Decorators Proposal on GitHub&lt;/a&gt; already does a great job of breaking down the basic use-cases of decorators. My goal isn't to recreate those examples there, but instead to highlight some lesser-known features and interactions. Additionally, in the next article in this series I'll highlight how to compose or chain multiple decorators on a single class property.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preface
&lt;/h2&gt;

&lt;p&gt;Each code sample will come with a link to an interactive &lt;a href="https://babeljs.io/repl#?browsers=defaults%2C%20not%20ie%2011%2C%20not%20ie_mob%2011&amp;amp;build=&amp;amp;builtIns=false&amp;amp;corejs=3.21&amp;amp;spec=false&amp;amp;loose=false&amp;amp;code_lz=Q&amp;amp;debug=false&amp;amp;forceAllTransforms=false&amp;amp;modules=false&amp;amp;shippedProposals=false&amp;amp;circleciRepo=&amp;amp;evaluate=true&amp;amp;fileSize=false&amp;amp;timeTravel=false&amp;amp;sourceType=module&amp;amp;lineWrap=false&amp;amp;presets=env%2Creact%2Cstage-2&amp;amp;prettier=false&amp;amp;targets=&amp;amp;version=7.24.7&amp;amp;externalPlugins=&amp;amp;assumptions=%7B%7D" rel="noopener noreferrer"&gt;Babel REPL playground&lt;/a&gt;, so you can try it for yourself without needing to set up a polyfill or spin up a repo. The "Evaluate" option in the top left (under &lt;code&gt;Settings&lt;/code&gt;) should be checked in all my examples, which means that you will be able to see the code, edit it, open your browser's dev console, and see the logs / results there. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You don't need to pay attention to the transpiled code on the right-hand side of the Babel REPL&lt;/strong&gt;, unless you want to dig into the polyfill for decorators. The left-hand side of the Babel REPL is where you can edit and write code to try out for yourself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To emphasize, your developer tools' console should show console logs. If it doesn't, make sure that &lt;code&gt;Evaluate&lt;/code&gt; is checked in the top left.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Auto-Accessors
&lt;/h2&gt;

&lt;p&gt;An important feature of the Decorators spec are auto-accessors. We'll start with learning what they are and how using auto-accessor will make writing decorators easier.&lt;/p&gt;

&lt;p&gt;The the Decorators Proposal &lt;a href="https://github.com/tc39/proposal-decorators?tab=readme-ov-file#class-auto-accessors" rel="noopener noreferrer"&gt;outlines auto-accessor here&lt;/a&gt;. But ultimately it's a simple feature; let's look at a basic working example: &lt;a href="https://babeljs.io/repl#?browsers=defaults%2C%20not%20ie%2011%2C%20not%20ie_mob%2011&amp;amp;build=&amp;amp;builtIns=false&amp;amp;corejs=3.21&amp;amp;spec=false&amp;amp;loose=false&amp;amp;code_lz=MYGwhgzhAECyCeBhcVoG8BQ1pmMAplAPYBO0AtvAEJFEj5gB20AvNAGZggT4YC-GDMCKMIAF2hj85AA6tojfAHc4SFBAAUASiEiIdfADoQRAOYaA5OwCWJcRYA0k6TMOUaBpjoxTZb6rT0TPJiJACu-ADcuqIGxmaWPMKMACaOzn7ugQyMWkA&amp;amp;debug=false&amp;amp;forceAllTransforms=false&amp;amp;modules=false&amp;amp;shippedProposals=false&amp;amp;circleciRepo=&amp;amp;evaluate=true&amp;amp;fileSize=false&amp;amp;timeTravel=false&amp;amp;sourceType=module&amp;amp;lineWrap=false&amp;amp;presets=env%2Creact%2Cstage-2&amp;amp;prettier=false&amp;amp;targets=&amp;amp;version=7.24.7&amp;amp;externalPlugins=&amp;amp;assumptions=%7B%7D" rel="noopener noreferrer"&gt;Babel REPL&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;accessor&lt;/span&gt; &lt;span class="nx"&gt;myBoolean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this class definition the &lt;code&gt;accessor&lt;/code&gt; keyword goes before the property name. However, this hasn't really changed anything about the property yet - next, we'll see how useful auto-accessors are when combined with decorators.&lt;/p&gt;

&lt;p&gt;(Note that you can also use &lt;code&gt;static&lt;/code&gt; with auto-accessors, such as &lt;code&gt;static accessor myBoolean = false&lt;/code&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating Decorators
&lt;/h2&gt;

&lt;p&gt;To better understand why we're using an auto-accessor, let's build some decorators.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Simple Decorator
&lt;/h3&gt;

&lt;p&gt;We'll start by combining auto-accessors with a decorator that doesn't actually do much, in order to get an idea of the syntax.&lt;/p&gt;

&lt;p&gt;Here's a functional decorator that keeps an internal variable, and allows you to get and set that variable through the property on the class: &lt;a href="https://babeljs.io/repl#?browsers=defaults%2C%20not%20ie%2011%2C%20not%20ie_mob%2011&amp;amp;build=&amp;amp;builtIns=false&amp;amp;corejs=3.21&amp;amp;spec=false&amp;amp;loose=false&amp;amp;code_lz=GYVwdgxgLglg9mABAZxgWwA4BsCmARHCOAJwEMoSAKAN1KxBwBpEiwocAPKASkQG8AUIkS4oiGGxzEwdAGp0GiALyJgdZDiGJiOKCGn8twgOa7KvQcKvbd-pBPbS5CzdYC-jIyjO0sFr8IOUjJY8vQ4yoi-ATZ6BkFOoS5eblqpqQIQWKTIyIgAsgCeAMLZuYbCAAKomLgERGQUxFqkEBA4uSSIaIUAQnBwuKRgAhmsyGLsmJFgOADuBSVlyOaZCMiDOAB0WHDGlADkwDDEEyJ7B8xTGFs9_ZvD3ALXt30DQ0gqUMQMANxrYA2uB2e0OGlYABNzsZLogXnd3jhHgIBEA&amp;amp;debug=false&amp;amp;forceAllTransforms=false&amp;amp;modules=false&amp;amp;shippedProposals=false&amp;amp;circleciRepo=&amp;amp;evaluate=true&amp;amp;fileSize=false&amp;amp;timeTravel=false&amp;amp;sourceType=module&amp;amp;lineWrap=false&amp;amp;presets=env%2Creact%2Cstage-2&amp;amp;prettier=false&amp;amp;targets=&amp;amp;version=7.24.7&amp;amp;externalPlugins=&amp;amp;assumptions=%7B%7D" rel="noopener noreferrer"&gt;Babel REPL&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;simpleDecorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&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="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;internalValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;get&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;internalValue&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;internalValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;internalValue&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;simpleDecorator&lt;/span&gt;
  &lt;span class="nx"&gt;accessor&lt;/span&gt; &lt;span class="nx"&gt;myBoolean&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This decorator returns an object with two methods: &lt;code&gt;get()&lt;/code&gt; and &lt;code&gt;set()&lt;/code&gt;. This is how a decorator for an auto-accessor can "decorate" or wrap both the setter and the getter for a property in a single place; we don't have to create a &lt;code&gt;simpleGetterDecorator&lt;/code&gt; and &lt;code&gt;simpleSetterDecorator&lt;/code&gt;. Instead, we've combined them into a single definition with auto-accessors, which is easier.&lt;/p&gt;

&lt;p&gt;In the end, this looks like a fairly normal function so far - which is great for an introduction!&lt;/p&gt;

&lt;h3&gt;
  
  
  Validation With Decorators
&lt;/h3&gt;

&lt;p&gt;To set us up for the rest of the article, let's update our decorator so that it actually does some sort of validation. We'll make a decorator that only allows you to set even numbers and nothing else. Here's what that would look like: &lt;a href="https://babeljs.io/repl#?browsers=defaults%2C%20not%20ie%2011%2C%20not%20ie_mob%2011&amp;amp;build=&amp;amp;builtIns=false&amp;amp;corejs=3.21&amp;amp;spec=false&amp;amp;loose=false&amp;amp;code_lz=GYVwdgxgLglg9mABAgNgTwKIDcCmYByIAtgEY4BOAzgBRYCGKIOANIhAlDgB5QCUiAbwBQiRChxREMMJ3JgGhUhUQBeRAAYRichJBzBW0QHMJ1fsNGXtu_dNnyUisuUOIAvs1eVT9FOdei7GCUkmDEqohOFLQMvAFSwNQwlPh0-NRhRLz-VlYA9HmIACYIAOSS3pJQABY4iL5MCVJQpZSIYHCSdO3EzvGiOlB6SHYUDlEuue7xMImZiACkiABMiACEKmrqOVOIBcVlXSgocADuyEVFPUpUQgCQd4PDUjJjCr0U8W4zr3LvNxFfPEnrZfuMPpNLN9RN9vkIICg6JQ2gBZNAAYURyIMogAAqhMLgCBDKFo6BAIDhkXByIgiIS8BMhHCgiFEJwiAAHCJgHDnNGYpE0OKsuDiAB0JyM1FKRRwwDoIBQkgaOFKrA5nPF9OwjIhcSEmu1DOJALUUHITHhCEoYpwkrg0tKdGAsnZ5DQ0iM7LgiEqL18MCuqvV7JwXONutNzgNRp1RImEQAzNbgnaHU6XW6LZ6wN6oL7_QWLldMs5Q3GTRNY-GtfG9WbEAAWVO2iVSmVZ5SVWB5n2IHBE67ljW1yMJ_VCIRAA&amp;amp;debug=false&amp;amp;forceAllTransforms=false&amp;amp;modules=false&amp;amp;shippedProposals=false&amp;amp;circleciRepo=&amp;amp;evaluate=true&amp;amp;fileSize=false&amp;amp;timeTravel=false&amp;amp;sourceType=module&amp;amp;lineWrap=false&amp;amp;presets=env%2Creact%2Cstage-2&amp;amp;prettier=false&amp;amp;targets=&amp;amp;version=7.24.7&amp;amp;externalPlugins=&amp;amp;assumptions=%7B%7D" rel="noopener noreferrer"&gt;Babel REPL&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onlyEvenNumbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&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="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;internalNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;get&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;internalNumber&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&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;num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&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="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// don't set the value if it's not a number or coerced to a number&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;internalNumber&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;num&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// don't allow odd numbers&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;internalNumber&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;internalNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;internalNumber&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;onlyEvenNumbers&lt;/span&gt;
  &lt;span class="nx"&gt;accessor&lt;/span&gt; &lt;span class="nx"&gt;myEvenNumber&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we add logic to the &lt;code&gt;set()&lt;/code&gt; method and now anyone trying to set the &lt;code&gt;myEvenNumber&lt;/code&gt; property on our class will go through that validation logic. Nice.&lt;/p&gt;

&lt;h3&gt;
  
  
  Decorator Options
&lt;/h3&gt;

&lt;p&gt;Now that we have a nice only-evens decorator, let's make it handle both even and odd numbers with an option to configure which type of number we want!&lt;/p&gt;

&lt;p&gt;Fortunately, because this is fairly-normal-looking JavaScript we're writing here, it's not too hard to configure it to work this way. We wrap the original decorator with a function that takes in an option, and then return the decorator. &lt;a href="https://babeljs.io/repl#?browsers=defaults%2C%20not%20ie%2011%2C%20not%20ie_mob%2011&amp;amp;build=&amp;amp;builtIns=false&amp;amp;corejs=3.21&amp;amp;spec=false&amp;amp;loose=false&amp;amp;code_lz=GYVwdgxgLglg9mABAUwG7LAZwPICdsAmBmAFAgDYCeAoulogLyJS4jICUiA3gFCKK5kUELiShIsBIgLIIcXAEMo8kqgXk2AGkRywUZAA8onXvwCQ5IYhh7ko9QDkQAWwBGdxogAMfRGcHCoty-_PwA5kIkJiGh_kIiSDb69uRObnYx_AC-mpmImJFq5NGhsbqYUIhgLp5p7riq6ux5ZjDAJDCYDgoOJNXO7CWlsQD0I9IIAOSVBZVQABbIiEVs1sDWUJOYVXCVClUu9XnmAQnWtil1GcN-WS1tfTUApIgATIgAhAxMZGBUtBhtgB-byIABciAAjINgmY4acgkk7GBHIdrsMzHcbq0LijUmjcJ4iscBPFEbjUelcHksbdfHc7jwIOQFJhtgBZSgAYRZbOC_AAAmhAXhCMQSCw2M1-AoIBBkGz5IhnDQ6FdqfxfEK6Dh8ERSMB1AVpYhZfLFYSVWL1TxGeU5shnAAHTxgZAAd0QnJ5rNIzXKcEsADpyHAwiRJsKwBCZIaQORKitkJNtPpnUGVQCwOrmjw006M6qMOrPJNFuRQ5MmQhMIHkCGwxGoxCFMBksxcJQbGFmHB8lYbEUYARluo2CnmI6C5m1QTc_nC1mS0wAMzVrB1hvhyN0FttjwsLtgHvKftzPtwIgHKkThcz4tznh5qeL2dUzwAFnXteDoe3zdNfdCVmWBj17FA6Gvepbxfe9s0fHgxkQABaVC0LQp8A1_RtJkvAgY2QOME1HDRk1TWDKGtBC70oohl0QMtkArOAqyw-s_wjPC93bQ9u3A2ZziHEckxg9MrTo6iKKo99V2_TcONwohuIPTs-NPATTzwqC7FE6daIIHMnxo6T6k_OTsO3LjAPbEC1L7KNtNwXTCxMux2AAbiAA&amp;amp;debug=false&amp;amp;forceAllTransforms=false&amp;amp;modules=false&amp;amp;shippedProposals=false&amp;amp;circleciRepo=&amp;amp;evaluate=true&amp;amp;fileSize=false&amp;amp;timeTravel=false&amp;amp;sourceType=module&amp;amp;lineWrap=false&amp;amp;presets=env%2Creact%2Cstage-2&amp;amp;prettier=false&amp;amp;targets=&amp;amp;version=7.24.7&amp;amp;externalPlugins=&amp;amp;assumptions=%7B%7D" rel="noopener noreferrer"&gt;Babel REPL&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;evensOrOdds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onlyEvens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;decorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&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="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;internalNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;get&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;internalNumber&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&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;num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&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="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// don't set the value if it's not a number&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;internalNumber&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;num&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onlyEvens&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&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;internalNumber&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;internalNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;internalNumber&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;evensOrOdds&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="nx"&gt;accessor&lt;/span&gt; &lt;span class="nx"&gt;myEvenNumber&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;evensOrOdds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;accessor&lt;/span&gt; &lt;span class="nx"&gt;myOddNumber&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We've now configured our decorator to take in arbitrary options, which allows users of our decorator to customize its behavior. Yay.&lt;/p&gt;

&lt;h2&gt;
  
  
  Metadata
&lt;/h2&gt;

&lt;p&gt;One additional tool your decorators can utilize is &lt;code&gt;context.metadata&lt;/code&gt;. This object is passed to each decorator and you could use it for a variety of things, but you need to be careful because the metadata object is the same for all invocations of every decorator.&lt;/p&gt;

&lt;h2&gt;
  
  
  Continue Learning
&lt;/h2&gt;

&lt;p&gt;Continue to &lt;a href="https://dev.to/frehner/composing-javascript-decorators-2o38"&gt;the next post in the series&lt;/a&gt; to learn how to compose (or apply multiple) decorators to a single property!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The Modern Guide to Packaging Your JavaScript Library</title>
      <dc:creator>Anthony Frehner</dc:creator>
      <pubDate>Wed, 31 Aug 2022 19:17:12 +0000</pubDate>
      <link>https://dev.to/frehner/the-modern-guide-to-packaging-your-javascript-library-4gc2</link>
      <guid>https://dev.to/frehner/the-modern-guide-to-packaging-your-javascript-library-4gc2</guid>
      <description>&lt;p&gt;I had &lt;a href="https://twitter.com/frehner_a/status/1552829987195158532" rel="noopener noreferrer"&gt;a lot of questions&lt;/a&gt; when it came to packaging up my JavaScript library, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;minify?&lt;/li&gt;
&lt;li&gt;output to single file or maintain file/folder structure?&lt;/li&gt;
&lt;li&gt;have an esm build (or cjs)?&lt;/li&gt;
&lt;li&gt;transpile or not? (e.g. jsx -&amp;gt; createElement)&lt;/li&gt;
&lt;li&gt;package.exports setup?&lt;/li&gt;
&lt;li&gt;other package.json settings? (e.g. type)&lt;/li&gt;
&lt;li&gt;multiple outputs - dev/prod?&lt;/li&gt;
&lt;li&gt;what is "double bundling"?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With the hope of answering these questions, and more, I created the &lt;a href="https://github.com/frehner/modern-guide-to-packaging-js-library" rel="noopener noreferrer"&gt;Modern Guide to Packaging Your JavaScript Library&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;I created it on GitHub so that, as time goes on, it will (hopefully) remain fresh and updated - if I had written it as an article, then no one else could have contributed as easily, and it may have gone stale.&lt;/p&gt;

&lt;p&gt;Hopefully this guide is helpful to you, too.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>npm</category>
    </item>
    <item>
      <title>The Case for Browser Devtools to Emulate Mobile Viewport Changes</title>
      <dc:creator>Anthony Frehner</dc:creator>
      <pubDate>Thu, 18 Nov 2021 18:25:48 +0000</pubDate>
      <link>https://dev.to/frehner/the-case-for-browser-devtools-to-emulate-mobile-viewport-changes-50bh</link>
      <guid>https://dev.to/frehner/the-case-for-browser-devtools-to-emulate-mobile-viewport-changes-50bh</guid>
      <description>&lt;p&gt;When working on responsive components or websites, I frequently find myself wishing that my desktop browser could better emulate how a mobile browser &lt;strong&gt;actually&lt;/strong&gt; behaves - specifically, when it comes to resizing the viewport as you scroll up and down.&lt;/p&gt;

&lt;p&gt;And yes, it's true that nothing beats using an actual mobile device to test your code, that doesn't mean that we can't make the desktop developer experience better.&lt;/p&gt;

&lt;p&gt;These following cases are only &lt;em&gt;some&lt;/em&gt; of the examples of where the desktop's "Responsive Design Mode" experience doesn't actually match how mobile browsers behave:&lt;/p&gt;

&lt;h2&gt;
  
  
  CSS Viewport Units
&lt;/h2&gt;

&lt;p&gt;I've been working with viewport units, like &lt;code&gt;vh&lt;/code&gt;, for a long time. I've even &lt;a href="https://github.com/w3c/csswg-drafts/issues/4329" rel="noopener noreferrer"&gt;helped resolve&lt;/a&gt; some of the issues with them, and it's exciting to see &lt;a href="https://developer.apple.com/safari/technology-preview/release-notes/" rel="noopener noreferrer"&gt;Safari Technical Preview release 135&lt;/a&gt; with support for the new &lt;code&gt;*vh&lt;/code&gt; units such as &lt;code&gt;lvh&lt;/code&gt;, &lt;code&gt;svh&lt;/code&gt;, and &lt;code&gt;dvh&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;These units can subtly or even quite drastically change how your website looks and behaves - especially the &lt;a href="https://dev.to/frehner/css-vh-dvh-lvh-svh-and-vw-units-27k4#dynamic-viewport-units"&gt;&lt;code&gt;dvh&lt;/code&gt; unit&lt;/a&gt; (which behaves as &lt;code&gt;vh&lt;/code&gt; used to behave when it was first implemented, until it was determined that in most cases that's actually &lt;a href="https://github.com/w3c/csswg-drafts/issues/4329#issuecomment-542420036" rel="noopener noreferrer"&gt;pretty bad behavior&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;However, if you &lt;a href="https://codepen.io/afrehner/pen/ZEJVbaE" rel="noopener noreferrer"&gt;play around with the new units&lt;/a&gt; on your desktop browser (assuming you use a browser that supports them), nothing happens - even if you're in responsive design mode! &lt;/p&gt;

&lt;p&gt;That's because your desktop browser doesn't change the viewport size, while the mobile version of your browser does. So the &lt;strong&gt;only&lt;/strong&gt; solution is to have an actual mobile browser open to test your component or website. (Again, ideally you're doing this already. But it can slow things down if this is your only option to test.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Window Resize Events
&lt;/h2&gt;

&lt;p&gt;If you have an event listener for window resize events, you may not have realized that mobile browsers fire those events quite frequently as you scroll around - because your desktop browser doesn't change the viewport, even in responsive design mode. &lt;a href="https://xleeg.csb.app/" rel="noopener noreferrer"&gt;Test out this codesandbox&lt;/a&gt; on your phone and on your desktop to see the difference. (Source code found &lt;a href="https://codesandbox.io/s/hopeful-shape-xleeg?file=/index.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  Proposal
&lt;/h2&gt;

&lt;p&gt;I think it would be a great improvement if desktop browsers' mobile responsive design mode had the ability to emulate the viewport changing sizes as you scroll. &lt;/p&gt;

&lt;p&gt;Additional thoughts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Perhaps it could be turned on and off with a preference toggle?&lt;/li&gt;
&lt;li&gt;You (probably?) don't need to add the actual browser chrome to the emulator, you could just resize the window. At least for an initial first release&lt;/li&gt;
&lt;li&gt;Maybe it should only work when you have an actual device selected in the Device picker dropdown?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you also think this would be helpful, please:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a star to the &lt;a href="https://bugs.chromium.org/p/chromium/issues/detail?id=1271608" rel="noopener noreferrer"&gt;Chrome issue&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add a vote for the &lt;a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1741940" rel="noopener noreferrer"&gt;Firefox request&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add a vote for the &lt;a href="https://bugs.webkit.org/show_bug.cgi?id=233329" rel="noopener noreferrer"&gt;Safari request&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please refrain from commenting things like "+1" :)&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>html</category>
      <category>css</category>
    </item>
    <item>
      <title>CSS *vh (dvh, lvh, svh) and *vw units</title>
      <dc:creator>Anthony Frehner</dc:creator>
      <pubDate>Mon, 01 Nov 2021 17:23:21 +0000</pubDate>
      <link>https://dev.to/frehner/css-vh-dvh-lvh-svh-and-vw-units-27k4</link>
      <guid>https://dev.to/frehner/css-vh-dvh-lvh-svh-and-vw-units-27k4</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;This background section is large; here's a handy link to the new unit section if you would like to skip ahead.&lt;/p&gt;

&lt;p&gt;However, I think it can be useful to understand how we got where we are. Additionally, this information will be useful to understand when talking about the &lt;code&gt;dvh&lt;/code&gt; and &lt;code&gt;dvw&lt;/code&gt; units later. &lt;/p&gt;

&lt;p&gt;Note: while I'll focus mainly on the &lt;code&gt;vh&lt;/code&gt; unit, please know that the &lt;code&gt;vw&lt;/code&gt; unit had the same issues. It's just easier to talk about one of them.&lt;/p&gt;

&lt;h3&gt;
  
  
  History of &lt;code&gt;vh&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;vh&lt;/code&gt; unit, as it originally existed, was defined as &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Equal to 1% of the height of the initial containing block.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
  Expand for more info about the initial containing block
  &lt;p&gt;The initial containing block (ICB) definition can be found &lt;a href="https://www.w3.org/TR/CSS2/visudet.html#containing-block-details" rel="noopener noreferrer"&gt;here&lt;/a&gt;, but I think the first bullet point summarizes it well (emphasis mine):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The containing block in which the root element lives is a rectangle called the initial containing block. For continuous media, it has &lt;strong&gt;the dimensions of the viewport&lt;/strong&gt; and is anchored at the canvas origin;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In other words, it's the same size as the viewport; or the same size as the content of your website that you can see without scrolling.&lt;/p&gt;





&lt;/p&gt;

&lt;p&gt;Which is very useful and good... that is, until mobile browsers started doing some tricks to maximize your phone's screen space. To see these tricks, open any webpage on your phone and scroll around some bit, while paying attention &lt;strong&gt;not&lt;/strong&gt; to the &lt;em&gt;content&lt;/em&gt; of the webpage but the &lt;em&gt;browser's UI&lt;/em&gt; itself.&lt;/p&gt;

&lt;p&gt;You may have noticed that the browser's UI &lt;em&gt;changes size&lt;/em&gt; as you scroll. And if the browser's UI and viewport changes size, then the question regarding &lt;code&gt;vh&lt;/code&gt; became: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If &lt;code&gt;vh&lt;/code&gt; is 1% of the initial containing block (ICB), but the ICB changes sizes as a user scrolls, what does &lt;code&gt;vh&lt;/code&gt; &lt;em&gt;really equal&lt;/em&gt;?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  First Solution, First Problem
&lt;/h3&gt;

&lt;p&gt;The first and simplest answer to that question was "&lt;code&gt;vh&lt;/code&gt; changes as the ICB changes." Logically, it would seem like that should be the final answer, too.&lt;/p&gt;

&lt;p&gt;However, there's a problem with this solution:&lt;/p&gt;

&lt;p&gt;Let's say you have a phone that has &lt;code&gt;100px&lt;/code&gt; of screen space. When you load up a page, the browser UI takes up &lt;code&gt;15px&lt;/code&gt; which leaves &lt;code&gt;85px&lt;/code&gt; left for your website's content. &lt;/p&gt;

&lt;p&gt;However, your browser is Super Cool, so when you scroll down the browser UI only takes up &lt;code&gt;10px&lt;/code&gt;, which leaves your website content with &lt;code&gt;90px&lt;/code&gt;. Or, in table form:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Browser UI State&lt;/th&gt;
&lt;th&gt;Browser UI&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;100vh&lt;/code&gt; in pixels&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;See everything / maximized / first page load / on scroll upward&lt;/td&gt;
&lt;td&gt;&lt;code&gt;15px&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;85px&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Small / minimized / on scroll downward&lt;/td&gt;
&lt;td&gt;&lt;code&gt;10px&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;90px&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On your Fancy Website™, you have 5 &lt;code&gt;&amp;lt;section style="height: 100vh"&amp;gt;&lt;/code&gt; elements on your page. When someone first loads the page, each &lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt; is &lt;code&gt;85px&lt;/code&gt; tall. But as they begin to scroll down, the browser UI begins to change size which causes those section elements to grow by 5 pixels, to a total of &lt;code&gt;90px&lt;/code&gt;! &lt;/p&gt;

&lt;p&gt;Now, let's say the user's now at the bottom of the page looking at the 5th section element. They decide they want to scroll up, which has the side effect of changing the browser UI to the maximum size. That causes &lt;code&gt;vh&lt;/code&gt; to shrink, and your sections are now all &lt;code&gt;5px&lt;/code&gt; smaller; when you're at the bottom of the page, that's a total of &lt;code&gt;20px&lt;/code&gt; of difference (4 sections with &lt;code&gt;5px&lt;/code&gt; each). &lt;/p&gt;

&lt;p&gt;Just by scrolling upwards a tiny bit, the &lt;em&gt;content&lt;/em&gt; of your page has jumped upwards 1/5 of your total screen space (100px total / 20px smaller)! That is a &lt;em&gt;very&lt;/em&gt; jarring experience, and honestly it sucked.&lt;/p&gt;

&lt;p&gt;Imagine if you had used &lt;code&gt;vh&lt;/code&gt; for things like &lt;code&gt;font-size&lt;/code&gt;, and how that would look!&lt;/p&gt;

&lt;h3&gt;
  
  
  Second Solution, Second Problem
&lt;/h3&gt;

&lt;p&gt;With this problem in mind, in &lt;a href="https://bugs.webkit.org/show_bug.cgi?id=141832" rel="noopener noreferrer"&gt;2015 Safari / Webkit engineers decided to change the behavior of &lt;code&gt;vh&lt;/code&gt; units&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Dynamically updating the height was not working, we had a few choices: drop viewport units on iOS, match the document size like before iOS 8, use the small view size, use the large view size.&lt;/p&gt;

&lt;p&gt;From the data we had, using the larger view size was the best compromise.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In other words, a dynamic &lt;code&gt;vh&lt;/code&gt; unit was not great, so they changed it to be a static unit equal to the size of the viewport &lt;em&gt;when the browser UI was its smallest&lt;/em&gt; (and the content / "view size" was the largest).&lt;/p&gt;

&lt;p&gt;About a year later &lt;a href="https://groups.google.com/a/chromium.org/g/blink-dev/c/BK0oHURgmJ4" rel="noopener noreferrer"&gt;Chrome / Blink engineers agreed and also updated &lt;code&gt;vh&lt;/code&gt; units to do the same&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Which is where we are now (as of the time of this writing) with &lt;code&gt;vh&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;One of the problems with &lt;code&gt;vh&lt;/code&gt; being the "largest view size" is that anything that is &lt;code&gt;height: 100vh&lt;/code&gt; is now larger or overflows the screen when you first load a page. It's pretty difficult, using just CSS, to get content to fit the page &lt;em&gt;exactly&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;And so, in 2019, a &lt;a href="https://github.com/w3c/csswg-drafts/issues/4329" rel="noopener noreferrer"&gt;new CSS proposal was born&lt;/a&gt;. And in 2021, that proposal, with feedback and improvements, was &lt;a href="https://github.com/w3c/csswg-drafts/issues/4329#issuecomment-863677668" rel="noopener noreferrer"&gt;accepted in the CSS spec&lt;/a&gt; as several new units!&lt;/p&gt;

&lt;h2&gt;
  
  
  The New CSS Units
&lt;/h2&gt;

&lt;p&gt;The large, small, dynamic, and traditional vh units.&lt;/p&gt;

&lt;h3&gt;
  
  
  Large Viewport Units
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;lvh&lt;/code&gt; &amp;amp; &lt;code&gt;lvw&lt;/code&gt; units &lt;a href="https://www.w3.org/TR/2021/WD-css-values-4-20211016/#large-viewport-percentage-units" rel="noopener noreferrer"&gt;are defined&lt;/a&gt; as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The large viewport-percentage units (lv*) are defined with respect to the large viewport size: the viewport sized assuming any UA interfaces that are dynamically expanded and retracted to be retracted. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In other words, the size when the browser UI is the smallest and the website content is the largest. &lt;code&gt;lvh&lt;/code&gt; is essentially how the &lt;code&gt;vh&lt;/code&gt; unit currently (at the time of this writing) acts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Small Viewport Units
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;svh&lt;/code&gt; &amp;amp; &lt;code&gt;svw&lt;/code&gt; units &lt;a href="https://www.w3.org/TR/2021/WD-css-values-4-20211016/#small-viewport-percentage-units" rel="noopener noreferrer"&gt;are defined&lt;/a&gt; as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The small viewport-percentage units (sv*) are defined with respect to the small viewport size: the viewport sized assuming any UA interfaces that are dynamically expanded and retracted to be expanded. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Essentially, &lt;code&gt;svh&lt;/code&gt; gives you units that you can use to fill the screen when the browser UI is at its largest, and the website content is at its smallest.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dynamic Viewport Units
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;dvh&lt;/code&gt; &amp;amp; &lt;code&gt;dvw&lt;/code&gt; units &lt;a href="https://www.w3.org/TR/2021/WD-css-values-4-20211016/#dynamic-viewport-percentage-units" rel="noopener noreferrer"&gt;are defined&lt;/a&gt; as (with emphasis mine):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The dynamic viewport-percentage units (dv*) are defined with respect to the dynamic viewport size: the viewport sized with dynamic consideration of any UA interfaces that are dynamically expanded and retracted. This allows authors to size content such that it can exactly fit within the viewport whether or not such interfaces are present.&lt;/p&gt;

&lt;p&gt;The sizes of the dynamic viewport-percentage units are not stable even while the viewport itself is unchanged. &lt;strong&gt;Using these units can cause content to resize e.g. while the user scrolls the page. Depending on usage, this can be disturbing to the user and/or costly in terms of performance.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While the &lt;code&gt;dvh&lt;/code&gt; &amp;amp; &lt;code&gt;dvw&lt;/code&gt; units &lt;em&gt;may&lt;/em&gt; sound good on paper, the caveats noted in the definition above (and in the first problem &amp;amp; solution section above) actually lead me to believe that you should only use them in very rare and specific situations.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Traditional Viewport Units
&lt;/h3&gt;

&lt;p&gt;With these new units, where does &lt;code&gt;vh&lt;/code&gt; and &lt;code&gt;vw&lt;/code&gt; sit? Interestingly enough, they are currently defined as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The UA-default viewport-percentage units (v*) are defined with respect to a UA-defined UA-default viewport size, which for any given document should be equivalent to the large viewport size, small viewport size, or some intermediary size.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Personally, I find this definition to be too vague, and &lt;a href="https://github.com/w3c/csswg-drafts/issues/6454#issuecomment-880808709" rel="noopener noreferrer"&gt;I have concerns about how this will affect users and developers&lt;/a&gt;. I guess we'll see where it goes!&lt;/p&gt;

</description>
      <category>css</category>
    </item>
    <item>
      <title>Polymorphic React Button Component in Typescript</title>
      <dc:creator>Anthony Frehner</dc:creator>
      <pubDate>Mon, 13 Sep 2021 22:16:33 +0000</pubDate>
      <link>https://dev.to/frehner/polymorphic-button-component-in-typescript-c28</link>
      <guid>https://dev.to/frehner/polymorphic-button-component-in-typescript-c28</guid>
      <description>&lt;p&gt;[Edited Oct 6 2021 with improved code due to feedback. See implementation section for details]&lt;/p&gt;

&lt;h2&gt;
  
  
  Polymorphic?
&lt;/h2&gt;

&lt;p&gt;The goal of this article is to create a component that can be either a button OR a react-router &lt;code&gt;Link&lt;/code&gt; component OR a native &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; (anchor) tag.&lt;/p&gt;

&lt;p&gt;But first, let's define the word "polymorphic." From &lt;a href="https://www.dictionary.com/browse/polymorphic" rel="noopener noreferrer"&gt;Dictionary.com&lt;/a&gt;: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;having more than one form or type&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So when we call something a "polymorphic" component, it means that we can use the same component and it will have more than one form under the hood.&lt;/p&gt;

&lt;p&gt;In this case, designers usually want a consistent look for interactive elements such as buttons and links, and developers want an easy interface to use these common styles, while also maintaining semantic and accessible HTML. &lt;/p&gt;

&lt;h2&gt;
  
  
  Use Cases / Examples
&lt;/h2&gt;

&lt;p&gt;So we're going to make a component called &lt;code&gt;&amp;lt;Button /&amp;gt;&lt;/code&gt; that will allow someone to choose whether to use it as a button, a react-router &lt;code&gt;Link&lt;/code&gt; component, or as an anchor for external links. And we want to have Typescript enforce and validate the correct props for each one.&lt;/p&gt;

&lt;p&gt;For example, we want to be able to do the following:&lt;/p&gt;

&lt;h3&gt;
  
  
  Button / Default
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;
  &lt;span class="na"&gt;as&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'button'&lt;/span&gt;
  &lt;span class="na"&gt;styleType&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'primary'&lt;/span&gt;
  &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&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;// evt should be of type React.MouseEvent&amp;lt;HTMLButtonElement, MouseEvent&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  hello!
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;
  &lt;span class="c1"&gt;// 'as' is optional, and will default to 'button'&lt;/span&gt;
  &lt;span class="na"&gt;styleType&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'secondary'&lt;/span&gt;
  &lt;span class="c1"&gt;// allow other button attributes, such as 'type'&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'button'&lt;/span&gt;
&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  hello!
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Link
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;
  &lt;span class="na"&gt;as&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'link'&lt;/span&gt;
  &lt;span class="c1"&gt;// 'to' is required since it's required in the Link component&lt;/span&gt;
  &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'/test'&lt;/span&gt;
  &lt;span class="na"&gt;styleType&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'primary'&lt;/span&gt;
  &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&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;// evt should be of type React.MouseEvent&amp;lt;HTMLAnchorElement, MouseEvent&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  hello!
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  External Link / Anchor Tag
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;
  &lt;span class="na"&gt;as&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'externalLink'&lt;/span&gt;
  &lt;span class="na"&gt;styleType&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'primary'&lt;/span&gt;
  &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&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;// evt should be of type React.MouseEvent&amp;lt;HTMLAnchorElement, MouseEvent&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// href and other anchor attributes should be allowed&lt;/span&gt;
  &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'/someurl'&lt;/span&gt;
  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'_blank'&lt;/span&gt;
  &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'noopener noreferrer'&lt;/span&gt;
&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  Link
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Unstyled button
&lt;/h3&gt;

&lt;p&gt;An unstyled button is occasionally used in designs where a designer wants some clickable text but without all the pomp and circumstance. Shouldn't be used very often.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;as&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'unstyled'&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Unstyled&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Implementation Notes:
&lt;/h2&gt;

&lt;p&gt;Hopefully the use-cases above show how we want our component to be polymorphic. When it comes to the implementation, I originally started by referring to these wonderful articles by &lt;a href="https://www.benmvp.com/blog/polymorphic-react-components-typescript/" rel="noopener noreferrer"&gt;Ben Ilegbodu&lt;/a&gt; and &lt;a href="https://isamatov.com/polymorphic-components-react-typescript/" rel="noopener noreferrer"&gt;Iskander Samatov&lt;/a&gt;. However, I kept running into issues with certain things like the rest parameters / props not being correctly typed or the &lt;code&gt;to&lt;/code&gt; prop not being correctly recognized for &lt;code&gt;link&lt;/code&gt; type Buttons. It was frustrating and I spent several days and &lt;a href="https://twitter.com/frehner_a/status/1436462652063195138?s=20" rel="noopener noreferrer"&gt;iterations&lt;/a&gt; on it. &lt;/p&gt;

&lt;p&gt;Finally, I took a step back, tried to simplify as much as I could, and got it to work. It's not &lt;em&gt;as&lt;/em&gt; clean as I had hoped, but it's working and that's what matters, right? Anyway, some takeaways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;del&gt;I had to use &lt;a href="https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates" rel="noopener noreferrer"&gt;type-predicate narrowing&lt;/a&gt; to get the rest params to be correctly typed.  There's probably room to improve them there, but see the functions &lt;code&gt;isLinkProps&lt;/code&gt;, &lt;code&gt;isButtonProps&lt;/code&gt;, and &lt;code&gt;isAnchorProps&lt;/code&gt;. Apparently it isn't enough for Typescript for us to key off of the &lt;code&gt;as&lt;/code&gt; prop? 🤷&lt;/del&gt;&lt;/li&gt;
&lt;li&gt;The anchor tag must explicitly have the &lt;code&gt;{rest.children}&lt;/code&gt; part; the &lt;code&gt;jsx-a11y/anchor-has-content&lt;/code&gt; ESLint plugin doesn't like it when you keep &lt;code&gt;children&lt;/code&gt; as part of the &lt;code&gt;{...rest}&lt;/code&gt; spread.&lt;/li&gt;
&lt;li&gt;&lt;del&gt;It took me awhile to figure out that I wanted &lt;code&gt;JSX.IntrinsicElements['button']&lt;/code&gt; as the prop type definition; I had tried other things like &lt;code&gt;React.ComponentPropsWithoutRef&amp;lt;&amp;gt;&lt;/code&gt; and &lt;code&gt;React.ElementType&amp;lt;&amp;gt;&lt;/code&gt; combinations without much success for some reason - they would fail one of the test cases I outlined above. One day I'll understand Typescript better to tell you why.&lt;/del&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;Edited; thanks to &lt;a href="https://dev.to/mcapoz/comment/1ijlg"&gt;this wonderful comment&lt;/a&gt; from &lt;a href="https://dev.to/mcapoz"&gt;Mae Capozzi&lt;/a&gt; below, the typing for this component can be simplified! I can remove the type-predicate narrowing issues I described above by &lt;em&gt;not&lt;/em&gt; destructuring the &lt;code&gt;as&lt;/code&gt; prop. Apparently TS likes that a lot more!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-router-dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LinkProps&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-router-dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;BaseProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactNode&lt;/span&gt;
  &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="na"&gt;styleType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primary&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="s1"&gt;secondary&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="s1"&gt;tertiary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ButtonAsButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;BaseProps&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;
  &lt;span class="nb"&gt;Omit&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ButtonHTMLAttributes&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLButtonElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;BaseProps&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ButtonAsUnstyled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Omit&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ButtonAsButton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;as&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="s1"&gt;styleType&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;as&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unstyled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="nx"&gt;styleType&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;BaseProps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;styleType&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ButtonAsLink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;BaseProps&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;
  &lt;span class="nb"&gt;Omit&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;LinkProps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;BaseProps&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;as&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;link&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ButtonAsExternal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;BaseProps&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;
  &lt;span class="nb"&gt;Omit&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AnchorHTMLAttributes&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLAnchorElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;BaseProps&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;as&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;externalLink&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ButtonProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;ButtonAsButton&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;ButtonAsExternal&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;ButtonAsLink&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;ButtonAsUnstyled&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;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ButtonProps&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;JSX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Element&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;allClassNames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;styleType&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;styleType&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;
    &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;link&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;// don't pass unnecessary props to component&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;className&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;styleType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;allClassNames&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;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="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;externalLink&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;styleType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;
        &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;allClassNames&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="c1"&gt;// provide good + secure defaults while still allowing them to be overwritten&lt;/span&gt;
        &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'_blank'&lt;/span&gt;
        &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'noopener noreferrer'&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unstyled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;styleType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;styleType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;allClassNames&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;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;



</description>
      <category>react</category>
      <category>javascript</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Javascript AWS SDK v3 S3 guide</title>
      <dc:creator>Anthony Frehner</dc:creator>
      <pubDate>Fri, 20 Aug 2021 16:35:44 +0000</pubDate>
      <link>https://dev.to/frehner/javascript-aws-sdk-v3-s3-guide-dg5</link>
      <guid>https://dev.to/frehner/javascript-aws-sdk-v3-s3-guide-dg5</guid>
      <description>&lt;p&gt;That is way too many acronyms in the title, yet they almost seem necessary. I promise I'm not trying to out-SEO you.&lt;/p&gt;

&lt;p&gt;With the new &lt;code&gt;v3&lt;/code&gt; update to the Javascript AWS-SDK, you can use the sdk quite a bit differently. You don't have to, but the new way can reduce the amount of code you import at runtime, and thus &lt;em&gt;can&lt;/em&gt; be "more performant." At the moment of writing this article, there aren't a lot of docs on it either. So this article is just as much for me as is it for you!&lt;/p&gt;

&lt;p&gt;The major difference is that you now import/require a very bare "client", and send your commands through that client.&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="c1"&gt;// The "old" way&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;S3&lt;/span&gt; &lt;span class="p"&gt;}&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="s1"&gt;awk-sdk&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;s3&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;S3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s3Config&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;object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;objConfig&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The "new" way&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;S3Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;GetObjectCommand&lt;/span&gt; &lt;span class="p"&gt;}&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="s1"&gt;@aws-sdk/client-s3&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;s3Client&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;S3Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s3Config&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;object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;s3Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GetObjectCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;objConfig&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, some things to point out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Note that you can install only the client you need, not the whole AWS SDK library. In this case, you only have to &lt;code&gt;npm install @aws-sdk/client-s3&lt;/code&gt;. This reduces the space on disk and the install time 👍&lt;/li&gt;
&lt;li&gt;You only need to import/require the exact functions you need from the client, e.g. &lt;code&gt;GetObjectCommand&lt;/code&gt;. This reduces the amount of code you need at runtime 👍&lt;/li&gt;
&lt;li&gt;You send the command you want through the client, and a promise is returned by default instead of needing to call &lt;code&gt;.promise()&lt;/code&gt; 👍&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's the &lt;a href="https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/" rel="noopener noreferrer"&gt;v3 documentation homepage&lt;/a&gt; if you want to explore it as well. I wouldn't say it's &lt;em&gt;bad&lt;/em&gt;, per se, but I wouldn't say it's good either. It does seem extensive though. &lt;/p&gt;

</description>
      <category>aws</category>
      <category>javascript</category>
    </item>
    <item>
      <title>A closer look at Javascript event listeners &amp; objects</title>
      <dc:creator>Anthony Frehner</dc:creator>
      <pubDate>Fri, 19 Feb 2021 17:15:31 +0000</pubDate>
      <link>https://dev.to/frehner/a-closer-look-at-javascript-event-listeners-objects-32pj</link>
      <guid>https://dev.to/frehner/a-closer-look-at-javascript-event-listeners-objects-32pj</guid>
      <description>&lt;p&gt;This post isn't meant to be an introduction to Javascript event listeners and the event object; instead it's meant to answer some random questions I had while working on my own event listener system (since I was unable to use &lt;code&gt;domNode.dispatchEvent&lt;/code&gt;). --edit-- I just discovered &lt;code&gt;EventTarget&lt;/code&gt;! See the section below&lt;/p&gt;

&lt;h2&gt;
  
  
  Questions:
&lt;/h2&gt;

&lt;h3&gt;
  
  
  If there are multiple event listeners, do they receive the same event object or different objects?
&lt;/h3&gt;

&lt;p&gt;They receive the same event object.&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;var&lt;/span&gt; &lt;span class="nx"&gt;button&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="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;temp1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;temp2&lt;/span&gt;

&lt;span class="nx"&gt;button&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="s1"&gt;click&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="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;temp1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;button&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="s1"&gt;click&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="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;temp2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;temp1&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;temp2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  If an event is &lt;code&gt;preventDefault()&lt;/code&gt;-ed, do other listeners still receive that event?
&lt;/h3&gt;

&lt;p&gt;Yes; &lt;code&gt;preventDefault()&lt;/code&gt; is a signal that the event's &lt;em&gt;action&lt;/em&gt; shouldn't happen, but other listeners will still get called. &lt;code&gt;stopPropagation()&lt;/code&gt; and &lt;code&gt;stopImmediatePropagation()&lt;/code&gt; is what's used to prevent other listeners from being called. (See questions below)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Event interface's preventDefault() method tells the user agent that if the event does not get explicitly handled, its default action should not be taken as it normally would be. The event continues to propagate as usual&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault" rel="noopener noreferrer"&gt;MDN Source&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  If there are multiple event listeners added to the same DOM element, and the first listener calls &lt;code&gt;stopPropagation()&lt;/code&gt;, do the other listeners receive the event?
&lt;/h3&gt;

&lt;p&gt;Yes. &lt;code&gt;stopPropagation()&lt;/code&gt; prevents the event from bubbling up to higher-up listeners, but it does not prevent listeners from being fired on the same DOM node. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When dispatched in a tree, invoking this method prevents event from reaching any objects other than the current object.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://dom.spec.whatwg.org/#interface-event" rel="noopener noreferrer"&gt;DOM Standards spec&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that's why &lt;code&gt;stopImmediatePropagation()&lt;/code&gt; exists; it will prevent ALL other event listeners from being fired. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Invoking this method prevents event from reaching any registered event listeners after the current one finishes running and, when dispatched in a tree, also prevents event from reaching any other objects.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://dom.spec.whatwg.org/#interface-event" rel="noopener noreferrer"&gt;DOM Standards spec&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  EventTarget
&lt;/h3&gt;

&lt;p&gt;If you ever think about implementing your own event listener system, you should consider using &lt;code&gt;EventTarget&lt;/code&gt; instead! Here's an example:&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;target&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;EventTarget&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nx"&gt;target&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="s1"&gt;myEvent&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="nx"&gt;evt&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;)})&lt;/span&gt;

&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;myEvent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;EventTarget&lt;/code&gt; works in all &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/EventTarget" rel="noopener noreferrer"&gt;modern browsers&lt;/a&gt; and &lt;a href="https://nodejs.org/api/events.html#events_eventtarget_and_event_api" rel="noopener noreferrer"&gt;NodeJS &amp;gt;14.5&lt;/a&gt;. That's a lot easier than doing it yourself! :)&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>html</category>
    </item>
    <item>
      <title>Interactions with the history stack</title>
      <dc:creator>Anthony Frehner</dc:creator>
      <pubDate>Mon, 15 Feb 2021 21:23:38 +0000</pubDate>
      <link>https://dev.to/frehner/the-history-stack-and-its-length-2j62</link>
      <guid>https://dev.to/frehner/the-history-stack-and-its-length-2j62</guid>
      <description>&lt;p&gt;JS Trivia time: given the following code, what is logged?&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;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pushState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/temp1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pushState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/temp2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pushState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/temp3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;before go&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;go&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;after go&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pushState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/temp4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;after go after pushState&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;
  Answer (click to see)
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;before&lt;/span&gt; &lt;span class="nx"&gt;go&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="nx"&gt;after&lt;/span&gt; &lt;span class="nx"&gt;go&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="nx"&gt;after&lt;/span&gt; &lt;span class="nx"&gt;go&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt; &lt;span class="nx"&gt;pushState&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;/p&gt;




&lt;h2&gt;
  
  
  Followup question!
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pushState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/temp1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pushState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/temp2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pushState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/temp3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;before go&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;go&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;after go&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/temp4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;after go after replaceState&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;
  Answer (click to see)
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;before&lt;/span&gt; &lt;span class="nx"&gt;go&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="nx"&gt;after&lt;/span&gt; &lt;span class="nx"&gt;go&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="nx"&gt;after&lt;/span&gt; &lt;span class="nx"&gt;go&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt; &lt;span class="nx"&gt;replaceState&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;/p&gt;




&lt;h2&gt;
  
  
  What's going on?
&lt;/h2&gt;

&lt;p&gt;The history stack allows you to navigate through it, and it will maintain the stack as it was through your navigation. However, when you push onto the stack, you erase any &lt;em&gt;future&lt;/em&gt; navigations from your current location in the stack. &lt;/p&gt;

&lt;p&gt;A simplified demonstration:&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;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pushState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/temp1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// history.stack looks like ['', '/temp1']&lt;/span&gt;
&lt;span class="c1"&gt;// current entry is '/temp1'&lt;/span&gt;

&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pushState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/temp2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// history.stack looks like ['', '/temp1', '/temp2']&lt;/span&gt;
&lt;span class="c1"&gt;// current entry is '/temp2'&lt;/span&gt;

&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pushState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/temp3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// history.stack looks like ['', '/temp1', '/temp2', '/temp3']&lt;/span&gt;
&lt;span class="c1"&gt;// current entry is '/temp3'&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;before go&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// before go 4&lt;/span&gt;

&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;go&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// history.stack looks like ['', '/temp1', '/temp2', '/temp3']&lt;/span&gt;
&lt;span class="c1"&gt;// current entry is ''&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;after go&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// after go 4&lt;/span&gt;

&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pushState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/temp4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// history.stack looks like ['', '/temp4']&lt;/span&gt;
&lt;span class="c1"&gt;// current entry is '/temp4'&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;after go after pushState&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// after go after pushState 2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and if we were only to change the &lt;code&gt;pushState&lt;/code&gt; call above with &lt;code&gt;replaceState&lt;/code&gt;, here's what it 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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/temp4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// history.stack looks like ['/temp4', '/temp1', '/temp2', '/temp3']&lt;/span&gt;
&lt;span class="c1"&gt;// current entry is '/temp4'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Spec talk
&lt;/h2&gt;

&lt;p&gt;Here's a link to the &lt;a href="https://html.spec.whatwg.org/multipage/history.html#url-and-history-update-steps" rel="noopener noreferrer"&gt;WHATWG HTML spec for history update steps&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The key thing to pay attention to here is step 2:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;2 If &lt;em&gt;isPush&lt;/em&gt; is true, then:&lt;/p&gt;

&lt;p&gt;2.1 Remove all the entries in browsingContext's session history after the current entry. If the current entry is the last entry in the session history, then no entries are removed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;isPush&lt;/code&gt; is true for &lt;code&gt;pushState&lt;/code&gt; and false for &lt;code&gt;replaceState&lt;/code&gt;, which can be found here for &lt;a href="https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate" rel="noopener noreferrer"&gt;pushState&lt;/a&gt; and here for &lt;a href="https://html.spec.whatwg.org/multipage/history.html#dom-history-replacestate" rel="noopener noreferrer"&gt;replaceState&lt;/a&gt; - paying attention to the last words in those sentences. &lt;/p&gt;

</description>
      <category>javascript</category>
      <category>jstrivia</category>
      <category>html</category>
    </item>
    <item>
      <title>The 'discarded' page lifecycle state</title>
      <dc:creator>Anthony Frehner</dc:creator>
      <pubDate>Tue, 10 Nov 2020 21:23:43 +0000</pubDate>
      <link>https://dev.to/frehner/the-discarded-page-lifecycle-state-4lo6</link>
      <guid>https://dev.to/frehner/the-discarded-page-lifecycle-state-4lo6</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;Browsers don't like the reputation of being resource hogs, even if you have 100+ tabs open. To help reduce CPU + memory demands while still allowing you to keep those tabs around, browsers came up with the &lt;em&gt;Page Lifecycle&lt;/em&gt; - a way to reduce resource usage while still providing a good user experience. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://developers.google.com/web/updates/2018/07/page-lifecycle-api" rel="noopener noreferrer"&gt;This article&lt;/a&gt; does a great job of further explaining the different states and what happens in them, as well as APIs you can use to check which state your page was or is in. &lt;/p&gt;

&lt;h2&gt;
  
  
  Discarded State
&lt;/h2&gt;

&lt;p&gt;Let's focus on the &lt;code&gt;discarded&lt;/code&gt; state. The article above defines that state as&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A page is in the discarded state when it is unloaded by the browser in order to conserve resources. No tasks, event callbacks, or JavaScript of any kind can run in this state, as discards typically occur under resource constraints, where starting new processes is impossible.&lt;/p&gt;

&lt;p&gt;In the discarded state the tab itself (including the tab title and favicon ) is usually visible to the user even though the page is gone.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or in other words - the page hasn't been used for awhile, and the browser wants to free up resources, so it's going to "close the page" while still showing the tab for the page. That's pretty cool and useful.&lt;/p&gt;

&lt;p&gt;However, what isn't detailed in that article (at the time of writing) is: &lt;em&gt;what exactly happens when the page is loaded up again?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The answer (again, for the moment) &lt;a href="https://twitter.com/philwalton/status/1326231502825271296?s=20" rel="noopener noreferrer"&gt;appears to be&lt;/a&gt;: it's up to each browser to determine that. (To be clear, there's nothing wrong with that!)&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually happens
&lt;/h2&gt;

&lt;p&gt;From my testing, let me tell you what appears to happens in Chrome - and how it caused a bug in my application:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The page is loaded&lt;/li&gt;
&lt;li&gt;After some time, Chrome determines that it needs to go into the &lt;code&gt;discarded&lt;/code&gt; state&lt;/li&gt;
&lt;li&gt;Chrome appears to cache the original HTML of the page that you're on&lt;/li&gt;
&lt;li&gt;The page goes into &lt;code&gt;discarded&lt;/code&gt; state and all resources (CPU, memory) are removed for that tab&lt;/li&gt;
&lt;li&gt;Now you open that tab/page again, and Chrome needs it to be in the &lt;code&gt;active&lt;/code&gt; state&lt;/li&gt;
&lt;li&gt;It pulls the cached HTML and re-parses + re-executes it - (which means that it refetches any CSS or Javascript referenced by the HTML)&lt;/li&gt;
&lt;li&gt;The page is now loaded&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The problem I ran into lies in that second-to-last step: it is using a cached version of the HTML but downloading a new version of the JS. &lt;/p&gt;

&lt;h2&gt;
  
  
  Is that a problem?
&lt;/h2&gt;

&lt;p&gt;But why would &lt;em&gt;that&lt;/em&gt; cause a problem?&lt;/p&gt;

&lt;p&gt;Well, in any case where you &lt;em&gt;need&lt;/em&gt; a change to happen in both your HTML &lt;em&gt;and&lt;/em&gt; your JS in order to work, and there's some place where you don't have versioned assets.&lt;/p&gt;

&lt;p&gt;In my case, it's because of how the infrastructure is set up: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We're using &lt;a href="https://github.com/systemjs/systemjs" rel="noopener noreferrer"&gt;import-maps with SystemJS&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;I have one import-map that is not versioned and is not cached; the JS assets it links to are though&lt;/li&gt;
&lt;li&gt;I have another import-map that is inlined into the HTML page; this import-map is for things like shared dependencies that don't change very often (e.g. &lt;code&gt;react&lt;/code&gt;, &lt;code&gt;react-dom&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So in this situation, I had put a new shared library into the inlined import-map, and also updated all the JS assets to look for the shared version of that library instead of the bundled one. However, &lt;em&gt;for those people that had a cached version of the HTML&lt;/em&gt; (for example, the people that had their page go into the &lt;code&gt;discarded&lt;/code&gt; state), they never downloaded the new HTML but &lt;strong&gt;did&lt;/strong&gt; download the new JS - and that broke the page!&lt;/p&gt;

&lt;h2&gt;
  
  
  A solution
&lt;/h2&gt;

&lt;p&gt;Fortunately, the solution for users is simple enough - just a page refresh will cause them to download the latest HTML and all is good again.&lt;/p&gt;

&lt;p&gt;For me, it also means that I can handle the situation where the app is being started from the &lt;code&gt;discarded&lt;/code&gt; state by checking &lt;code&gt;document.wasDiscarded&lt;/code&gt;, and if that's true, then force a full-page refresh which should resolve the bug before it happens.&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="k"&gt;if&lt;/span&gt;&lt;span class="p"&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;wasDiscarded&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>javascript</category>
      <category>html</category>
    </item>
    <item>
      <title>Making CSS vars with SCSS vars</title>
      <dc:creator>Anthony Frehner</dc:creator>
      <pubDate>Tue, 06 Oct 2020 17:15:15 +0000</pubDate>
      <link>https://dev.to/frehner/making-css-vars-with-scss-vars-19dc</link>
      <guid>https://dev.to/frehner/making-css-vars-with-scss-vars-19dc</guid>
      <description>&lt;p&gt;SASS treats CSS variables (also known as custom properties) in a special way, which means it's a little difficult to create CSS vars dynamically:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Sass parses custom property declarations differently than other property declarations. All tokens, including those that look like SassScript, are passed through to CSS as-is.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://sass-lang.com/documentation/style-rules/declarations#custom-properties" rel="noopener noreferrer"&gt;SASS docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Which can cause problems, especially for my situation where I want to&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Keep the SASS variables around because they're used in a lot of existing code, but also&lt;/li&gt;
&lt;li&gt;Add CSS variables for new code&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So here's how I've worked around this. It's not ideal, because it still duplicates the name of the SASS variable, but it works for my situation. 🤷&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nv"&gt;$primary-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#30dbb4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;@mixin&lt;/span&gt; &lt;span class="nf"&gt;add-css-variable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$color&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nt"&gt;--&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="nd"&gt;:&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;color&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// repeat as necessary for all your vars&lt;/span&gt;
  &lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;add-css-variable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'primary-color'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$primary-color&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;You can test this out in the wonderful &lt;a href="https://www.sassmeister.com" rel="noopener noreferrer"&gt;SassMeister playground&lt;/a&gt;&lt;/p&gt;

</description>
      <category>css</category>
      <category>scss</category>
      <category>sass</category>
    </item>
    <item>
      <title>The Order of Javascript Object Keys</title>
      <dc:creator>Anthony Frehner</dc:creator>
      <pubDate>Wed, 02 Sep 2020 17:33:46 +0000</pubDate>
      <link>https://dev.to/frehner/the-order-of-js-object-keys-458d</link>
      <guid>https://dev.to/frehner/the-order-of-js-object-keys-458d</guid>
      <description>&lt;p&gt;Trivia time! What does the following array 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="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="mi"&gt;2&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="mi"&gt;1&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;00&lt;/span&gt;&lt;span class="dl"&gt;'&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;b&lt;/span&gt;&lt;span class="dl"&gt;'&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;'&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;
  Answer (click to see)
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&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;2&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;3&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;00&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;b&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;a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;/p&gt;

&lt;p&gt;So what exactly is going on?? Here are the ordering rules:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Numbers are ordered first, and they are ordered within themselves from smallest to largest as long as they are &lt;code&gt;&amp;gt;=0&lt;/code&gt; (see below for more details) &lt;/li&gt;
&lt;li&gt;Strings come second, and they are ordered within themselves by &lt;em&gt;insertion&lt;/em&gt; order&lt;/li&gt;
&lt;li&gt;Symbols come last, and they are ordered within themselves by &lt;em&gt;insertion&lt;/em&gt; order (note that we didn't use symbols in this example)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;But wait, why did &lt;code&gt;'3'&lt;/code&gt; come before &lt;code&gt;'00'&lt;/code&gt; if strings are ordered within themselves by insertion order?&lt;/p&gt;

&lt;p&gt;Well, turns out that JS will see if your string can be converted to a number - if it can, then it will order it with the &lt;strong&gt;numbers&lt;/strong&gt; and not the strings. &lt;/p&gt;

&lt;p&gt;And what about &lt;code&gt;'00'&lt;/code&gt;? Apparently it converts it to a &lt;em&gt;new number&lt;/em&gt;, then does something similar to &lt;code&gt;toString()&lt;/code&gt; on &lt;em&gt;new number&lt;/em&gt;, and compares that &lt;em&gt;new string&lt;/em&gt; with the &lt;em&gt;original string&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;If they match, then it can be lumped in with the numbers. If it doesn't match, then it's a string.&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;originalString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;00&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stringToNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;originalString&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;matchesOriginalString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;stringToNumber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;originalString&lt;/span&gt; &lt;span class="c1"&gt;// false: '0' !== '00'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pretty clear, huh? :P&lt;/p&gt;

&lt;p&gt;Thanks to this article for helping &lt;a href="https://www.stefanjudis.com/today-i-learned/property-order-is-predictable-in-javascript-objects-since-es2015/" rel="noopener noreferrer"&gt;https://www.stefanjudis.com/today-i-learned/property-order-is-predictable-in-javascript-objects-since-es2015/&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Here's the wording from &lt;a href="https://tc39.es/ecma262/#sec-ordinaryownpropertykeys" rel="noopener noreferrer"&gt;the spec&lt;/a&gt;:&lt;/p&gt;

&lt;h2&gt;
  
  
  Numbers
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;For each own property key P of O such that P is an array index, in ascending numeric index order, Add P as the last element of keys.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;i.e. insert numerical keys first in ascending order&lt;/p&gt;

&lt;h2&gt;
  
  
  Strings
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;For each own property key P of O such that Type(P) is String and P is not an array index, in ascending chronological order of property creation, Add P as the last element of keys.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;i.e. insert string keys in order of creation as long as they're not an &lt;code&gt;array index&lt;/code&gt;. So what's that?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;An &lt;em&gt;integer index&lt;/em&gt; is a String-valued property key that is a canonical numeric String (see 7.1.21) and whose numeric value is either +0 or a positive integer ≤ 2&lt;sup&gt;53&lt;/sup&gt; - 1. An &lt;code&gt;array index&lt;/code&gt; is an &lt;em&gt;integer index&lt;/em&gt; whose numeric value i is in the range +0 ≤ i &amp;lt; 2&lt;sup&gt;32&lt;/sup&gt; - 1.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://tc39.es/ecma262/#array-index" rel="noopener noreferrer"&gt;source for array index&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;i.e. a string key that is a canonical numeric string and greater than +0&lt;/p&gt;

&lt;p&gt;so... what's a canonical numeric string?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[A canonical numeric string is an] &lt;em&gt;argument&lt;/em&gt; converted to a Number value if it is a String representation of a Number that would be produced by ToString, or the string "-0".&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://tc39.es/ecma262/#sec-canonicalnumericindexstring" rel="noopener noreferrer"&gt;source for canonical numeric string&lt;/a&gt;&lt;br&gt;
i.e. if that string is the same as any number that is &lt;code&gt;toString()&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Symbols
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;For each own property key P of O such that Type(P) is Symbol, in ascending chronological order of property creation, Add P as the last element of keys.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;i.e. insert symbol keys in order of creation&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>jstrivia</category>
    </item>
  </channel>
</rss>
