<?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: Jade Banks</title>
    <description>The latest articles on DEV Community by Jade Banks (@jadebanks).</description>
    <link>https://dev.to/jadebanks</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%2F3271857%2F2bc2c330-e9d2-4c69-922e-02e3652d8b99.png</url>
      <title>DEV Community: Jade Banks</title>
      <link>https://dev.to/jadebanks</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jadebanks"/>
    <language>en</language>
    <item>
      <title>Why Truthy Checks can Break on Zero in TypeScript</title>
      <dc:creator>Jade Banks</dc:creator>
      <pubDate>Mon, 23 Mar 2026 06:03:00 +0000</pubDate>
      <link>https://dev.to/jadebanks/why-truthy-checks-can-break-on-zero-in-typescript-515o</link>
      <guid>https://dev.to/jadebanks/why-truthy-checks-can-break-on-zero-in-typescript-515o</guid>
      <description>&lt;p&gt;JavaScript/TypeScript can treat non-boolean values as booleans in certain situations. Basically, there are a set of values which are defined as &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Falsy" rel="noopener noreferrer"&gt;Falsy&lt;/a&gt;. All other values are considered &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Truthy" rel="noopener noreferrer"&gt;Truthy&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When combined with &lt;a href="https://www.typescriptlang.org/docs/handbook/2/objects.html#optional-properties" rel="noopener noreferrer"&gt;Optional Properties&lt;/a&gt;, code can be more concise and easier to read.&lt;/p&gt;

&lt;p&gt;Consider a system for filtering numerical values where each filter property is optional.&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;FilterOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;greaterThan&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;lessThan&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="cm"&gt;/* Returns true if `value` satisfies the filter, false otherwise */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isInFilter&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="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FilterOptions&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;greaterThan&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;greaterThan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&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;filter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lessThan&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lessThan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&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="cm"&gt;/*
  Demonstrates the filtering function by logging
  the numbers between -5 and 5 that satisfy the filter
*/&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;filterExample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FilterOptions&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;results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

    &lt;span class="k"&gt;for &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;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&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="nf"&gt;isInFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&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;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&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;// No filtering&lt;/span&gt;
&lt;span class="c1"&gt;// Output: -5,-4,-3,-2,-1,0,1,2,3,4,5&lt;/span&gt;
&lt;span class="nf"&gt;filterExample&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;

&lt;span class="c1"&gt;// Numbers above 2&lt;/span&gt;
&lt;span class="c1"&gt;// Output: 3,4,5&lt;/span&gt;
&lt;span class="nf"&gt;filterExample&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;greaterThan&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works fine, with and without the &lt;code&gt;greaterThan&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;However if we apply the following, it will fail&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This fails&lt;/span&gt;
&lt;span class="c1"&gt;// Expected Output: "1,2,3,4,5"&lt;/span&gt;
&lt;span class="c1"&gt;// Actual Output: "-5,-4,-3,-2,-1,0,1,2,3,4,5"&lt;/span&gt;
&lt;span class="nf"&gt;filterExample&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;greaterThan&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The bug is in 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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;greaterThan&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;greaterThan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="k"&gt;return&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;When &lt;code&gt;filter.greaterThan&lt;/code&gt; is &lt;code&gt;0&lt;/code&gt;, it is considered &lt;code&gt;falsy&lt;/code&gt; in the same way &lt;code&gt;undefined&lt;/code&gt; is. So it behaves as if that filter property wasn't set.&lt;/p&gt;

&lt;p&gt;The fix is to change the line to the following&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;greaterThan&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;greaterThan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="k"&gt;return&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;Same fix would be needed for the &lt;code&gt;lessThan&lt;/code&gt; property.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;When treating non-boolean values as booleans, make sure you've considered all the possible &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Falsy" rel="noopener noreferrer"&gt;Falsy&lt;/a&gt; values that could apply in your application. The same issue could occur for an empty string &lt;code&gt;""&lt;/code&gt; or &lt;code&gt;NaN&lt;/code&gt; for example.&lt;/p&gt;

