<?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: Will Heslam</title>
    <description>The latest articles on DEV Community by Will Heslam (@willheslam).</description>
    <link>https://dev.to/willheslam</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%2F646585%2Fcc7fc879-4ee0-4a66-b10a-94ca01c226e3.jpeg</url>
      <title>DEV Community: Will Heslam</title>
      <link>https://dev.to/willheslam</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/willheslam"/>
    <language>en</language>
    <item>
      <title>Control flow analysis of aliased conditional expressions in TypeScript</title>
      <dc:creator>Will Heslam</dc:creator>
      <pubDate>Mon, 05 Jul 2021 12:51:12 +0000</pubDate>
      <link>https://dev.to/willheslam/control-flow-analysis-of-aliased-conditional-expressions-in-typescript-kdp</link>
      <guid>https://dev.to/willheslam/control-flow-analysis-of-aliased-conditional-expressions-in-typescript-kdp</guid>
      <description>&lt;p&gt;An &lt;a href="https://github.com/microsoft/TypeScript/pull/44730"&gt;interesting feature&lt;/a&gt; was added to TypeScript recently that will improve the ergonomics of code that relies on type narrowing or discrimination:&lt;/p&gt;

&lt;p&gt;TS 4.4 can infer when a variable's type implies something about the type of another.&lt;/p&gt;

&lt;p&gt;A simple example given in the PR description:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&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;isString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&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;isString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;  &lt;span class="c1"&gt;// Ok&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;In TS 4.3.4, &lt;a href="https://www.typescriptlang.org/play?#code/GYVwdgxgLglg9mABMMAKAHgLkeA1mOAdzAEpEBvAKERsQgQGcpEYGBlKAJxjAHNEAvIigBPAA4BTOMETpBAoQHIm3Pouq0YM1Kw6reZKrWOyAdABsJfKAAsA3DQD0jxAFFOnOJ2wB5AEYAVhLQLAyI0sLiEoiKeATE6sYAvpRJQA"&gt;accessing &lt;code&gt;x.length&lt;/code&gt; is a type error&lt;/a&gt;...&lt;/p&gt;

&lt;p&gt;Even though we know that if (and only if) &lt;code&gt;isString&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;, &lt;code&gt;x&lt;/code&gt; must be a &lt;code&gt;string&lt;/code&gt;, the type checker doesn't know that!&lt;/p&gt;

&lt;p&gt;This is because &lt;code&gt;isString&lt;/code&gt; is just a stupid old boolean - it doesn't know or care why it happens to be true or false.&lt;/p&gt;

