<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Replay</title>
    <description>The latest articles on Forem by Replay (@replayio).</description>
    <link>https://forem.com/replayio</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%2Forganization%2Fprofile_image%2F5443%2F355a41d2-f4c6-4e96-834a-46f8d44656d6.jpeg</url>
      <title>Forem: Replay</title>
      <link>https://forem.com/replayio</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/replayio"/>
    <language>en</language>
    <item>
      <title>Debugging a React useEffect bug</title>
      <dc:creator>Cecelia Martinez</dc:creator>
      <pubDate>Tue, 19 Apr 2022 14:11:23 +0000</pubDate>
      <link>https://forem.com/replayio/debugging-a-react-useeffect-bug-2h5k</link>
      <guid>https://forem.com/replayio/debugging-a-react-useeffect-bug-2h5k</guid>
      <description>&lt;p&gt;Developing with a framework like React can add a level of complexity to debugging. Hooks like useEffect can often be a culprit, because it can be configured to only run during certain renders. If you rely on a hook running and it doesn’t, this can cause issues. Here’s a real world example of debugging a useEffect bug in Replay DevTools.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bug
&lt;/h2&gt;

&lt;p&gt;We received reports that sometimes the tooltip that displays the line hit count would be stuck in a loading state forever. This was an intermittent bug, making it harder to pin down because it couldn’t always be reproduced. Fortunately with replay, once we were able to record the bug, we had a debuggable reproduction to work with. &lt;/p&gt;

&lt;p&gt;With a bug like this, there could be a lot of things going on. It could be a backend error, network request related, or the result of something going wrong in the front end. Replay engineer Josh Morrow describes his debugging process below.&lt;/p&gt;

&lt;h2&gt;
  
  
  The debugging process
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Create a reproducible example
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://app.replay.io/recording/hit-counts-stuck-loading--12da5073-0dde-41b2-a3ac-9ed2d5b4eb4f" rel="noopener noreferrer"&gt;This replay&lt;/a&gt; captures the bug behavior. &lt;/p&gt;

&lt;p&gt;In the replay, we can see the tooltip working properly initially, with line hit counts displayed when hovering over the line of code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjy0f3tkgtc3c1ekb3hjn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjy0f3tkgtc3c1ekb3hjn.png" alt="Screenshot of replay" width="800" height="529"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is a click on a comment, which opens a new source in the replay. In Replay, comments can be linked to a specific line of code, so clicking the comment automatically opens the editor and focuses on the line of code.&lt;/p&gt;

&lt;p&gt;After this, the tooltip always shows &lt;code&gt;Loading...&lt;/code&gt; on hover.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnr206dmzogbqfwwuz57e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnr206dmzogbqfwwuz57e.png" alt="Screenshot of replay" width="800" height="529"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Understand expected behavior
&lt;/h3&gt;

&lt;p&gt;Replay will fetch the number of times a line of code has executed and display that number in a tool tip when you hover on the line. There is a &lt;code&gt;LineNumberToolTip&lt;/code&gt; component that handles this functionality on the front end.&lt;/p&gt;

&lt;p&gt;We can see in the replay that the &lt;code&gt;updateBreakpointHitCounts&lt;/code&gt; function executes early in the replay, and we can see the tooltip updating with the line hit count as expected. The two hits of this function line up with the application working properly in the UI.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1jdym7c6rygdbohdtzkr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1jdym7c6rygdbohdtzkr.png" alt="Screenshot of replay" width="800" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once we understand how the application is supposed to act, we can investigate the point where it doesn’t behave in this same way.&lt;/p&gt;

&lt;h3&gt;
  
  
  Isolate the issue
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;updateBreakpointHitCounts&lt;/code&gt; does not execute after the new source is opened. This clues us in that whatever triggers that function is not working properly.&lt;/p&gt;

&lt;p&gt;We can see that &lt;code&gt;setBreakpointHitCounts&lt;/code&gt; within the &lt;code&gt;setHoveredLineNumber&lt;/code&gt; function does execute at the proper locations, so we can narrow in to functionality between this function and &lt;code&gt;updateBreakpointHitCounts&lt;/code&gt;, which doesn’t execute as expected.&lt;/p&gt;

&lt;p&gt;Logging the parameters of &lt;code&gt;setBreakpointHitCounts&lt;/code&gt; shows that the line number changes, but the source id is the same for the entire replay. This means that even though a different file is open in the editor, the &lt;code&gt;LineNumberTooltip&lt;/code&gt; component thinks we are still in the same file as before.&lt;/p&gt;