&lt;p&gt;If a value can be &lt;code&gt;undefined&lt;/code&gt; or &lt;code&gt;null&lt;/code&gt;, you'd need something like the following&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;greaterThan&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;greaterThan&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;...)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;    &lt;span class="c1"&gt;// Note: single `=` to apply loose equality&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;filter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;greaterThan&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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 play around with this code in this &lt;a href="https://www.typescriptlang.org/play/?#code/C4TwDgpgBAYglgG2BATgeTMOB7AdgZygF4oBvAWACgooBzFCAQ2RQBUALR3AfgC4pcAVwC2AI1QAaKjQQR8+Dlz4CR4lFMoBfANxUqAegBUUAEoRgglASjAUg6HABmUAAYA3Rgnsuo+ZnHxHODkbdmggpEkoR098aGxgMJQAdwDoQ30qR0FcAGMsPCgAgElceEiUAAoPLwh+ITEoiJZ+cpYMAoIASn5RbGxZLjJpKH19KA4A6MZEQmSw3CgIOETUKDAUbEgUUCLCAC9UbBGxifYpqfFcxkE41wAGHyncvHw4ABNUCHfXGIR8EBPRarXyMYTQeYgVw5T5BXDfJ74E7jeAADygoihuU4uFocFxoXCAwQ2FSBIQ+OgwGwricUEqzVQADp6EwWIpFgBCIgkGEQOHfKAAMiFUBq9igAB4SIyUCyGMxUByui4RnSGYgWPK2UqccLReLoNLoprmazFWwcV0RjQGBYrNN-hBdJRkWcppxCH5wXt8BLGIRGH03BA1c4NRUmbJ5Bz9WLPBKAHwy01y6MKK02qB2yyLP5xF0jHMO2z2F2aPSUIxQEYAEQgwletkVhBBsvxtGiOXyOEWmKgJNoeNxIxBDTUhHEwGSEAgiwAtABWKBcH7LxLMUFYQJQtupqgZLLdzomioAUVRYLAsgjLVgqY6vfwXWG1BoUBeBGA2bkgiQ+F4cdUAAbQAXWIKAwMLN8aEcbAUHpWRvzgCCl20IopRIRd0LgABqXDrRg991RKMpU0qOAJFPFgukI996J-P1-yZMBbnYCiumg+jP3wAYICjbBaEqBgmOAfAmQAK2wfFKgAcgkWTaPLStTgAORpdsRyrcY0EEYBWOAfglwkecABYTIAZhMgAmEyAEYJHuCQHNsqzzMXLJUwvK8b1ITROJU8ZVNUVBA2DaBrIMHS9IM-g3IkDzKFlbzhGvCBKgoIjzXZHF+Fsqh-Og05JkIGJZiiqAL0gfJBV0-S9P4AAiFyJHixdGoqgBBfJBE8KA6tiqBGuMszLJs+zHOciRXIkdyOqSrzL1S3ys2y3UuH4JyCoCyggA" rel="noopener noreferrer"&gt;Playground&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>testing</category>
    </item>
    <item>
      <title>React: Use 'Excess Property Checks' in setState() to avoid subtle bugs</title>
      <dc:creator>Jade Banks</dc:creator>
      <pubDate>Mon, 09 Mar 2026 04:27:43 +0000</pubDate>
      <link>https://dev.to/jadebanks/react-use-excess-property-checks-in-setstate-to-avoid-subtle-bugs-3o9p</link>
      <guid>https://dev.to/jadebanks/react-use-excess-property-checks-in-setstate-to-avoid-subtle-bugs-3o9p</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;


&lt;div class="crayons-card c-embed"&gt;

  When using objects as state, annotate the return type on callbacks passed to &lt;code&gt;setState()&lt;/code&gt;&lt;br&gt;
