<?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: Jop</title>
    <description>The latest articles on DEV Community by Jop (@_904affc0d58589ee2ac5).</description>
    <link>https://dev.to/_904affc0d58589ee2ac5</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3913909%2F3e8e9765-3bf5-41f1-9717-bfe626ba331b.png</url>
      <title>DEV Community: Jop</title>
      <link>https://dev.to/_904affc0d58589ee2ac5</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/_904affc0d58589ee2ac5"/>
    <language>en</language>
    <item>
      <title>I ran one API response through two JSON-to-Zod converters. One silently turned every field into z.string().</title>
      <dc:creator>Jop</dc:creator>
      <pubDate>Wed, 24 Jun 2026 13:00:00 +0000</pubDate>
      <link>https://dev.to/_904affc0d58589ee2ac5/i-ran-one-api-response-through-two-json-to-zod-converters-one-silently-turned-every-field-into-965</link>
      <guid>https://dev.to/_904affc0d58589ee2ac5/i-ran-one-api-response-through-two-json-to-zod-converters-one-silently-turned-every-field-into-965</guid>
      <description>&lt;p&gt;You have an API response. You want a Zod schema. So you paste the JSON into a JSON-to-Zod converter, copy the output, and ship it.&lt;/p&gt;

&lt;p&gt;Here's the trap: a lot of those converters infer &lt;strong&gt;basic types only&lt;/strong&gt;. Your &lt;code&gt;email&lt;/code&gt;, your &lt;code&gt;uuid&lt;/code&gt;, your &lt;code&gt;url&lt;/code&gt;, your ISO timestamp — they all come out as &lt;code&gt;z.string()&lt;/code&gt;. The schema compiles, the types look right, and your validator quietly accepts &lt;code&gt;"not-an-email"&lt;/code&gt;, &lt;code&gt;"ftp://nope"&lt;/code&gt;, and &lt;code&gt;"2026-99-99"&lt;/code&gt; forever.&lt;/p&gt;

&lt;p&gt;I wanted to see exactly how much gets lost, so I ran the &lt;strong&gt;same&lt;/strong&gt; payload through two tools and diffed the output. Everything below is real, copy-pasteable output — nothing edited.&lt;/p&gt;

&lt;h2&gt;
  
  
  The input
&lt;/h2&gt;

&lt;p&gt;A pretty ordinary user object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3f2a9c1e-5b7d-4e8a-9f1c-2d3e4f5a6b7c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ada@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"website"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://ada.dev"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rating"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-03-04T10:15:30Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_active"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Lyon"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"zip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"69001"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"early-adopter"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"beta"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Tool 1 — &lt;code&gt;json-to-zod&lt;/code&gt; (npm)
&lt;/h2&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;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;website&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;rating&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;is_active&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;Structurally correct. But every meaningful field is a bare &lt;code&gt;z.string()&lt;/code&gt; / &lt;code&gt;z.number()&lt;/code&gt;. This schema will happily validate &lt;code&gt;email: "lol"&lt;/code&gt; and &lt;code&gt;created_at: "yesterday"&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tool 2 — TypeMorph
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;zod&lt;/span&gt;&lt;span class="dl"&gt;"&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;const&lt;/span&gt; &lt;span class="nx"&gt;userAddressSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;A-Z0-9&lt;/span&gt;&lt;span class="se"&gt;][&lt;/span&gt;&lt;span class="sr"&gt;A-Z0-9&lt;/span&gt;&lt;span class="se"&gt;\s\-]{1,8}[&lt;/span&gt;&lt;span class="sr"&gt;A-Z0-9&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;$/i&lt;/span&gt;&lt;span class="p"&gt;),&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;type&lt;/span&gt; &lt;span class="nx"&gt;UserAddress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;userAddressSchema&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;website&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;min&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="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;rating&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;min&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="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;iso&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;is_active&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userAddressSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()),&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;type&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;userSchema&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The honest diff
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;&lt;code&gt;json-to-zod&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;TypeMorph&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;a UUID&lt;/td&gt;
&lt;td&gt;&lt;code&gt;z.string()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;z.uuid()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;email&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;an email&lt;/td&gt;
&lt;td&gt;&lt;code&gt;z.string()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;z.email()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;website&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;a URL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;z.string()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;z.url()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;created_at&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ISO datetime&lt;/td&gt;
&lt;td&gt;&lt;code&gt;z.string()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;z.iso.datetime()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;age&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;34&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;z.number()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;z.number().int()…&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two things worth being honest about:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The format detection is real and value-derived.&lt;/strong&gt; &lt;code&gt;z.uuid()&lt;/code&gt;, &lt;code&gt;z.email()&lt;/code&gt;, &lt;code&gt;z.url()&lt;/code&gt;, &lt;code&gt;z.iso.datetime()&lt;/code&gt; come from looking at the actual values — they're facts, not guesses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The numeric ranges and the zip regex are name-based guesses.&lt;/strong&gt; &lt;code&gt;age&lt;/code&gt; → &lt;code&gt;.min(0).max(150)&lt;/code&gt;, &lt;code&gt;rating&lt;/code&gt; → &lt;code&gt;.min(0).max(5)&lt;/code&gt; are inferred from field &lt;em&gt;names&lt;/em&gt;, not values, so they can be wrong (a rating could be 0–10). TypeMorph lets you turn that "smart" layer off and keep only the facts. I'd rather it be upfront about which is which than pretend a guess is a fact.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;A Zod schema's whole job is to reject bad data. &lt;code&gt;z.string()&lt;/code&gt; where you meant &lt;code&gt;z.email()&lt;/code&gt; is a validator that passes the exact inputs you wrote it to catch. You get the &lt;em&gt;feeling&lt;/em&gt; of validation with none of the coverage — which is worse than no schema, because you stop checking.&lt;/p&gt;