&lt;p&gt;In this video below, Josh outlines how he isolated the issue to the &lt;code&gt;sourceId&lt;/code&gt; value.&lt;/p&gt;


&lt;div&gt;
  &lt;iframe src="https://loom.com/embed/1564adc1d3114783a723044bf5f2a6a4"&gt;
  &lt;/iframe&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  Tracing the root cause
&lt;/h3&gt;

&lt;p&gt;Now that we have isolated the issue to the source id not updating, we can dig in and investigate why.&lt;/p&gt;

&lt;p&gt;Within the &lt;code&gt;LineNumberToolTip&lt;/code&gt; component on line &lt;code&gt;89&lt;/code&gt;, we are getting the source from &lt;code&gt;useSelector&lt;/code&gt;. By logging the &lt;code&gt;source.id&lt;/code&gt; on the next line, we can see that it does update correctly. (Read more about why we log the value on the next line in our documentation here).&lt;/p&gt;

&lt;p&gt;However, this updated value is not being passed into &lt;code&gt;setBreakpointHitCounts&lt;/code&gt;, because when that function executes, it always uses the stale &lt;code&gt;sourceId&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;So what’s going on here? The function &lt;code&gt;setBreakpointHitCounts&lt;/code&gt; is being called inside a defined function, &lt;code&gt;setHoveredLineNumber&lt;/code&gt;. When you define a function in JavaScript, you create what’s called a closure. This means that the value of &lt;code&gt;sourceId&lt;/code&gt; when the function is defined, is what the value will always be inside that particular function definition. (&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures" rel="noopener noreferrer"&gt;You can read more about closures here&lt;/a&gt;). When you have a bug where a particular value is not updating as expected or evaluating as a stale value, one of the first questions to consider if whether you’re dealing with the effect of a closure.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;setHoveredLineNumber&lt;/code&gt; function is being called inside a &lt;code&gt;useEffect&lt;/code&gt; hook in the &lt;code&gt;LineNumberTooltip&lt;/code&gt; component on line &lt;code&gt;142&lt;/code&gt;. This hook calls the &lt;code&gt;setHoveredLineNumber&lt;/code&gt; function whenever we hover over a line, but because of the closure created by &lt;code&gt;useEffect&lt;/code&gt;, the original &lt;code&gt;sourceId&lt;/code&gt; is still being used.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/RecordReplay/devtools/pull/6381" rel="noopener noreferrer"&gt;Here is the pull request with the fix&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;To dig into what’s happening here — the &lt;code&gt;setHoveredLineNumber&lt;/code&gt; function declaration is re-executed every time the component renders. However, the function is being assigned to the &lt;code&gt;editor.codemirror.on&lt;/code&gt; hook on line &lt;code&gt;142&lt;/code&gt; &lt;em&gt;inside&lt;/em&gt; the &lt;code&gt;useEffect&lt;/code&gt; hook. This creates another closure around the assignment of &lt;code&gt;setHoveredLineNumber&lt;/code&gt; with the original &lt;code&gt;sourceId&lt;/code&gt; value. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;useEffect&lt;/code&gt; hook is configurable, so it may or may not run on every render. In this case, by passing an empty array as the second parameter, we are telling &lt;code&gt;useEffect&lt;/code&gt; to only run once when the component is mounted. This means that even if the updated &lt;code&gt;sourceId&lt;/code&gt; is passed to the &lt;code&gt;setHoveredLineNumber&lt;/code&gt; function in a new declaration, the original assignment with the original value is still being used.&lt;/p&gt;

&lt;p&gt;In the fix, we pass &lt;code&gt;source&lt;/code&gt; as a value to the array in the second parameter. This means that the &lt;code&gt;useEffect&lt;/code&gt; hook will run again whenever the value of &lt;code&gt;source&lt;/code&gt; is updated. This way, we always use the correct &lt;code&gt;sourceId&lt;/code&gt; when assigning &lt;code&gt;setHoveredLineNumber&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the video below, Josh digs into the root cause and how it was ultimately resolved.&lt;/p&gt;


&lt;div&gt;
  &lt;iframe src="https://loom.com/embed/8029bffaba72405a892a28d47a145c6f"&gt;
  &lt;/iframe&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  Learn more
&lt;/h3&gt;

&lt;p&gt;Replay has React DevTools built in so you can debug issues related to components, props, and state. Check out &lt;a href="https://docs.replay.io/docs/react-c5c42fe645fa45eb89d96f896e6a1b31" rel="noopener noreferrer"&gt;our documentation here&lt;/a&gt; for more examples.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Debugging TypeScript using Replay</title>
      <dc:creator>Cecelia Martinez</dc:creator>
      <pubDate>Fri, 18 Mar 2022 20:34:16 +0000</pubDate>
      <link>https://forem.com/replayio/debugging-typescript-using-replay-5fa3</link>
      <guid>https://forem.com/replayio/debugging-typescript-using-replay-5fa3</guid>
      <description>&lt;p&gt;Recently, I sat down with Mateusz Burzyński, aka &lt;a href="https://twitter.com/AndaristRake" rel="noopener noreferrer"&gt;AndaristRake&lt;/a&gt;, to talk about how he debugged an issue in TypeScript using Replay for Node. With the &lt;a href="https://github.com/RecordReplay/replay-node-cli" rel="noopener noreferrer"&gt;replay-node-cli&lt;/a&gt;, you can record a Node runtime for debugging after the fact — no browser required.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fimoyjanr6q5xlhgdki79.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fimoyjanr6q5xlhgdki79.png" alt="Screenshot of Github issue" width="800" height="323"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The bug
&lt;/h2&gt;

&lt;p&gt;The bug, &lt;a href="https://github.com/microsoft/TypeScript/issues/48160" rel="noopener noreferrer"&gt;described in the GitHub issue here&lt;/a&gt;, happens when using &lt;a href="https://github.com/microsoft/TypeScript/pull/47190" rel="noopener noreferrer"&gt;a newer feature in TypeScript&lt;/a&gt; that supports control flow analysis on dependent parameters contextually typed from a discriminated union type. 😅 There’s a lot to unpack there!&lt;/p&gt;

&lt;p&gt;Take a look at the example below, and we’ll break it down.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Foo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="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="nx"&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="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="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt; &lt;span class="k"&gt;void&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;methodSyntax&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;payload&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="nx"&gt;kind&lt;/span&gt; &lt;span class="o"&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="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// error, Property 'toFixed' does not exist on type 'string | number'&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;kind&lt;/span&gt; &lt;span class="o"&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="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// error, Property 'toUpperCase' does not exist on type 'string | number'&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;Here we have a &lt;strong&gt;union type&lt;/strong&gt;, which just means a combination of multiple types. The argument for our function can be &lt;code&gt;number | string&lt;/code&gt;. A &lt;strong&gt;discriminated union&lt;/strong&gt; means that there is a parameter that helps you distinguish (or &lt;em&gt;discriminate&lt;/em&gt;) which type in the union applies (There’s &lt;a href="https://css-tricks.com/typescript-discriminated-unions" rel="noopener noreferrer"&gt;a great blog post here&lt;/a&gt; on discriminated unions from CSS-Tricks).&lt;/p&gt;

&lt;p&gt;In this case, we should be able to check &lt;code&gt;kind&lt;/code&gt; and if it’s &lt;code&gt;a&lt;/code&gt;, the type should be narrowed to &lt;code&gt;number&lt;/code&gt;. Same goes for &lt;code&gt;b&lt;/code&gt;, it should be narrowed to &lt;code&gt;string&lt;/code&gt;. This is what is meant by “control flow analysis” — we can use an &lt;code&gt;if&lt;/code&gt; statement or other logical check to narrow the type.&lt;/p&gt;

&lt;p&gt;However, that’s not working in our example. Instead, the type is still &lt;code&gt;number | string&lt;/code&gt; for each parameter.&lt;/p&gt;
&lt;h2&gt;
  
  
  The debugging process
&lt;/h2&gt;

&lt;p&gt;Mateusz walked us through how he investigated and ultimately identified the root cause of the bug using Replay. These steps can be used for any issue, and are a great example of how a developer debugs in the real world.&lt;/p&gt;
&lt;h3&gt;
  
  
  Generate a reproducible example
&lt;/h3&gt;

&lt;p&gt;This step was easy thanks to Replay. To get started debugging this issue, Mateusz recorded a small test of the following code. Using &lt;code&gt;replay-node-cli&lt;/code&gt; he recorded the run to create &lt;a href="https://app.replay.io/recording/broken-dependent-param-method-variant--9ec573e1-1b77-4c3a-bc4b-beed7ad29db0" rel="noopener noreferrer"&gt;a debuggable replay of the bug&lt;/a&gt;.&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="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;str&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;e&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&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="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;num&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;e&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;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// this fails for some reason, as a method&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;fooBad&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(&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;cb&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="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;num&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="nf"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// error!&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="nf"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;abc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// error!&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;h3&gt;
  
  
  Identify expected behavior
&lt;/h3&gt;

&lt;p&gt;The next step in debugging is understanding what is &lt;em&gt;supposed&lt;/em&gt; to happen when the application works as expected.&lt;/p&gt;

&lt;p&gt;The original GitHub issue has &lt;a href="https://www.typescriptlang.org/play?ts=4.6.2#code/JYOwLgpgTgZghgYwgAgGIHt3IN4FgBQyyAthGABboAmAFAHQNxQDmAzgFzIDaA5HDwBpkIAK7EARtAC6yAD7ce4wclZgooZlICUnAG7pgVAgF8A3AQIJ0IVSTKUqAZQCe4OAA9OGLAF4cBIlIKahoAa1AqIQAHOGcAG3Q4Ki1-QiJkYBhkMIjkH3zkPh4UvDT05Bj4xKo6MHRUYHcIWi1TZAB6duRoKHQoIQAFXqjoMGdCuoamqh5kKnQIVmF0MG73YFtrZDGRwtV1EGY5YTFJKB4A9ONLokzs8JAqPILFYtTyokqEpNr0AFUoiMoABhOCsCA0VodLo9PqDYajcY8OoAoGg8GzeaLZarCDrTYgbbOXY8fYaY6iCTQC5lZDXNL0+mWay2GAiEAIACi7iiUC8mDy7zswSonDZHLAwC2OUe0Vi32SQtuWRlT3yfiKJRu6S+1V+U2akKInWQ6FCsW19PKd1Vzw1Si1tM+8r1KMB0HREKhJrNFtpVrpJgs+GZNlWTF6AHdUOyEPzfEKgg5OKq5VUkikfAA+JUZFUPNUvfiOj4VF0-SaNQ3erq+5yW7U2gt2woO3PO9M1N1osFeto+831-2XRnBqxhkhwURwOIAEUWahECDAIgOzHjgtKgXsIQYdCYbBL6XHti4BbTCpkfgPrHMtKbuXVhWL7bLnf1VZa-drg4b9-zj4vG2W4fLqFb-O6IK9pC36mr+w4MiYQA" rel="noopener noreferrer"&gt;a playground reproduction that shows&lt;/a&gt; this feature works when using function declaration syntax and manual destructuring, but fails when using the method syntax.&lt;/p&gt;

&lt;p&gt;Because the bug only occurs when using the method syntax, we can make an example of what &lt;em&gt;should&lt;/em&gt; happen when this works properly. Mateusz &lt;a href="https://app.replay.io/recording/correct-dependent-param-arrow-function-variant--c9d5b21a-5013-4ea2-85db-f831ba09f40b" rel="noopener noreferrer"&gt;also recorded a replay here&lt;/a&gt; of the working behavior when using an arrow function syntax.&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="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;str&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;e&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&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="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;num&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;e&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;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// suddenly works for arrow function&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;fooOkay1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&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="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cb&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;num&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="nf"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;abc&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="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;Understanding what working code looks like is critical to debugging, because it allows you to identify what is &lt;em&gt;different&lt;/em&gt; between the working and non-working code.&lt;/p&gt;

&lt;p&gt;It also gives you a goalpost for debugging. Before you define your problem, you need to know what the application is expected to do in a given situation.&lt;/p&gt;
&lt;h3&gt;
  
  
  Define the problem
&lt;/h3&gt;

&lt;p&gt;We are now able to define the problem very specifically. Using a framework of “expected behavior” and “actual behavior” is a clear way to define the bug.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Expected behavior:&lt;/strong&gt; TypeScript should narrow the type of a discriminated union when using control flow analysis in a declared method.&lt;/p&gt;

&lt;p&gt;🚫 &lt;strong&gt;Actual behavior:&lt;/strong&gt; TypeScript does not narrow the type in declared method &lt;strong&gt;only.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now that we know the problem, we can start investigating why it occurs.&lt;/p&gt;
&lt;h3&gt;
  
  
  Isolate the issue
&lt;/h3&gt;

&lt;p&gt;Next was understanding where in the code the error was actually happening. To do this, it’s important to understand &lt;strong&gt;what code executed at the time the error occurred.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Replay helps with this by showing how many times a given line of code was hit during the recording. You can also lock in to a specific instance of that code’s execution to inspect your application values at that time. &lt;/p&gt;

&lt;p&gt;Mateusz started with the &lt;code&gt;getDiscriminantPropertyAccess&lt;/code&gt; function, which takes in &lt;code&gt;computedType&lt;/code&gt; and &lt;code&gt;expr&lt;/code&gt; parameters. From reviewing the code when this feature was added to TypeScript, he identified this function as related to the narrowing process.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7urusf9rxswt3w86nzbd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7urusf9rxswt3w86nzbd.png" alt="Screenshot" width="597" height="210"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the Replay console, you can view the value of properties at a given time during the code execution. Once the functions related to the bug have been isolated, the values of these parameters can be evaluated. This can be done with print statements or using the console.&lt;/p&gt;

&lt;p&gt;Mateusz output &lt;code&gt;computedType&lt;/code&gt; with the helper function &lt;code&gt;.__debugTypeToString()&lt;/code&gt; to further evaluate.&lt;/p&gt;

&lt;p&gt;The issue here is that the type should come back as either &lt;code&gt;string&lt;/code&gt; or &lt;code&gt;number&lt;/code&gt; depending on the variable, but instead both are showing a type of &lt;code&gt;string | number&lt;/code&gt; which is causing the failure because of a type mismatch. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbh88d9s8l9qdk87xqi6h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbh88d9s8l9qdk87xqi6h.png" alt="Screenshot" width="800" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the working example, however, the type evaluated correctly as &lt;code&gt;number&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7qr26aruy7g3pkl9z6au.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7qr26aruy7g3pkl9z6au.png" alt="Screenshot" width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Tracing the root cause
&lt;/h3&gt;

&lt;p&gt;Mateusz then used Replay’s stepping functionality to trace the execution of the code. By comparing the working example with the non-working example, he was able to identify lines that only executed in the working example. The lines after the declaration of &lt;code&gt;access&lt;/code&gt; aren’t executed  when the bug occurs. We can see in the replay that these lines show &lt;code&gt;0 hits&lt;/code&gt; on hover.&lt;/p&gt;


&lt;div&gt;
  &lt;iframe src="https://loom.com/embed/01830047552347d086085e49888ac3b8"&gt;
  &lt;/iframe&gt;
&lt;/div&gt;



&lt;p&gt;Because &lt;code&gt;access&lt;/code&gt; is being evaluated on line &lt;code&gt;79105&lt;/code&gt; and failing the check, the next step is looking at &lt;code&gt;getCandidateDiscriminantPropertyAccess()&lt;/code&gt; to see the return value.&lt;/p&gt;

&lt;p&gt;We can see on line &lt;code&gt;79060&lt;/code&gt; inside this function that &lt;code&gt;reference&lt;/code&gt; is being evaluated to see if &lt;code&gt;ts.isBindingPattern()&lt;/code&gt; or &lt;code&gt;ts.isFunctionExpressionOrArrowFunction()&lt;/code&gt; is true. Because our syntax is &lt;em&gt;method&lt;/em&gt; syntax and &lt;em&gt;not&lt;/em&gt; a function expression or arrow function, the &lt;code&gt;reference&lt;/code&gt; fails the check and the function does not continue.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F66qorgab8969p8ykwjpw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F66qorgab8969p8ykwjpw.png" alt="Screenshot" width="562" height="186"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can see this by printing the function to the console to see the return value.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Broken version returns false, type is not narrowed&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6sbg99d0tjktkhbo99g6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6sbg99d0tjktkhbo99g6.png" alt="Screenshot" width="800" height="116"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Working version returns true, type is narrowed&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftasi0jfecuqxmwy3w0ig.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftasi0jfecuqxmwy3w0ig.png" alt="Screenshot" width="800" height="85"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;We need to ensure that a function declared with the method syntax also evaluates true within the &lt;code&gt;getCandidateDiscriminantPropertyAccess()&lt;/code&gt; function so it is narrowed appropriately.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/microsoft/TypeScript/pull/48110" rel="noopener noreferrer"&gt;Here is the pull request&lt;/a&gt; with the suggested resolution by Mateusz, which adds an option for &lt;code&gt;isObjectLiteralMethod()&lt;/code&gt; to the check to ensure that method syntax will also trigger the destructuring of the discriminant union. The fix was merged and this bug has officially been resolved! 🥳&lt;/p&gt;

&lt;p&gt;👉 Want to record your own Node applications? Check out the &lt;a href="https://docs.replay.io/docs/recording-node-js-0820cd9dacfe4c7ea78b25c8fae17ac4" rel="noopener noreferrer"&gt;Replay Node guide here&lt;/a&gt; and let us know at &lt;a href="https://replay.io/discord" rel="noopener noreferrer"&gt;replay.io/discord&lt;/a&gt; if you have any questions!&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>debugging</category>
      <category>opensource</category>
      <category>node</category>
    </item>
  </channel>
</rss>