e.g. Instead of &lt;code&gt;setState(state =&amp;gt;&lt;/code&gt; do &lt;code&gt;setState((state): State =&amp;gt;&lt;/code&gt;&lt;br&gt;

&lt;/div&gt;


&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://www.w3schools.com/react/react_es6_spread.asp" rel="noopener noreferrer"&gt;Spread Operator&lt;/a&gt; is a JavaScript feature, it is particularly relevant however to React because State Objects should &lt;a href="https://react.dev/learn/updating-objects-in-state#treat-state-as-read-only" rel="noopener noreferrer"&gt;not be mutated&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here we see a common pattern in the following code (parts omitted for clarity)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Define app/component state&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;State&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Declare initial state&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;State&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unknown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;count&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="c1"&gt;// Partial state updates made like this&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;resetCount&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="err"&gt; &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="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;count&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="p"&gt;}));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;Consider this code for setting the &lt;code&gt;count&lt;/code&gt; property back to zero, while preserving the values of the other properties.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This code doesn't trigger any errors, despite the mistake&lt;/span&gt;
&lt;span class="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;cont&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="c1"&gt;// typo of `count`&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The issue is the typo will not be picked up by the type checker. This is because the spread operator has given us a fully formed object of the &lt;code&gt;State&lt;/code&gt; type, the incorrectly spelt &lt;code&gt;cont&lt;/code&gt; is considered an additional (excess) property. The type checker is considering this valid.&lt;/p&gt;

&lt;p&gt;Due to IntelliSense/Auto complete, it's not likely this would happen when writing new code but &lt;u&gt;can occur more subtly when renaming&lt;/u&gt;.&lt;/p&gt;

&lt;p&gt;If we decide, for example, after some time, that &lt;code&gt;count&lt;/code&gt; should be &lt;code&gt;personCount&lt;/code&gt;. Many references to the old name will be flagged in the IDE as errors, prompting us to update them. This won't be the case for &lt;code&gt;setState()&lt;/code&gt; calls though. Not at build nor run time. We're now setting a property that doesn't exist in the State type (&lt;code&gt;count&lt;/code&gt;) and the target property (&lt;code&gt;personCount&lt;/code&gt;) keeps it's existing value. This refactor could introduce a breaking change.&lt;/p&gt;

&lt;p&gt;There is a good chance this would not be picked up in code review either, due to the nature of code reviews focussing on the changed lines, and this error being on an unchanged line.&lt;/p&gt;

&lt;h2&gt;
  
  
  How can we fix this
&lt;/h2&gt;

&lt;p&gt;Using the IDE Refactor &amp;gt; Rename feature would give us better coverage of the rename but we cannot be expected to remember this every time.&lt;/p&gt;

&lt;p&gt;We want to force TypeScript to apply &lt;a href="https://www.typescriptlang.org/docs/handbook/2/objects.html#excess-property-checks" rel="noopener noreferrer"&gt;Excess Property Checks&lt;/a&gt;. This can be done by simply setting a return type on the function passed to &lt;code&gt;setState()&lt;/code&gt;. In other words, we change &lt;code&gt;state =&amp;gt;&lt;/code&gt; to &lt;code&gt;(state): State =&amp;gt;&lt;/code&gt; and it would look like this&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;State&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;personCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Renamed from `count`&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;State&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unknown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;personCount&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="c1"&gt;// IDE error shown (as desired) saying that 'count' is not a known property&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;resetCount&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;setState&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;State&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;// This line changed to include return type&lt;/span&gt;
&lt;span class="err"&gt; &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="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;count&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="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;By specifying the return type explicitly, we now get build time checking on the properties we specify and can be more confident our code is what we intend.&lt;/p&gt;

&lt;h2&gt;
  
  
  useReducer()
&lt;/h2&gt;

&lt;p&gt;The normal approach for complex state structures is to use &lt;a href="https://react.dev/learn/typescript#typing-usereducer" rel="noopener noreferrer"&gt;useReducer()&lt;/a&gt; instead.&lt;/p&gt;

&lt;p&gt;The same advice applies here, make sure to explicitly set the return type&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✘ - Relying on inferred return type won't check excess properties&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;reducer&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;State&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Action&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="c1"&gt;// ✔ - Setting the return type helps catch incorrect properties&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;reducer&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;State&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Action&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="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Other Approaches
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Immer library
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://react.dev/learn/updating-objects-in-state#write-concise-update-logic-with-immer" rel="noopener noreferrer"&gt;Immer&lt;/a&gt; library provides a proxy object to do in place updates. We don't need to re-create objects ourselves, so can rely on TypeScript checking that the properties exist.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use a 'Helper' function
&lt;/h3&gt;

&lt;p&gt;I wouldn't suggest this as a general approach as it's not idiomatic React and may make the code harder to understand for other developers.&lt;/p&gt;

&lt;p&gt;However it's worth showing as the &lt;a href="https://www.typescriptlang.org/docs/handbook/utility-types.html#partialtype" rel="noopener noreferrer"&gt;Partial&amp;lt;Type&amp;gt;&lt;/a&gt; Utility Type can be very useful&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Helper function&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;partial&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Partial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="o"&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="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&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="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;partial&lt;/span&gt;
  &lt;span class="p"&gt;}));&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;resetCount&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;cont&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="c1"&gt;// Error: 'cont' correctly flagged as not existing&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Finally
&lt;/h2&gt;

&lt;p&gt;You can read the &lt;a href="https://www.typescriptlang.org/docs/handbook/type-compatibility.html" rel="noopener noreferrer"&gt;Type Compatibility&lt;/a&gt; documentation if you want to learn in more detail about this and the reasoning behind the design decisions.&lt;/p&gt;

&lt;p&gt;If you use AI tools and the code it generates doesn't follow the suggested fix automatically, then add this into your agents/rules file, giving it the correct and incorrect forms as an example.&lt;/p&gt;

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