&lt;p&gt;For TS to understand the expression implies something about its inputs, &lt;code&gt;typeof x === 'string'&lt;/code&gt; has to be inlined inside the if statement (or ternary if you're that way inclined).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;  &lt;span class="c1"&gt;// Ok&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is pretty annoying because we can no longer rearrange our code as we see fit.&lt;/p&gt;

&lt;p&gt;We have to choose: do we structure our program to appease the cold, emotionless type checker, or appease nice and cuddly humans using lovely abstractions like names and expression reuse?&lt;/p&gt;

&lt;p&gt;We can have our cake and eat it by pulling out the expression into a &lt;a href="https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates"&gt;type guard predicate&lt;/a&gt;, but that's a lot of boilerplate and even bug prone - if our guard predicate and function body fall out of sync, we have an invisible type-checker-defeating bug on our hands!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;brokenIsStr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&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;That's a very dense and dry cake!&lt;/p&gt;

&lt;p&gt;At this point TS is looking less like "just JS with types" and more like a verbose subset that's hard to read and write.&lt;/p&gt;

&lt;p&gt;This has changed in TS 4.4, as &lt;code&gt;isString&lt;/code&gt; is &lt;a href="https://www.typescriptlang.org/play?ts=4.4.2#code/GYVwdgxgLglg9mABMMAKAHgLkeA1mOAdzAEpEBvAKERsQgQGcpEYGBlKAJxjAHNEAvIigBPAA4BTOMETpBAoQHIm3Pouq0YM1Kw6reZKrWOyAdABsJfKAAsaAenuIA8rg00AvpQ9A"&gt;now imbued&lt;/a&gt; with the implication our brains associate with it - TS understands that iff &lt;code&gt;isString&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;, &lt;code&gt;x&lt;/code&gt; must be a &lt;code&gt;string&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This means we can start decoupling our conditionals from the expressions they depend on; our TS programs start looking a bit more nimble, our cake a little moister!&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;p&gt;Variables don't encode a history of their every logical implication - it's not magic.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;foo&lt;/code&gt;'s type can only imply something about &lt;code&gt;bar&lt;/code&gt; when &lt;code&gt;foo&lt;/code&gt; is &lt;code&gt;const&lt;/code&gt; and either:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the result of a conditional expression about &lt;code&gt;bar&lt;/code&gt; in the current scope (i.e. &lt;code&gt;foo&lt;/code&gt; is a boolean)&lt;/li&gt;
&lt;li&gt;a discriminant property of &lt;code&gt;bar&lt;/code&gt; (i.e. &lt;code&gt;bar&lt;/code&gt; is a &lt;a href="https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions"&gt;discriminated union&lt;/a&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It supports &lt;a href="https://www.typescriptlang.org/play?ts=4.4.2#code/GYVwdgxgLglg9mABMMAKAHgLkeA1mOAdzAEpEBvAKERsQgQGcpEYGBlKAJxjAHNEAvIigBPAA4BTOMETpBAoQHIm3PosrVa9ME2GE4AGQkA3CQBsGAEQkSxgluy49+AHxcOOq3pprbdUAAtOGyNTC2tbeyh9UPMrGzs3Dyc+HzpGZmA4EE5Y8ISooJCTOIjE91ZPZzS-TJhTPPjIoSycxrLEJMqU71p0nWYGGHR2gpb6iVHIrscvGozEACsQJgB5MEmSsw6hbq9OitnnA+T9maq+E73j856NPphgVGAJqbEyKj6+uQB6H8QVNU+gBfNKPVBDEZbJrvChpb6IP44MD4IhgNKgh5PZZrDZ5MofeG0X7-QGpEGUYFAA"&gt;up to 5 levels of indirection&lt;/a&gt; before giving up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&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;isString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&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;twoLevelsDeep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;isString&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;isString&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;threeLevelsDeep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;twoLevelsDeep&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;isString&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fourLevelsDeep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;threeLevelsDeep&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;isString&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fiveLevelsDeep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fourLevelsDeep&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;isString&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sixLevelsDeep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fiveLevelsDeep&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;isString&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;justOneLevelDeep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;isString&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;isString&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;isString&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;isString&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;isString&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;isString&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;fiveLevelsDeep&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="c1"&gt;// string&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;sixLevelsDeep&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="c1"&gt;// unknown&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;justOneLevelDeep&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="c1"&gt;// string&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and as of yet it doesn't fold away identical expressions.&lt;/p&gt;

&lt;p&gt;Whilst an aliased conditional expression on a destructured field will allow for narrowing the original object's type, the flow analysis cannot narrow the type of a destructured sibling.&lt;/p&gt;

&lt;p&gt;This coincidentally makes destructuring arguments inside the function signature less useful to the type checker - you may be better off destructuring arguments on the next line.&lt;/p&gt;

&lt;p&gt;As an example, a predicate upon &lt;code&gt;foo&lt;/code&gt; cannot influence the inferred type of &lt;code&gt;bar&lt;/code&gt; here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bar&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;Baz&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;But it can influence the type of &lt;code&gt;baz&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Baz&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;foo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bar&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;baz&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This might change in the future, but it's something to bear in mind.&lt;/p&gt;

&lt;p&gt;Another important limitation is that narrowing a specific property of an object (as opposed to narrowing the type of the object overall) &lt;a href="https://github.com/microsoft/TypeScript/pull/44730#issuecomment-868703811"&gt;requires that property to be readonly&lt;/a&gt;, potentially tipping the balance in favour of readonly properties by default.&lt;br&gt;
Despite going out of its way to support mutability, the more advanced TypeScript's analysis gets, the more it encourages functional programming with immutability.&lt;/p&gt;
&lt;h2&gt;
  
  
  Downsides
&lt;/h2&gt;

&lt;p&gt;There's inevitably some implicit complexity introduced - we'll have to take care to remember when a seemingly innocent boolean is being relied upon by the type checker elsewhere.&lt;/p&gt;

&lt;p&gt;Any kind of inference increases coupling between disparate parts of our program - a change over here is more likely to change something over there.&lt;br&gt;
This is a trade off we make all the time; to avoid it entirely requires redundantly and tediously enunciating every single type in your program.&lt;/p&gt;

&lt;p&gt;Anyone stuck working with an older version of TS will also have to be slightly more careful when blindly copy pasting from the internet - the weaker inference may render copied code incompatible.&lt;/p&gt;
&lt;h2&gt;
  
  
  A Practical Example
&lt;/h2&gt;

&lt;p&gt;Let's build a slightly contrived e-commerce website with React - how hard could it be?&lt;/p&gt;

&lt;p&gt;Our customers will go through several steps - browsing the catalogue, selecting shipping, then confirming and paying for their order.&lt;/p&gt;

&lt;p&gt;Let's represent those steps as React component state using a discriminated union... something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ShoppingStep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shopping&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;discountCode&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;loggedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&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;SelectShippingStep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Omit&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ShoppingStep&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;step&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;step&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;select-shipping&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ConfirmOrderStep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Omit&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SelectShippingStep&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;step&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;step&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;confirm-order&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="na"&gt;shippingAddress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Address&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;OnlineShop&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;
    &lt;span class="nx"&gt;ShoppingStep&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;SelectShippingStep&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;ConfirmOrderStep&lt;/span&gt;
  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shopping&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;loggedIn&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="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;With each step represented as a separate component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Catalogue&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;ShoppingStep&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="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;ShippingSelect&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;SelectShippingStep&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="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;ConfirmOrder&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;ConfirmOrderStep&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;freeShipping&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;children&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;ReactNode&lt;/span&gt;
  &lt;span class="p"&gt;},&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's put it all together by picking the component depending on the step and calculating free shipping eligibility:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shippingMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shippingAddress&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="nx"&gt;checkFreeShippingEligibility&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shippingAddress&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`Congrats! Free shipping on &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&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="s2"&gt; items!`&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;

  &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;step&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shopping&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Catalogue&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;select-shipping&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ShippingSelect&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;confirm-order&lt;/span&gt;&lt;span class="dl"&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ConfirmOrder&lt;/span&gt;
          &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="nx"&gt;freeShipping&lt;/span&gt;&lt;span class="o"&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;shippingAddress&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
            &lt;span class="nx"&gt;checkFreeShippingEligibility&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shippingAddress&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;shippingMessage&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Now pay up!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ConfirmOrder&lt;/span&gt;&lt;span class="err"&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;



&lt;p&gt;Here's the &lt;a href="https://www.typescriptlang.org/play?jsx=1&amp;amp;ts=4.3.4&amp;amp;strict=true#code/JYWwDg9gTgLgBDAnmApnA3nASighgYxgDkIATNAXzgDMoIQ4AiKPQxgKFElgzgFcAzigDKMXDEo06DZqxgd2SVHACCpUiwEC4AXlVQouRAB4BMKMAB2AcwB8i5GgCSEhnvTs4cS7hAoAXHBmFjaecACOfLiWMMBIgZZ8IABGKFBhYBb4Ad5JqekU7A7KwgAWEGBgVtaiKGC6GGFmdYGMAuWV1RxepMAC+BB8MQDCZCgA-IHB1WEANhDW1iikTpaByRAQs3iW7IVKaMIo24RlwJ02tfV6APIgccZlFVWXEmAANEzNYIy2cABkjS831aQhOMAAtO1zi9rN04HEUCABIEVAYjMYXEj7PtHHBRpZqMAoCAblByFArg07g8juCzhcam9Pm03r8AUCgm9WgNCcSQBDoBT4dDGWoNCgtKj1JoBHsiigAB7ceDUIaEYAQSxwG6WWZWEQdAAUAEpAgApYQADQAdABRbZ+GKc3lmOAAbTM4hQnyEMFE3oAug1BCIxBJjGEvE9GVSAD5wOkoU6lGHVeP4rVEklkilXMK2I0eLzA7lfDqwxjvKNweaLZarQLUXCzITVrwUE1FLyu+Ci2EAWUlAlwS10NbaqbFMuHjAR2q9EgB-xr+FKyYA1gAxFiGtM2B3AazAZLAfVII010vem2I5HtkvXiQ2-vVcWymtdx9ecZwAAGBLWIYMACAAhHAO4oGgr42HAWpwAAJOgi4oLergCDa2w2DApRUHeYF-lecCBEM5BEpYyzdkEADucRrnARooS+bwmpyPa4EI5bPF0-hESwMB8FA2rGMM4gtgsfBoOgNoyShVAAPT2I++AcWgbTHMmkIwXCvHfnA-GCcJDKwkmhAYDJzHegpSklipnGMLy2YCkKaSMLp34GUJDFEV4IlZvyuZpD5JbSbJ4YoIUemPrQUHGdUOjFlFj6Tvu1jvrO85ct6y7Bcp674Nuu5xQe+rHqe56IJeSV6Ux+EPtVJZMdp6VaLlJZftVkVRTZSXIVOg7DqOaDjL+jAkNRcBgEY-BgKBjBdXpxjyQSTmBVAPXtWEhSFOw5D4LMuAsDQ6qxPBoliHWklGpkFQoomFbpixFrWvajooDERR7QdR1qpYGrwcVNQaYQ110GAd2mf6-WPXUZpwJatoOki70wJ9ybfWgv3-dqK0BeSaRVZNYN3bjOb45SbwcolXgxXujLrJs2zRAA3KuqazBKliTNgcgkOQW3VnDCOvcjH27ejh2Yydmramum6QXTsKHmVZ5xJVYT4ai6ImFiIC2A+zUzlKqhGwIgsM1sOzsEAA"&gt;full code in the playground&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This works, but our shipping message logic is pretty dense, and our free shipping check is duplicated!&lt;/p&gt;

&lt;p&gt;Can we do better?&lt;/p&gt;

&lt;p&gt;Let's split apart the shipping message logic and reuse the free shipping check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;freeShipping&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shippingAddress&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="nx"&gt;checkFreeShippingEligibility&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shippingAddress&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;shippingMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nx"&gt;freeShipping&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`Congrats! Free shipping on &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&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="s2"&gt; items!`&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;

  &lt;span class="p"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;confirm-order&lt;/span&gt;&lt;span class="dl"&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ConfirmOrder&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;freeShipping&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;freeShipping&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;shippingMessage&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Now pay up!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ConfirmOrder&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Much better! But this line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`Congrats! Free shipping on &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&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="s2"&gt; items!`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;actually fails the type checker in TS 4.3.4 due to &lt;code&gt;state.items&lt;/code&gt; not necessarily being present: &lt;a href="https://www.typescriptlang.org/play?jsx=1#code/JYWwDg9gTgLgBDAnmApnA3nASighgYxgDkIATNAXzgDMoIQ4AiKPQxgKFElgzgFcAzigDKMXDEo06DZqxgd2SVHACCpUiwEC4AXlVQouRAB4BMKMAB2AcwB8i5GgCSEhnvTs4cS7hAoAXHBmFjaecACOfLiWMMBIgZZ8IABGKFBhYBb4Ad5JqekU7A7KwgAWEGBgVtaiKGC6GGFmdYGMAuWV1RxepMAC+BB8MQDCZCgA-IHB1WEANhDW1iikTpaByRAQs3iW7IVKaMIo24RlwJ02tfV6APIgccZlFVWXEmAANEzNYIy2cABkjS831aQhOMAAtO1zi9rN04HEUCABIEVAYjMYXEj7PtHHBRpZqMAoCAblByFArg07g8juCzhcam9Pm03r8AUCgm9WgNCcSQBDoBT4dDGWoNCgtKj1JoBHsiigAB7ceDUIaEYAQSxwG6WWZWEQdAAUAEpAgApYQADQAdABRbZ+GKc3lmOAAbTM4hQnyEMFE3oAug1BCIxBJjGEvE9GVSAD5wOkoU6lGHVeP4rVEklkilXMK2I0eLzA7lfDqwxjvKNweaLZarQLUXCzITVrwUE1FLyu1UsQ1pmy6GttVNimWSgSMBHar0SAH-Gv4UrJgDWADF+wzYQ7gNZgMlgPqkEaa6XvTbEcj2yXzxIbaLYeLZTWu2Fe0Ex7CALKT3BLYdb1oFAB0ZM84HGOAAAMCWsQwYAEABCOBNxAz9B2sOAtTgAASdA5xQS9XAEG1thsGBSioK8kKg8DAiGcgiUsZZuyCAB3OJlzgI0CIfN4TU5HtcCEctni6fxwJYGA+CgbVjGGcQWwWPg0HQG11IIqgAHp7FvfBhLQNpjmTSFH3EySUGk2S4EeL902MwgMHUvjvW03SS30kTGF5bMBSFNJGAk28vCkmTtVPYKS3krN+VzNInI08MUCoYDQNhHR0FS7dqgodzIq8fC7JsX8tH-NBxkgxgSDYuAwCMfgwEQxhCnymytIJXy4qgPKSy7Dt5XYch8FmXAWBodVYmwhSxDrFSjUyCoUUTCt034i1rXtR0UBiIohpGsa1UsDVsOyy4HJgea6DAJakxTDCrjNOBLVtB0kW2mBduTfa0EO47tQ62LyTSCLaqupaAZzIHKTeDlixLLKiusdZNm2aIAG4l1TWYJUsSZsDkEhyDCChq0e57NrenbBq+0afomzVtWXNdULS6pd33Q9j0QEHqNRdETCxEBbBvMybGfSdpQlLRSeRrYdnYIA"&gt;here's proof&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The fix is to duplicate the shipping address check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shippingMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shippingAddress&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;freeShipping&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`Congrats! Free shipping on &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&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="s2"&gt; items!`&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and now we're paying the price just to satisfy the type checker.&lt;/p&gt;

&lt;p&gt;Let's take advantage of the enhanced inference introduced in TS 4.4 to not only deduplicate, but further tidy up our code!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hasShippingAddress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shippingAddress&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;

  &lt;span class="c1"&gt;// `hasShippingAddress` conditional alias&lt;/span&gt;
  &lt;span class="c1"&gt;// allows state to be narrowed to ConfirmOrderStep&lt;/span&gt;
  &lt;span class="c1"&gt;// so `items` and `shippingAddress` are known to be present&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;freeShipping&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nx"&gt;hasShippingAddress&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="nx"&gt;checkFreeShippingEligibility&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shippingAddress&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;// state is again narrowed to ConfirmOrderStep because&lt;/span&gt;
  &lt;span class="c1"&gt;// `freeShipping` is an aliased conditional twice removed!&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shippingMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;freeShipping&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`Congrats! Free shipping on &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&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="s2"&gt; items!`&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&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;step&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;

  &lt;span class="c1"&gt;// switching on an (aliased) destructured discriminant property&lt;/span&gt;
  &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;step&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="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;confirm-order&lt;/span&gt;&lt;span class="dl"&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ConfirmOrder&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;freeShipping&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;freeShipping&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;shippingMessage&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Now pay up!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ConfirmOrder&lt;/span&gt;&lt;span class="err"&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;



&lt;p&gt;Here's the &lt;a href="https://www.typescriptlang.org/play?jsx=1&amp;amp;ts=4.4.2#code/JYWwDg9gTgLgBDAnmApnA3nASighgYxgDkIATNAXzgDMoIQ4AiKPQxgKFElgzgFcAzigDKMXDEo06DZqxgd2SVHACCpUiwEC4AXlVQouRAB4BMKMAB2AcwB8i5GgCSEhnvTs4cS7hAoAXHBmFjaecACOfLiWMMBIgZZ8IABGKFBhYBb4Ad5JqekU7A7KwgAWEGBgVtaiKGC6GGFmdYGMAuWV1RxepMAC+BB8MQDCZCgA-IHB1WEANhDW1iikTpaByRAQs3iW7IVKaMIo24RlwJ02tfV6APIgccZlFVWXEmAANEzNYIy2cABkjS831aQhOMAAtO1zi9rN04HEUCABIEVAYjMYXEj7PtHHBRpZqMAoCAblByFArg07g8juCzhcam9Pm03r8AUCgm9WgNCcSQBDoBT4dDGWoNCgtKj1JoBHsiigAB7ceDUIaEYAQSxwG6WWZWEQdAAUAEpAgApYQADQAdABRbZ+GKc3lmOAAbTM4hQnyEMFE3oAug1BCIxBJjGEvE9GVSAD5wOkoU6lGHVeP4rVEklkilXMK2I0eLzA7lfDqwxjvKNweaLZarQLUXCzITVrwUE1FLyu+ClXACBmw8WyhptVNimWSgSMBHar0SbtwAD0y7gAAN+4OJ8Op1p13Beb1YlqW3AW8AB2FV+fZvMAO7aBdoGAQOCpby4AwQe-LBBvgls1Jck0nzLwbwEN910RZED2iUgN1FXcJX3c8WDgABrSwf21V93zQTJJRQGIwl7KQUENNMbF0GstyHaoR2nAF-hrfBSmTDCADEWEoxkHWAaxgGSYB9SQI0a1Lb0bRggR2xLSSJBtJCGL3OV5K7a812fBFtFwaxcCsT9v1-BC8MA-lc1At58PwXBQ00jdaAo+ibAPPpz21C8Bz-I84k1HxZgQe9gGyOAWBACAADdlgAQlIrU3WUmwAFlpz0tA9Cc3jYRrcYNwJaxDBgAQYrgbiKKCHdqjgLU4AAEnQZ9pNcAQbW2GwYFKKgZJi9ca0CIZyCJSxliXMjGreKg9GfJcIOCmA2Oq2rojgI0vKEUgTTgchgj4Qg+BYBDen6Cx7h8Z1MgqNIkCaea2NW74tuLEtbKEctni6fwJLClAYAO7VjGGcQWwWPg0HQG1IefKhl3seTXrQNpjmTSEkrhL75K8Fg-qgAGXJqZHCAwSGlPDFAYbhl7vKYXkgMFECoEYDHMex-7Vu+rxAazCyGeJqGyaoLL8Z0dAhaqmwKEpzGS0a8XrFSrR0rgcY8sYEh7zgMAjH4MAYsYQppc55dzJzBmpZLLsO3ldhyHwWYvzQNVLA1WqgbEOswaNS6wBRRMK3TN4zTgS1bQdJFiJgIpbft9CnZd7V8aTQgvboH3AiT-05auIOQ-tR0I6j5MY8d9UT21E3gIpcSvG932K8sylrMBZ6vDFqjrHWTZtmiABuVjU1mCVLEmbA5BIcgwgoasc+tPPw5Im2i4dmhS-8w92PwLiePx-jBOE0TEGrhEWtRdETCxEBbDktHGKlVRVOnzuth2dggA"&gt;full code in 4.4&lt;/a&gt; as compared to &lt;a href="https://www.typescriptlang.org/play?jsx=1&amp;amp;ts=4.3.4#code/JYWwDg9gTgLgBDAnmApnA3nASighgYxgDkIATNAXzgDMoIQ4AiKPQxgKFElgzgFcAzigDKMXDEo06DZqxgd2SVHACCpUiwEC4AXlVQouRAB4BMKMAB2AcwB8i5GgCSEhnvTs4cS7hAoAXHBmFjaecACOfLiWMMBIgZZ8IABGKFBhYBb4Ad5JqekU7A7KwgAWEGBgVtaiKGC6GGFmdYGMAuWV1RxepMAC+BB8MQDCZCgA-IHB1WEANhDW1iikTpaByRAQs3iW7IVKaMIo24RlwJ02tfV6APIgccZlFVWXEmAANEzNYIy2cABkjS831aQhOMAAtO1zi9rN04HEUCABIEVAYjMYXEj7PtHHBRpZqMAoCAblByFArg07g8juCzhcam9Pm03r8AUCgm9WgNCcSQBDoBT4dDGWoNCgtKj1JoBHsiigAB7ceDUIaEYAQSxwG6WWZWEQdAAUAEpAgApYQADQAdABRbZ+GKc3lmOAAbTM4hQnyEMFE3oAug1BCIxBJjGEvE9GVSAD5wOkoU6lGHVeP4rVEklkilXMK2I0eLzA7lfDqwxjvKNweaLZarQLUXCzITVrwUE1FLyu+ClXACBmw8WyhptVNimWSgSMBHar0SbtwAD0y7gAAN+4OJ8Op1p13Beb1YlqW3AW8AB2FV+fZvMAO7aBdoGAQOCpby4AwQe-LBBvgls1Jck0nzLwbwEN910RZED2iUgN1FXcJX3c8WDgABrSwf21V93zQTJJRQGIwl7KQUENNMbF0GstyHaoR2nAF-hrfBSmTDCADEWEoxkHWAaxgGSYB9SQI0a1Lb0bRggR2xLSSJBtJCGL3OV5K7a812fBFtFwaxcCsT9v1-BC8MA-lc1At58PwXBQ00jdaAo+ibAPPpz21C8Bz-I84k1HxZgQe9gGyOAWBACAADdlgAQlIrU3WUmwAFlpz0tA9Cc3jYRrcYNwJaxDBgAQYrgbiKKCHdqjgLU4AAEnQZ9pNcAQbW2GwYFKKgZJi9ca0CIZyCJSxliXMjGreKg9GfJcIOCmA2Oq2rojgI0vKEUgTTgchgj4Qg+BYBDen6Cx7h8Z1MgqNIkCaea2NW74tuLEtbKEctni6fwJLClAYAO7VjGGcQWwWPg0HQG1IefKhl3seTXrQNpjmTSEkrhL75K8Fg-qgAGXJqZHCAwSGlPDFAYbhl7vKYXkgMFECoEYDHMex-7Vu+rxAazCyGeJqGyaoLL8Z0dAhaqmwKEpzGS0a8XrFSrR0rgcY8sYEh7zgMAjH4MAYsYQppc55dzJzBmpZLLsO3ldhyHwWYvzQNVLA1WqgbEOswaNS6wBRRMK3TN4zTgS1bQdJFiJgIpbft9CnZd7V8aTQgvboH3AiT-05auIOQ-tR0I6j5MY8d9UT21E3gIpcSvG932K8sylrMBZ6vDFqjrHWTZtmiABuVjU1mCVLEmbA5BIcgwgoasc+tPPw5Im2i4dmhS-8w92PwLiePx-jBOE0TEGrhEWtRdETCxEBbDktHGKlVRVOnzuth2dggA"&gt;the same in 4.3.4&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is loads better - we've got (slightly more) destructuring, lots of named variables and naturally narrowed types, without duplicating type guard expressions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;TS 4.4's flow analysis of aliased conditional expressions starts to deliver - to stretch an analogy - a type checked, moist and light, more JavaScript-y cake.&lt;/p&gt;

&lt;p&gt;Our TS code can start looking a bit more like the flexible, human-friendly programs we're used to; we're telling the machine what to do, not the other way around!&lt;/p&gt;

&lt;p&gt;Included in the 4.4 release notes is another &lt;a href="https://devblogs.microsoft.com/typescript/announcing-typescript-4-4/#cfa-aliased-conditions"&gt;write-up of the new feature&lt;/a&gt; - I recommend giving the whole thing a read as there are a bunch of juicy new features waiting to be tried out!&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>react</category>
    </item>
  </channel>
</rss>