&lt;h2&gt;
  
  
  When &lt;code&gt;json-to-zod&lt;/code&gt; is still fine
&lt;/h2&gt;

&lt;p&gt;It's a tiny, zero-dependency function you can call inside your own code. If you just want a rough scaffold and you'll add &lt;code&gt;.email()&lt;/code&gt; / &lt;code&gt;.uuid()&lt;/code&gt; / enums by hand, it does the job. It's also basically unmaintained at this point (last publish was years ago), so don't expect Zod v4 output or fixes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try the comparison yourself
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx typemorph-cli zod your-response.json &lt;span class="nt"&gt;--root&lt;/span&gt; User
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;CLI: &lt;code&gt;npm i -g typemorph-cli&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Web (paste JSON, get 40+ formats): &lt;a href="https://typemorph.dev" rel="noopener noreferrer"&gt;https://typemorph.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Full honest comparison: &lt;a href="https://typemorph.dev/alternatives/json-to-zod" rel="noopener noreferrer"&gt;https://typemorph.dev/alternatives/json-to-zod&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;I'm building TypeMorph in public — a JSON/OpenAPI → Zod/TypeScript/Go/Prisma converter that tries to detect what your data actually is instead of dumping everything to &lt;code&gt;z.string()&lt;/code&gt;. Feedback (and "your inference got this wrong") very welcome.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How accurate are JSON Zod generators actually? I compared three on the same input</title>
      <dc:creator>Jop</dc:creator>
      <pubDate>Sun, 21 Jun 2026 14:39:24 +0000</pubDate>
      <link>https://dev.to/_904affc0d58589ee2ac5/how-accurate-are-json-zod-generators-actually-i-compared-three-on-the-same-input-fj5</link>
      <guid>https://dev.to/_904affc0d58589ee2ac5/how-accurate-are-json-zod-generators-actually-i-compared-three-on-the-same-input-fj5</guid>
      <description>&lt;p&gt;I've been relying on JSON→Zod generators to scaffold schemas from API responses,&lt;br&gt;
and kept wondering — how much do they actually infer vs just dumping &lt;code&gt;z.string()&lt;/code&gt;?&lt;br&gt;
So I ran three tools on the same payload to find out.&lt;/p&gt;

&lt;p&gt;Input (same for all):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"a@b.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"a1b2c3d4-…"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"joined"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-01-01T00:00:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"website"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://a.com"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;json-to-zod&lt;/strong&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="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;joined&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;website&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;Zero format detection. Technically correct, validates nothing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;quicktype (&lt;code&gt;-l typescript-zod&lt;/code&gt;)&lt;/strong&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="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;joined&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;coerce&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;// caught the date&lt;/span&gt;
  &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;website&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;1 out of 6. Better, but stops there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TypeMorph&lt;/strong&gt; &lt;em&gt;(disclosure: side project I built — take with salt)&lt;/em&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="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pending&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;closed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;joined&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;iso&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;min&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="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;website&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;url&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 interesting design questions this raised for me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Enums from a single sample&lt;/strong&gt; → you get &lt;code&gt;z.enum(["active"])&lt;/code&gt; which rejects valid future values.
