<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>APIs on Vedant Andhale</title>
    <link>https://www.vedant.me/tags/apis/</link>
    <description>Recent content in APIs on Vedant Andhale</description>
    <image>
      <url>https://www.vedant.me/</url>
      <link>https://www.vedant.me/</link>
    </image>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Thu, 10 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://www.vedant.me/tags/apis/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>A type annotation does not validate an API response</title>
      <link>https://www.vedant.me/notebook/what-i-got-wrong-about-types/</link>
      <pubDate>Thu, 10 Sep 2026 00:00:00 +0000</pubDate>
      
      <guid>https://www.vedant.me/notebook/what-i-got-wrong-about-types/</guid>
      <description>TypeScript can narrow checked values, but external JSON still needs runtime validation.</description>
      <content:encoded><![CDATA[<p>An API response can arrive as valid JSON and still be the wrong shape. A field may be missing, a number may become a string, or an upstream error may use a different response body.</p>
<p>In TypeScript, asserting a type does not inspect that response:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-ts" data-lang="ts"><span class="line"><span class="cl"><span class="kr">type</span> <span class="nx">Shipment</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">id</span>: <span class="kt">string</span><span class="p">;</span> <span class="nx">delayDays</span>: <span class="kt">number</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">shipment</span> <span class="o">=</span> <span class="p">(</span><span class="k">await</span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">())</span> <span class="kr">as</span> <span class="nx">Shipment</span><span class="p">;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>The assertion tells the compiler what to assume. It does not establish that the server returned those fields.</p>
<h2 id="check-the-boundary">Check the boundary</h2>
<p>For a small value, the check can be explicit:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-ts" data-lang="ts"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">isShipment</span><span class="p">(</span><span class="nx">value</span>: <span class="kt">unknown</span><span class="p">)</span><span class="o">:</span> <span class="nx">value</span> <span class="k">is</span> <span class="nx">Shipment</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">value</span> <span class="o">!==</span> <span class="s2">&#34;object&#34;</span> <span class="o">||</span> <span class="nx">value</span> <span class="o">===</span> <span class="kc">null</span><span class="p">)</span> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;id&#34;</span> <span class="k">in</span> <span class="nx">value</span> <span class="o">&amp;&amp;</span> <span class="k">typeof</span> <span class="nx">value</span><span class="p">.</span><span class="nx">id</span> <span class="o">===</span> <span class="s2">&#34;string&#34;</span> <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;delayDays&#34;</span> <span class="k">in</span> <span class="nx">value</span> <span class="o">&amp;&amp;</span> <span class="k">typeof</span> <span class="nx">value</span><span class="p">.</span><span class="nx">delayDays</span> <span class="o">===</span> <span class="s2">&#34;number&#34;</span> <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">Number</span><span class="p">.</span><span class="nb">isFinite</span><span class="p">(</span><span class="nx">value</span><span class="p">.</span><span class="nx">delayDays</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">response</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s2">&#34;Shipment request failed&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">payload</span>: <span class="kt">unknown</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">isShipment</span><span class="p">(</span><span class="nx">payload</span><span class="p">))</span> <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s2">&#34;Unexpected shipment response&#34;</span><span class="p">);</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>This validates only the contract shown. It does not decide whether a negative delay is meaningful or whether the identifier belongs to the current user. Those are separate domain and permission checks.</p>
<p>For larger schemas, a runtime validation library may make the contract easier to maintain. The important point is where the evidence comes from: a check that runs on the incoming value, not an assertion that disappears when the code is compiled.</p>
<p>Once the value passes that boundary, static types are useful for keeping the rest of the application consistent. They help prevent an internal caller from accidentally passing the wrong structure. Runtime validation and static checking support different parts of the same path.</p>
<p>Reference: <a href="https://www.typescriptlang.org/docs/handbook/2/narrowing.html">TypeScript handbook: narrowing and type predicates</a>.</p>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