You need multiple samples aggregated before enums are safe. Worth it?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Name-based heuristics&lt;/strong&gt; (&lt;code&gt;age → .int().min(0).max(150)&lt;/code&gt;) — useful shortcut or too opinionated?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open-vocab fields&lt;/strong&gt; like &lt;code&gt;currency&lt;/code&gt; or &lt;code&gt;country&lt;/code&gt; should stay &lt;code&gt;z.string()&lt;/code&gt; even if they look like enums&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Is anyone doing something smarter here? Curious what approach others take&lt;br&gt;
when generating schemas from real API data.&lt;/p&gt;

</description>
      <category>api</category>
      <category>tooling</category>
      <category>typescript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Why I stopped copy-pasting JSON into online converters and built TypeMorph</title>
      <dc:creator>Jop</dc:creator>
      <pubDate>Sun, 14 Jun 2026 11:20:42 +0000</pubDate>
      <link>https://dev.to/_904affc0d58589ee2ac5/why-i-stopped-copy-pasting-json-into-online-converters-and-built-typemorph-42mp</link>
      <guid>https://dev.to/_904affc0d58589ee2ac5/why-i-stopped-copy-pasting-json-into-online-converters-and-built-typemorph-42mp</guid>
      <description>&lt;p&gt;Why I stopped copy-pasting JSON into online converters and built TypeMorph&lt;/p&gt;

&lt;p&gt;Every developer has done it. You grab a JSON response from an API, paste it into some random online tool, and get a TypeScript interface back. Fast, convenient — and potentially a huge security mistake.&lt;/p&gt;

&lt;p&gt;I did this for years before I realized what I was actually doing: sending production API responses, internal data models, and sometimes customer data to servers I knew nothing about.&lt;/p&gt;

&lt;p&gt;So I built TypeMorph (&lt;a href="https://typemorph.dev" rel="noopener noreferrer"&gt;https://typemorph.dev&lt;/a&gt;) — a schema conversion tool that runs 100% in your browser. No servers. No uploads. Your data never leaves your machine.&lt;/p&gt;

&lt;p&gt;The problem with online converters&lt;/p&gt;

&lt;p&gt;Most "free" online converters are not actually free. You're paying with your data.&lt;/p&gt;

&lt;p&gt;When you paste a JSON payload into a third-party tool:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;That payload hits their servers&lt;/li&gt;
&lt;li&gt;It may be logged, stored, or analyzed&lt;/li&gt;
&lt;li&gt;If it contains PII or proprietary business logic, you just leaked it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For personal projects this might be fine. But for a startup or enterprise team? One slip and you've violated your own data handling policies.&lt;/p&gt;

&lt;p&gt;What TypeMorph does differently&lt;/p&gt;

&lt;p&gt;TypeMorph uses a single AST pipeline that runs entirely in your browser:&lt;/p&gt;

&lt;p&gt;JSON / YAML / OpenAPI / JSON Schema&lt;br&gt;
  └─► inferSchema()  →  Schema AST&lt;br&gt;
        └─► generators  →  160+ output formats&lt;/p&gt;

&lt;p&gt;Everything is client-side. There is no backend. The conversion happens in your tab and stays there.&lt;/p&gt;

&lt;p&gt;160+ output formats&lt;/p&gt;

&lt;p&gt;TypeMorph doesn't just do TypeScript. Paste one JSON object and get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validation schemas: Zod, Yup, Valibot, ArkType, Joi&lt;/li&gt;
&lt;li&gt;Languages: Go, Rust, Python (Pydantic), Java, Kotlin, Swift, C#, Dart&lt;/li&gt;
&lt;li&gt;Databases: Prisma, Drizzle, Kysely, MySQL, PostgreSQL, MongoDB&lt;/li&gt;
&lt;li&gt;API: OpenAPI, Postman, Protobuf, GraphQL&lt;/li&gt;
&lt;li&gt;Frontend: React Props, Vue Props, Pinia, Redux Slice&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And it's not dumb code generation. The Zod generator applies semantic validators automatically:&lt;/p&gt;

&lt;p&gt;// Input JSON: { "email": "&lt;a href="mailto:user@example.com"&gt;user@example.com&lt;/a&gt;", "age": 28, "score": 94 }&lt;/p&gt;

&lt;p&gt;// TypeMorph output:&lt;br&gt;
export const rootSchema = z.object({&lt;br&gt;
  email: z.string().email(),           // detected from field name&lt;br&gt;
  age: z.number().int().min(0).max(150), // semantic range&lt;br&gt;
  score: z.number().min(0).max(100),   // semantic range&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;Schema Quality Score&lt;/p&gt;

&lt;p&gt;TypeMorph grades your schema A–F and tells you why:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fields typed as any&lt;/li&gt;
&lt;li&gt;Missing format constraints (uuid, email, datetime…)&lt;/li&gt;
&lt;li&gt;Mixed naming conventions (camelCase vs snake_case)&lt;/li&gt;
&lt;li&gt;Overly deep nesting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's like a linter for your data model.&lt;/p&gt;

&lt;p&gt;Breaking Change Detector&lt;/p&gt;

&lt;p&gt;Paste two versions of a schema and TypeMorph tells you exactly what broke:&lt;/p&gt;

&lt;p&gt;0% compatible · 7 breaking · 1 info&lt;/p&gt;

&lt;p&gt;❌ id        REMOVED  — Required field 'id' was removed&lt;br&gt;
❌ name      REMOVED  — Required field 'name' was removed&lt;br&gt;
⚠️ user_id  ADDED    — New required field. Existing payloads will be invalid&lt;/p&gt;

&lt;p&gt;This alone has saved me from shipping breaking API changes more than once.&lt;/p&gt;

&lt;p&gt;VS Code Extension&lt;/p&gt;

&lt;p&gt;Last week I shipped a VS Code extension so you don't even need to open a browser.&lt;/p&gt;

&lt;p&gt;Ctrl+Shift+T on any JSON file → pick a format → output appears in a side panel.&lt;/p&gt;

&lt;p&gt;Install it from the VS Code Marketplace (&lt;a href="https://marketplace.visualstudio.com/items?itemName=TypeMorph.typemorph-vscode" rel="noopener noreferrer"&gt;https://marketplace.visualstudio.com/items?itemName=TypeMorph.typemorph-vscode&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;CLI for CI pipelines&lt;/p&gt;

&lt;p&gt;npm install -g typemorph-cli&lt;/p&gt;

&lt;p&gt;typemorph zod schema.json        # convert to Zod&lt;br&gt;
typemorph quality schema.json    # schema quality score&lt;br&gt;
typemorph diff v1.json v2.json   # breaking change detection&lt;/p&gt;

&lt;p&gt;Try it&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Web: &lt;a href="https://typemorph.dev" rel="noopener noreferrer"&gt;https://typemorph.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;VS Code: Marketplace (&lt;a href="https://marketplace.visualstudio.com/items?itemName=TypeMorph.typemorph-vscode" rel="noopener noreferrer"&gt;https://marketplace.visualstudio.com/items?itemName=TypeMorph.typemorph-vscode&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;CLI: npm install -g typemorph-cli&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've ever pasted company data into a random online tool and immediately felt uneasy about it — TypeMorph is for you.&lt;/p&gt;

&lt;p&gt;What output formats do you wish existed? Drop them in the comments.&lt;/p&gt;

&lt;p&gt;ents.&lt;/p&gt;

</description>
      <category>privacy</category>
      <category>security</category>
      <category>showdev</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Why ChatGPT sucks at generating Types (and how I fixed it)</title>
      <dc:creator>Jop</dc:creator>
      <pubDate>Sat, 30 May 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/_904affc0d58589ee2ac5/why-chatgpt-sucks-at-generating-types-and-how-i-fixed-it-4did</link>
      <guid>https://dev.to/_904affc0d58589ee2ac5/why-chatgpt-sucks-at-generating-types-and-how-i-fixed-it-4did</guid>
      <description>&lt;p&gt;Have you ever pasted a JSON payload into ChatGPT and asked it to generate a TypeScript interface or Go struct? &lt;/p&gt;

&lt;p&gt;Usually, it works. But sometimes, it decides to be "helpful" and changes your &lt;code&gt;user_id&lt;/code&gt; to &lt;code&gt;UserId&lt;/code&gt;, or completely forgets an optional field. In a production CI/CD pipeline, this kind of AI hallucination is a nightmare. &lt;/p&gt;

&lt;p&gt;I got tired of this, so I built &lt;strong&gt;&lt;a href="https://typeflow.pro" rel="noopener noreferrer"&gt;TypeFlow&lt;/a&gt;&lt;/strong&gt;: A developer tool that converts JSON, SQL, or YAML into 16+ languages instantly. But the real secret isn't just AI—it's a &lt;strong&gt;Hybrid Architecture&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: AI is smart, but unpredictable
&lt;/h2&gt;

&lt;p&gt;When dealing with Data Transfer Objects (DTOs), schemas, and type definitions, you need &lt;strong&gt;100% deterministic output&lt;/strong&gt;. If an AI "guesses" a type wrong, your app crashes.&lt;/p&gt;

&lt;p&gt;Legacy tools like QuickType are fully deterministic but feel outdated and struggle with broken syntax. Modern devs just use AI, but sacrifice reliability. &lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: The Hybrid "TypeFlow" Engine
&lt;/h2&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%2F40cp8zrr0tg1iz6q7kqv.gif" 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%2F40cp8zrr0tg1iz6q7kqv.gif" alt=" " width="559" height="263"&gt;&lt;/a&gt;&lt;br&gt;
I wanted the absolute reliability of a compiler, mixed with the "magic" of an LLM. Here is how I built the TypeFlow engine:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Core: Pure AST Parsing (Zero AI)
&lt;/h3&gt;

&lt;p&gt;When you paste JSON or SQL into TypeFlow, it doesn't go to an LLM. Instead, I wrote a custom parser that reads the input and builds a &lt;strong&gt;Unified Abstract Syntax Tree (AST)&lt;/strong&gt; entirely in the browser. &lt;br&gt;
From that single AST, the engine deterministically generates 16 different outputs (TypeScript, Python Pydantic, Go Structs, Rust, Swift, etc.) in &lt;code&gt;0.01&lt;/code&gt; seconds. &lt;br&gt;
&lt;strong&gt;Result:&lt;/strong&gt; Zero hallucinations. 100% reliable types.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The Magic: AI for Semantics and Healing
&lt;/h3&gt;

&lt;p&gt;If the core is pure logic, where does the AI come in? I used the &lt;strong&gt;Gemini API&lt;/strong&gt; for the things logic &lt;em&gt;can't&lt;/em&gt; do easily:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Schema Healing:&lt;/strong&gt; If you paste broken JSON (missing commas, trailing brackets), the AI intercepts it, heals the syntax instantly, and passes it to the AST parser.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Semantic Zod Validation:&lt;/strong&gt; AST parsers just see &lt;code&gt;string&lt;/code&gt;. I added an AI feature that reads the context of your data and infers semantic rules, automatically generating &lt;code&gt;z.string().email()&lt;/code&gt; or &lt;code&gt;z.string().uuid()&lt;/code&gt; for Zod validation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mermaid ER Diagrams:&lt;/strong&gt; The AI reads the AST structure and generates beautiful Mermaid database relationship diagrams in real-time.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; Next.js 14 (App Router) + TailwindCSS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Editor:&lt;/strong&gt; Monaco Editor (VS Code engine in the browser)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI:&lt;/strong&gt; Gemini 2.5 Flash (Using Server-Sent Events &lt;code&gt;alt=sse&lt;/code&gt; for real-time code streaming)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hosting:&lt;/strong&gt; Cloudflare Pages (Blazing fast global edge delivery)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it out!
&lt;/h2&gt;

&lt;p&gt;I just launched the beta version. It's completely free to use right now.&lt;br&gt;
Check it out here: &lt;strong&gt;&lt;a href="https://typeflow.pro" rel="noopener noreferrer"&gt;TypeFlow.pro&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let me know what you think! Have you experienced AI hallucinations breaking your code before? Drop a comment below!👇&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
