<?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: Dmitry Zakharov</title>
    <description>The latest articles on DEV Community by Dmitry Zakharov (@dzakh).</description>
    <link>https://dev.to/dzakh</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%2F988236%2Fbe515a70-aadf-48ea-ba1f-e92410a78528.jpeg</url>
      <title>DEV Community: Dmitry Zakharov</title>
      <link>https://dev.to/dzakh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dzakh"/>
    <language>en</language>
    <item>
      <title>I Ditched My Keyboard for Voice Coding 🎙️</title>
      <dc:creator>Dmitry Zakharov</dc:creator>
      <pubDate>Wed, 08 Apr 2026 10:22:00 +0000</pubDate>
      <link>https://dev.to/dzakh/i-ditched-my-keyboard-for-voice-coding-54n9</link>
      <guid>https://dev.to/dzakh/i-ditched-my-keyboard-for-voice-coding-54n9</guid>
      <description>&lt;p&gt;This is something that wouldn't be possible even five months ago. The tools to write with voice already existed, but dictating all the brackets and quotes in a programming language? A nightmare. But then AI changed everything.&lt;/p&gt;

&lt;p&gt;I'm working at &lt;a href="https://envio.dev/" rel="noopener noreferrer"&gt;Envio&lt;/a&gt;, the fastest blockchain data provider and indexing tooling, and today I'm able to write 100% of my code using AI. So instead of dictating code itself, I use my voice to tell Claude which code it should write. This is the key insight that makes voice coding actually viable.&lt;/p&gt;

&lt;p&gt;But let me start from the beginning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I wanted to ditch the keyboard
&lt;/h2&gt;

&lt;p&gt;My main motivation was frustration with the ergonomic setup. I got really used to the Mac keyboard, but working on my laptop is a painful experience for my neck and wrists. For a long time, I wanted to move away from the laptop keyboard to something else. I bought an &lt;a href="https://www.logitech.com/en-us/products/mice/mx-ergo-s-wireless-trackball-mouse.html" rel="noopener noreferrer"&gt;MX Ergo S&lt;/a&gt; mouse and a &lt;a href="https://www.moergo.com/pages/glove80" rel="noopener noreferrer"&gt;Glove80&lt;/a&gt; split keyboard, but the learning curve to switch to them was just too much for me.&lt;/p&gt;

&lt;p&gt;So I thought — what if I try to get rid of the keyboard completely? Instead of learning to type on a new split keyboard, what if I start speaking instead? Speaking is sometimes even faster than writing. In my case, it's not such a big benefit because I type quite fast, but when you're in a flow, it might win you a few seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  WhisperFlow: The first try
&lt;/h2&gt;

&lt;p&gt;I started my experience with &lt;a href="https://wisprflow.ai/" rel="noopener noreferrer"&gt;WhisperFlow&lt;/a&gt; — my colleagues recommended it to me. It was good, but not good enough. So I postponed it for a few months.&lt;/p&gt;

&lt;p&gt;But then I finally decided that I'm going to stop using my laptop keyboard. I need a change. Next week, I'm going to start using WhisperFlow 100%. Through friction, but I'll do this.&lt;/p&gt;

&lt;p&gt;I quite quickly reached the 2,000 words quota, so I upgraded to the pro plan. The experience was actually quite decent. My workflow is fully AI-driven — I use Claude to write all the code, giving it detailed instructions and following best practices of AI-driven development. And it worked quite well with voice, with some learning period, but it didn't make me sacrifice my performance.&lt;/p&gt;

&lt;p&gt;But there were frustrating things. WhisperFlow quite often didn't get me right. The auto-correction helped in some cases, but it didn't fire 100% of the time. You still had to reread the output and fix it from time to time.&lt;/p&gt;

&lt;p&gt;Initially, I started fixing errors with the keyboard. But by the second day, I finally switched to MX Ergo as my mouse and used it to select the text with errors. Then I used WhisperFlow again to re-dictate correctly. This worked quite decently.&lt;/p&gt;

&lt;p&gt;There's also a command mode where you can select some part of the text, say a command like "fix grammar" or "make this sound more professional", and it sends it to an LLM to return a modified output. An interesting feature, but after discovering it, I actually never used it. The times I tried to use it on a big text to fix grammar, it just said servers are overloaded. So it didn't work. But I think this has decent potential.&lt;/p&gt;

&lt;h2&gt;
  
  
  Switching to Aqua Voice
&lt;/h2&gt;

&lt;p&gt;After working with WhisperFlow for a week, I had frustrations with it getting only about 80% of my speech right and the need to frequently correct it. So I asked Claude to recommend me a more accurate and fast solution for voice typing. It suggested &lt;a href="https://aquavoice.com/" rel="noopener noreferrer"&gt;Aqua Voice&lt;/a&gt;, saying that a lot of Reddit users praise its accuracy and speed — they use their own model.&lt;/p&gt;

&lt;p&gt;I decided to give it a try. The subscription costs $10, compared to $15 for WhisperFlow. Their own model was available only on the paid plan, and the free model didn't work well.&lt;/p&gt;

&lt;p&gt;Aqua Voice was faster, but the accuracy was maybe only a few percent better than WhisperFlow. If you want more accuracy, in my experience, there's almost no difference.&lt;/p&gt;

&lt;p&gt;What was frustrating in Aqua Voice is the much worse awareness of cursor placement. In WhisperFlow, if you place your cursor somewhere in the middle of the text and dictate a few words, it just puts them perfectly there. But Aqua Voice always capitalizes the words and adds a dot at the end — super frustrating when editing already written text.&lt;/p&gt;

&lt;p&gt;Aqua Voice claims a feature where they can access your page, see the words already present, and use them as context. But I didn't see it used well. They also don't have a command mode like WhisperFlow has (which I didn't use anyway). Still, I think this might be the future of voice typing, so I'd like to have it in my arsenal.&lt;/p&gt;

&lt;h2&gt;
  
  
  WhisperFlow vs Aqua Voice: The verdict (so far)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href="https://wisprflow.ai/" rel="noopener noreferrer"&gt;WhisperFlow&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href="https://aquavoice.com/" rel="noopener noreferrer"&gt;Aqua Voice&lt;/a&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Accuracy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~80%&lt;/td&gt;
&lt;td&gt;Slightly better&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cursor awareness&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Great&lt;/td&gt;
&lt;td&gt;Frustrating&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Command mode&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Price&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$15/mo&lt;/td&gt;
&lt;td&gt;$10/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Speed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;td&gt;Faster&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For now, I'm going to try switching back to WhisperFlow because I really value the ability to edit parts of written text with voice. Maybe my experience will be frustrating — usually it's much easier to notice when something got worse than subtle improvements. If so, I'll update this article after a few more weeks.&lt;/p&gt;

&lt;h2&gt;
  
  
  The results
&lt;/h2&gt;

&lt;p&gt;I'm only two weeks into this. This article itself was dictated with Aqua Voice, with some AI formatting afterwards. But here's what I can already say:&lt;/p&gt;

&lt;p&gt;My neck got much more relaxed. I almost don't type anything unless I need to write some really specific numbers or code examples for documentation. I also started using a stand for my laptop and having the monitor in a better position for my neck. The Glove80 is still not used — I use the F5 button to start voice dictating on my laptop standing on a stand. It's a bit tough on the hands to reach for it in that position, but it's already much better than it used to be.&lt;/p&gt;

&lt;p&gt;I'm quite happy with how it goes. I think voice dictating is the future of programming and how we interact with AI.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about Claude's built-in voice input?
&lt;/h2&gt;

&lt;p&gt;You might be wondering — Claude has voice input, why not just use that? I tried it, and I didn't like it. The problem is that it automatically sends the message when you finish talking, so you don't have time to actually think.&lt;/p&gt;

&lt;p&gt;The way I use a dictating tool is: I press F5 once and start speaking, sometimes with long pauses to think about what I want to say next. When you're typing, it's easier to structure your thoughts — you can pause, reread, adjust. Then I press F5 again to commit the message. With Claude's built-in voice input, whenever you have a pause for a few seconds to think, it just immediately submits what you've said and starts answering you, even though you're not done yet.&lt;/p&gt;

&lt;p&gt;Another benefit of a standalone dictation tool is that it works everywhere. I use it in Claude, in Discord, in Telegram, in my editor — anywhere I need to type. I even started using voice dictation on my iPhone via the free Microsoft SwiftKey keyboard app.&lt;/p&gt;




&lt;p&gt;If you have any recommendations about other voice dictating tools I should try, give me a suggestion. Follow me on &lt;a href="https://x.com/dzakh_dev" rel="noopener noreferrer"&gt;X&lt;/a&gt;, and if you're curious, check out &lt;a href="https://github.com/enviodev/hyperindex" rel="noopener noreferrer"&gt;HyperIndex&lt;/a&gt; — the project I'm working on at &lt;a href="https://envio.dev/" rel="noopener noreferrer"&gt;Envio&lt;/a&gt;. It's an ultra-fast open-source indexing framework for blockchain data, and you can see how my daily work looks like.&lt;/p&gt;

</description>
      <category>health</category>
      <category>voice</category>
      <category>keyboard</category>
      <category>wrists</category>
    </item>
    <item>
      <title>Zod v4 got 17x Slower than v3 🚦</title>
      <dc:creator>Dmitry Zakharov</dc:creator>
      <pubDate>Sat, 03 May 2025 16:56:00 +0000</pubDate>
      <link>https://dev.to/dzakh/zod-v4-17x-slower-and-why-you-should-care-1m1</link>
      <guid>https://dev.to/dzakh/zod-v4-17x-slower-and-why-you-should-care-1m1</guid>
      <description>&lt;p&gt;Hey everyone! 👋&lt;/p&gt;

&lt;p&gt;I'm &lt;a href="https://x.com/dzakh_dev" rel="noopener noreferrer"&gt;Dmitry&lt;/a&gt;, the creator of &lt;a href="https://dev.to/dzakh/welcome-sury-the-fastest-schema-with-next-gen-dx-5gl4"&gt;Sury&lt;/a&gt;—the fastest schema library out there. If you’re a fan of &lt;a href="https://v4.zod.dev/" rel="noopener noreferrer"&gt;Zod&lt;/a&gt; (and who isn’t?), you’ll want to read this. Today, I want to share some surprising findings about Zod v4’s performance, what it means for you, and how to avoid the pitfalls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Zod v4: 17x Slower? Not Quite, But...
&lt;/h2&gt;

&lt;p&gt;Let’s start with a little clickbait:&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Zod v4 became 17 times slower, and nobody noticed 🙈&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is 100% true, but of course, that’s not the whole story. Let’s dig in.&lt;/p&gt;

&lt;p&gt;Recently, while prepping for the big &lt;a href="https://dev.to/dzakh/welcome-sury-the-fastest-schema-with-next-gen-dx-5gl4"&gt;Sury v10&lt;/a&gt; release, I decided to rerun my benchmarks with &lt;a href="https://v4.zod.dev/" rel="noopener noreferrer"&gt;Zod v4&lt;/a&gt;. The results? Fascinating.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For a non-trivial schema, Zod v4 is now &lt;strong&gt;8x faster&lt;/strong&gt; than before.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;But&lt;/strong&gt; when you create a schema and use it just once (a common pattern in React components), performance drops significantly—down to about &lt;strong&gt;6 ops/ms&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You might think, “6 ops/ms is still fast!” But in UI-heavy apps, every millisecond counts. If you’re using Zod in your React components, this could mean a noticeable performance hit.&lt;/p&gt;
&lt;h3&gt;
  
  
  Schema from the benchmark
&lt;/h3&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="c1"&gt;// 13.5 kB (min + gzip)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;zodSchema&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;number&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;negNumber&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;maxNumber&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;string&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;longString&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;boolean&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;deeplyNested&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;foo&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;num&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;bool&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="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;
  
  
  What Changed in Zod v4?
&lt;/h2&gt;

&lt;p&gt;My hunch was that Zod v4 started using &lt;code&gt;eval&lt;/code&gt; (or, more precisely, JIT compilation via &lt;code&gt;new Function&lt;/code&gt;) for validation. This isn’t a bad thing — libraries like &lt;a href="https://github.com/sinclairzx81/typebox" rel="noopener noreferrer"&gt;TypeBox&lt;/a&gt;, &lt;a href="https://arktype.io/" rel="noopener noreferrer"&gt;ArkType&lt;/a&gt;, and even &lt;a href="https://github.com/DZakh/sury" rel="noopener noreferrer"&gt;Sury&lt;/a&gt; use similar techniques for speed.&lt;/p&gt;

&lt;p&gt;But there’s a tradeoff:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Schema creation&lt;/strong&gt; becomes slower (because of the JIT compilation step).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Although, it's not a problem for most users, because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Validation&lt;/strong&gt; becomes much faster (once the schema is compiled and used multiple times).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Zod v3 was already a bit slow at schema creation, but v4 takes it further. If you’re creating schemas on the fly (again, think React), you might feel the slowdown.&lt;/p&gt;
&lt;h2&gt;
  
  
  Is Eval/JIT Bad? Not Really.
&lt;/h2&gt;

&lt;p&gt;There’s a common myth that &lt;code&gt;eval&lt;/code&gt;/&lt;code&gt;new Function&lt;/code&gt; is always slow or unsafe. In reality, when used carefully, it can unlock incredible performance. &lt;a href="https://github.com/DZakh/sury" rel="noopener noreferrer"&gt;Sury&lt;/a&gt; leans heavily on JIT compilation and is still almost on par with &lt;a href="https://valibot.dev/" rel="noopener noreferrer"&gt;Valibot&lt;/a&gt;, which is designed for quick initialization.&lt;/p&gt;

&lt;p&gt;Here’s a quick comparison (min + gzip):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Library&lt;/th&gt;
&lt;th&gt;Import Size&lt;/th&gt;
&lt;th&gt;Parse (same schema)&lt;/th&gt;
&lt;th&gt;Create &amp;amp; Parse Once&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/DZakh/sury" rel="noopener noreferrer"&gt;Sury&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;4.27 kB&lt;/td&gt;
&lt;td&gt;94,828 ops/ms (JIT only)&lt;/td&gt;
&lt;td&gt;166 ops/ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/colinhacks/zod" rel="noopener noreferrer"&gt;Zod v3&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;13.5 kB&lt;/td&gt;
&lt;td&gt;1,191 ops/ms (no JIT)&lt;/td&gt;
&lt;td&gt;93 ops/ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://v4.zod.dev/" rel="noopener noreferrer"&gt;Zod v4&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;13.5 kB&lt;/td&gt;
&lt;td&gt;8,437 ops/ms&lt;/td&gt;
&lt;td&gt;6 ops/ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://valibot.dev/" rel="noopener noreferrer"&gt;Valibot&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;1.23 kB&lt;/td&gt;
&lt;td&gt;1,721 ops/ms (no JIT)&lt;/td&gt;
&lt;td&gt;287 ops/ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/sinclairzx81/typebox" rel="noopener noreferrer"&gt;TypeBox&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;22.8 kB&lt;/td&gt;
&lt;td&gt;99,640 ops/ms (only assert support)&lt;/td&gt;
&lt;td&gt;111 ops/ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://arktype.io/" rel="noopener noreferrer"&gt;ArkType&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;45.8 kB&lt;/td&gt;
&lt;td&gt;67,552 ops/ms&lt;/td&gt;
&lt;td&gt;11 ops/ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Full comparison in &lt;a href="https://github.com/DZakh/sury#comparison" rel="noopener noreferrer"&gt;Sury’s README&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  What Should Zod Users Do?
&lt;/h2&gt;

&lt;p&gt;If you’re using Zod in a backend or for long-lived schemas, you’ll love the new speed. But if you’re creating schemas dynamically (like in React components), you might want to benchmark your app or consider alternatives.&lt;/p&gt;

&lt;p&gt;Zod v4 does offer both normal and JIT-optimized parsing, so you might be able to tweak your usage. But it’s worth being aware of the tradeoffs.&lt;/p&gt;

&lt;p&gt;Also, there should be a way to disable JIT compilation for those who don't want it. I don't know the exact API or whether it's exposed, but I've seen that this option exists in Zod's internals.&lt;/p&gt;
&lt;h2&gt;
  
  
  Enter Sury: The Fastest Schema Library
&lt;/h2&gt;

&lt;p&gt;Okay, shameless plug time! 😅&lt;/p&gt;

&lt;p&gt;I built &lt;a href="https://dev.to/dzakh/welcome-sury-the-fastest-schema-with-next-gen-dx-5gl4"&gt;Sury&lt;/a&gt; to solve exactly these problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Blazing fast&lt;/strong&gt; parsing and validation (thanks to JIT)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tiny bundle size&lt;/strong&gt; and tree-shakable API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Great TypeScript inference&lt;/strong&gt; and developer experience&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standard Schema&lt;/strong&gt; and &lt;strong&gt;JSON Schema&lt;/strong&gt; support out of the box&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Declarative transformations&lt;/strong&gt; and automatic serialization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sury is already used in production by many companies and is compatible with tools like tRPC, TanStack Form, Hono, and more.&lt;/p&gt;

&lt;p&gt;Here’s what using Sury looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;S&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;sury&lt;/span&gt;&lt;span class="dl"&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;filmSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schema&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;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bigint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;title&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="kr"&gt;string&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;S&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;S&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;rating&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="nf"&gt;union&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;G&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;PG&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;PG13&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;R&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;// On hover: S.Schema&amp;lt;{ id: bigint; title: string; tags: string[]; rating: "G" | "PG" | "PG13" | "R"; }, Input&amp;gt;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Film&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Output&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;filmSchema&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;// On hover: { id: bigint; title: string; tags: string[]; rating: "G" | "PG" | "PG13" | "R"; }&lt;/span&gt;

&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseOrThrow&lt;/span&gt;&lt;span class="p"&gt;(&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="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;My first film&lt;/span&gt;&lt;span class="dl"&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="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Loved&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;S&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;filmSchema&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Throws S.Error with message: Failed at ["rating"]: Expected "G" | "PG" | "PG13" | "R", received "S"&lt;/span&gt;

&lt;span class="c1"&gt;// Or do it safely:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safe&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseOrThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filmSchema&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;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&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;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Expected "G" | "PG" | "PG13" | "R", received "S"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And yes, Sury also uses JIT under the hood, but with a focus on both creation and validation speed.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Zod v4 is a fantastic library, and &lt;a href="https://x.com/colinhacks" rel="noopener noreferrer"&gt;Colin&lt;/a&gt; (the author) did an amazing job supporting both normal and JIT-optimized parsing.&lt;/li&gt;
&lt;li&gt;If you care about performance—especially in dynamic or UI-heavy scenarios — benchmark your usage.&lt;/li&gt;
&lt;li&gt;If you want the fastest schema validation, smallest bundle, and next-gen DX, give &lt;a href="https://github.com/DZakh/sury" rel="noopener noreferrer"&gt;Sury&lt;/a&gt; a try.
(And if you like it, a GitHub star would make my day! ⭐)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for reading! If you have questions, feedback, or want to see more benchmarks, let me know in the comments or ping me on &lt;a href="https://x.com/dzakh_dev" rel="noopener noreferrer"&gt;X&lt;/a&gt;. See you soon with more updates! 🚀&lt;/p&gt;

</description>
      <category>zod</category>
      <category>typescript</category>
      <category>schema</category>
      <category>validation</category>
    </item>
    <item>
      <title>Welcome Sury - The fastest schema with next-gen DX 🚀</title>
      <dc:creator>Dmitry Zakharov</dc:creator>
      <pubDate>Mon, 28 Apr 2025 18:45:14 +0000</pubDate>
      <link>https://dev.to/dzakh/welcome-sury-the-fastest-schema-with-next-gen-dx-5gl4</link>
      <guid>https://dev.to/dzakh/welcome-sury-the-fastest-schema-with-next-gen-dx-5gl4</guid>
      <description>&lt;p&gt;Recently I wrote a &lt;a href="https://dev.to/dzakh/javascript-schema-library-from-the-future-5420"&gt;comprehensive article about a JavaScript Schema library from the future&lt;/a&gt; where I introduced &lt;a href="https://github.com/DZakh/sury" rel="noopener noreferrer"&gt;ReScript Schema&lt;/a&gt; and demonstrated some truly groundbreaking features you won't find in any other library. I highly recommend checking it out first.&lt;/p&gt;

&lt;p&gt;While the feedback was overwhelmingly positive, I noticed that despite having TypeScript support, many developers were hesitant simply because of "&lt;a href="https://rescript-lang.org/" rel="noopener noreferrer"&gt;ReScript&lt;/a&gt;" in the name. So after months of dedicated development, I'm excited to present you &lt;a href="https://github.com/DZakh/sury" rel="noopener noreferrer"&gt;Sury&lt;/a&gt; - a TypeScript-first schema validation library that brings all those amazing features while staying true to its ReScript roots 🔥&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Sury?
&lt;/h2&gt;

&lt;p&gt;It's a schema library you'll definitely want to use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API that's easy to read and write, similar to TS types and without noise&lt;/li&gt;
&lt;li&gt;Nice type inference with real types on schema hover&lt;/li&gt;
&lt;li&gt;The fastest parsing with explicit throws&lt;/li&gt;
&lt;li&gt;Convenient error handling with readable error messages&lt;/li&gt;
&lt;li&gt;And more after the code example ☺️
&lt;/li&gt;
&lt;/ul&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="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;S&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;sury&lt;/span&gt;&lt;span class="dl"&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;filmSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schema&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;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bigint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;title&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="kr"&gt;string&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;S&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;S&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;rating&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="nf"&gt;union&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;G&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;PG&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;PG13&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;R&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;// On hover: S.Schema&amp;lt;{ id: bigint; title: string; tags: string[]; rating: "G" | "PG" | "PG13" | "R"; }, Input&amp;gt;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Film&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Output&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;filmSchema&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;// On hover: { id: bigint; title: string; tags: string[]; rating: "G" | "PG" | "PG13" | "R"; }&lt;/span&gt;

&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseOrThrow&lt;/span&gt;&lt;span class="p"&gt;(&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="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;My first film&lt;/span&gt;&lt;span class="dl"&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="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Loved&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;S&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;filmSchema&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Throws S.Error with message: Failed at ["rating"]: Expected "G" | "PG" | "PG13" | "R", received "S"&lt;/span&gt;

&lt;span class="c1"&gt;// Or do it safely:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safe&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseOrThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filmSchema&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;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&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;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Expected "G" | "PG" | "PG13" | "R", received "S"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Honestly, I think that's already enough to make you like &lt;strong&gt;Sury&lt;/strong&gt; and want to try it in your next project, or at least &lt;a href="https://github.com/DZakh/sury" rel="noopener noreferrer"&gt;leave a star on GitHub&lt;/a&gt; ⭐&lt;/p&gt;

&lt;p&gt;Well, probably that's still a lot to ask for, since I've only shown ~10% of the features so far 😅&lt;/p&gt;

&lt;p&gt;Let's continue with the ecosystem talk - the most important part of any library.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ecosystem
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Standard Schema
&lt;/h3&gt;

&lt;p&gt;Set aside the performance, transformations, and serialization features for now, even though they're some of the library's greatest strengths. These features become truly valuable once you have a robust ecosystem to use them with - which was the main focus of the &lt;strong&gt;Sury&lt;/strong&gt; (ReScript Schema v10) release.&lt;/p&gt;

&lt;p&gt;A major enhancement is native support for &lt;a href="https://standardschema.dev/" rel="noopener noreferrer"&gt;Standard Schema&lt;/a&gt;, which is already integrated with over 32 popular libraries. Previous versions required explicitly calling &lt;code&gt;S.toStandard&lt;/code&gt;, but now every &lt;strong&gt;Sury&lt;/strong&gt; schema automatically implements the &lt;a href="https://standardschema.dev/" rel="noopener noreferrer"&gt;Standard Schema&lt;/a&gt; interface.&lt;/p&gt;

&lt;p&gt;Here's an example from the newly released &lt;a href="https://orpc.unnoq.com/" rel="noopener noreferrer"&gt;oRPC&lt;/a&gt; showing how to replace &lt;a href="https://zod.dev/" rel="noopener noreferrer"&gt;&lt;strong&gt;Zod&lt;/strong&gt;&lt;/a&gt; with &lt;a href="https://github.com/DZakh/sury" rel="noopener noreferrer"&gt;&lt;strong&gt;Sury&lt;/strong&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;import { ORPCError, os } from '@orpc/server'
&lt;/span&gt;&lt;span class="gd"&gt;- import { z } from 'zod'
&lt;/span&gt;&lt;span class="gi"&gt;+ import * as S from 'sury'
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;export const listPlanet = os
&lt;/span&gt;  .input(
&lt;span class="gd"&gt;-   z.object({
-     limit: z.number().int().min(1).max(100).optional(),
-     cursor: z.number().int().min(0).default(0),
-   }),
&lt;/span&gt;&lt;span class="gi"&gt;+   S.schema({
+     limit: S.optional(S.int32.with(S.min, 1).with(S.max, 100)),
+     cursor: S.optional(S.int32.with(S.min, 0), 0),
+   })
&lt;/span&gt;  )
  .handler(async ({ input }) =&amp;gt; {
    // your list code here
    return [{ id: 1, name: 'name' }]
  })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it just works - faster, smaller and more flexible 👍&lt;br&gt;
You may notice the &lt;code&gt;.with&lt;/code&gt; calls which might look unfamiliar. This is a new feature introduced in this release that I'll definitely talk about later.&lt;/p&gt;
&lt;h3&gt;
  
  
  JSON Schema
&lt;/h3&gt;

&lt;p&gt;But what's more important and much more widely used is JSON Schema. I used to support it only for ReScript users, but now it's time to share some gold with TypeScript users as well 🫡&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;// 1. Create some advanced schema with transformations&lt;/span&gt;
&lt;span class="c1"&gt;//    S.to - for easy &amp;amp; fast coercion&lt;/span&gt;
&lt;span class="c1"&gt;//    S.shape - for fields transformation&lt;/span&gt;
&lt;span class="c1"&gt;//    S.meta - with examples in Output format&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;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;USER_ID&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with&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="nx"&gt;to&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="nx"&gt;bigint&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;USER_NAME&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="kr"&gt;string&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="nf"&gt;with&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="nx"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&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="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;USER_ID&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="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;USER_NAME&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="nf"&gt;with&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="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;User entity in our system&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;examples&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="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nx"&gt;n&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="s2"&gt;Dmitry&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;span class="c1"&gt;// On hover:&lt;/span&gt;
&lt;span class="c1"&gt;// S.Schema&amp;lt;{&lt;/span&gt;
&lt;span class="c1"&gt;//     id: bigint;&lt;/span&gt;
&lt;span class="c1"&gt;//     name: string;&lt;/span&gt;
&lt;span class="c1"&gt;// }, {&lt;/span&gt;
&lt;span class="c1"&gt;//     USER_ID: string;&lt;/span&gt;
&lt;span class="c1"&gt;//     USER_NAME: string;&lt;/span&gt;
&lt;span class="c1"&gt;// }&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Use built-in S.toJSONSchema and see how everything in the Input format&lt;/span&gt;
&lt;span class="c1"&gt;//    It's just asking to put itself to Fastify or any other server with OpenAPI integration 😁&lt;/span&gt;
&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toJSONSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userSchema&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// {&lt;/span&gt;
&lt;span class="c1"&gt;//   type: "object",&lt;/span&gt;
&lt;span class="c1"&gt;//   additionalProperties: true,&lt;/span&gt;
&lt;span class="c1"&gt;//   properties: {&lt;/span&gt;
&lt;span class="c1"&gt;//     USER_ID: {&lt;/span&gt;
&lt;span class="c1"&gt;//       type: "string",&lt;/span&gt;
&lt;span class="c1"&gt;//     },&lt;/span&gt;
&lt;span class="c1"&gt;//     USER_NAME: {&lt;/span&gt;
&lt;span class="c1"&gt;//       type: "string",&lt;/span&gt;
&lt;span class="c1"&gt;//     },&lt;/span&gt;
&lt;span class="c1"&gt;//   },&lt;/span&gt;
&lt;span class="c1"&gt;//   required: ["USER_ID", "USER_NAME"],&lt;/span&gt;
&lt;span class="c1"&gt;//   description: "User entity in our system",&lt;/span&gt;
&lt;span class="c1"&gt;//   examples: [&lt;/span&gt;
&lt;span class="c1"&gt;//     {&lt;/span&gt;
&lt;span class="c1"&gt;//       USER_ID: "0",&lt;/span&gt;
&lt;span class="c1"&gt;//       USER_NAME: "Dmitry",&lt;/span&gt;
&lt;span class="c1"&gt;//     },&lt;/span&gt;
&lt;span class="c1"&gt;//   ],&lt;/span&gt;
&lt;span class="c1"&gt;// }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Offtopic: Serialization
&lt;/h4&gt;

&lt;p&gt;In the example above I used several advanced features of the library. Let's take a moment to explore one of &lt;strong&gt;Sury&lt;/strong&gt;'s most powerful capabilities - &lt;strong&gt;bidirectional schemas&lt;/strong&gt;. Every schema can both parse and serialize data without needing separate decoders or encoders.&lt;/p&gt;

&lt;p&gt;Let's see how this works with our previous example:&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;// You can use it for parsing Input to Output&lt;/span&gt;
&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseOrThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;USER_ID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;USER_NAME&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Dmitry&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;userSchema&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// { id: 0n, name: "Dmitry" }&lt;/span&gt;
&lt;span class="c1"&gt;// See how "0" is turned into 0n and fields are renamed&lt;/span&gt;

&lt;span class="c1"&gt;// And reverse the schema and use it for parsing Output to Input&lt;/span&gt;
&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseOrThrow&lt;/span&gt;&lt;span class="p"&gt;(&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="mi"&gt;0&lt;/span&gt;&lt;span class="nx"&gt;n&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="s2"&gt;Dmitry&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;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userSchema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// { USER_ID: "0", USER_NAME: "Dmitry" }&lt;/span&gt;
&lt;span class="c1"&gt;// Just use `S.reverse` and get a full-featured schema with switched `Output` and `Input` types&lt;/span&gt;
&lt;span class="c1"&gt;// Note: You can use `S.reverseConvertOrThrow(data, schema)` if you don't need to perform validation&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And while we're at it, I'll quickly show why &lt;strong&gt;Sury&lt;/strong&gt; is the fastest schema library out there. &lt;strong&gt;Sury&lt;/strong&gt; heavily relies on JIT optimizations by inlining all validations and transformations using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function" rel="noopener noreferrer"&gt;&lt;code&gt;new Function&lt;/code&gt;&lt;/a&gt;. It's not something new and used by other libraries like &lt;a href="https://github.com/sinclairzx81/typebox" rel="noopener noreferrer"&gt;TypeBox&lt;/a&gt;, &lt;a href="https://arktype.io/" rel="noopener noreferrer"&gt;ArkType&lt;/a&gt; and even &lt;a href="https://v4.zod.dev/v4" rel="noopener noreferrer"&gt;Zod@4&lt;/a&gt;. But they mostly do basic validations, while &lt;strong&gt;Sury&lt;/strong&gt; embraces transformations as it's core primitive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This is how S.parseOrThrow(data, userSchema) is compiled&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="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="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&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="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;v0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;USER_ID&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="nx"&gt;v2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;USER_NAME&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;v0&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;e&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="nx"&gt;v0&lt;/span&gt;&lt;span class="p"&gt;);&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;v1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;v1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BigInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&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;e&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="nx"&gt;v0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;v2&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;e&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="nx"&gt;v2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v1&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="nx"&gt;v2&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This is how S.reverseConvertOrThrow(data, userSchema) is compiled&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="o"&gt;=&amp;gt;&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;v0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;USER_ID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;v0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;USER_NAME&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Neat, right? 🤯&lt;/p&gt;

&lt;h3&gt;
  
  
  Internal Representation
&lt;/h3&gt;

&lt;p&gt;Let's go back to ecosystem talk. While &lt;strong&gt;Sury&lt;/strong&gt; provides JSON Schema integration out of the box, having a good internal representation is crucial for a schema library. It enables building custom tools and growing the ecosystem. In this version, I've made significant progress by representing every schema as a discriminated union, similar to JSON Schema.&lt;/p&gt;

&lt;p&gt;This design allows you to easily reason about and work with the Input and Output types of your schemas in your code. Here's another advanced example of &lt;strong&gt;Sury&lt;/strong&gt; usage where you can use the internal representation of the input type, to create a dynamic schema for parsing environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;envSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&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="nx"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;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="nx"&gt;schema&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="s2"&gt;boolean&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;union&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="nf"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;t&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;with&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="nx"&gt;to&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="nf"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;with&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="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;schema&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="nf"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;with&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="nx"&gt;to&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="nf"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;with&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="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;schema&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="nf"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;f&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;with&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="nx"&gt;to&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="nf"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;with&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="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;schema&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="nf"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;with&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="nx"&gt;to&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="nf"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;with&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="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;schema&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with&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="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&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;schema&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="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="nx"&gt;schema&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="s2"&gt;bigint&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="nx"&gt;schema&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="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;S&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="nf"&gt;with&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="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;schema&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;jsonString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&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="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseOrThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;envSchema&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="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseOrThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;envSchema&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="nf"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseOrThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;envSchema&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="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// 1&lt;/span&gt;
&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseOrThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;envSchema&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="nx"&gt;bigint&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// 1n&lt;/span&gt;
&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseOrThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;envSchema&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// "1"&lt;/span&gt;
&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseOrThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[0, 1, 2]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;envSchema&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="nf"&gt;array&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="nx"&gt;int32&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt; &lt;span class="c1"&gt;// [0, 1, 2]&lt;/span&gt;
&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseOrThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;envSchema&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="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with&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="nx"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&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="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})));&lt;/span&gt; &lt;span class="c1"&gt;// {field: true}&lt;/span&gt;
&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseOrThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;envSchema&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="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Failed parsing: Expected number, received "true"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This demonstrates how the schema's input type information can be used to modify the schema dynamically, while preserving the original output type that we want.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sury Ecosystem Future
&lt;/h3&gt;

&lt;p&gt;The Environment Variable example is inspired by my library &lt;a href="https://github.com/DZakh/rescript-envsafe" rel="noopener noreferrer"&gt;ReScript EnvSafe&lt;/a&gt;, which enables ReScript users to safely access environment variables using Sury. Over 3 years of Sury development, I've built many tools on top of it. While some remained internal, others evolved into powerful ReScript libraries like &lt;a href="https://github.com/DZakh/rescript-rest" rel="noopener noreferrer"&gt;ReScript Rest&lt;/a&gt; and &lt;a href="https://github.com/enviodev/rescript-stripe" rel="noopener noreferrer"&gt;ReScript Stripe&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I believe porting these libraries to TypeScript will make them even more powerful and accessible to a wider audience.&lt;/p&gt;

&lt;p&gt;Furthermore, since libraries like &lt;a href="https://github.com/DZakh/rescript-rest" rel="noopener noreferrer"&gt;ReScript Rest&lt;/a&gt; and &lt;a href="https://github.com/DZakh/rescript-envsafe" rel="noopener noreferrer"&gt;ReScript EnvSafe&lt;/a&gt; are fundamental to most projects, I plan to integrate them directly into the core &lt;a href="https://github.com/DZakh/sury" rel="noopener noreferrer"&gt;&lt;strong&gt;Sury&lt;/strong&gt;&lt;/a&gt; library:&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;// Comming soon ↓&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;S&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;sury&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;handler&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;@sury/fastify&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Fastify&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;fastify&lt;/span&gt;&lt;span class="dl"&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;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;port&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="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PORT&lt;/span&gt;&lt;span class="dl"&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="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with&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="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;devFallback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;host&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="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;HOST&lt;/span&gt;&lt;span class="dl"&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="kr"&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;devFallback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;debug&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="nf"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--debug&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;optional&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="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="c1"&gt;// It'll throw if the env variables are not provided&lt;/span&gt;
&lt;span class="c1"&gt;// unless NODE_ENV is "development"&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getPosts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/posts&lt;/span&gt;&lt;span class="dl"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;input&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;skip&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="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;skip&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;int32&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;take&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="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;take&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;int32&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;responses&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;data&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="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;postSchema&lt;/span&gt;&lt;span class="p"&gt;))],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// A simple example with query params,&lt;/span&gt;
&lt;span class="c1"&gt;// but it also supports body, headers, path params&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;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;curl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getPosts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;skip&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="na"&gt;take&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="c1"&gt;// curl -X GET /posts?skip=0&amp;amp;take=10&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fastify&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Fastify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;:&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="c1"&gt;// Fastify is just an example. It can be any other framework&lt;/span&gt;

&lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fastify&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getPosts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;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;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// { skip: 0, take: 10 }&lt;/span&gt;
  &lt;span class="k"&gt;return&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Post 1&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;// A simple RPC like API. You don't need to think about the transport layer at all&lt;/span&gt;

&lt;span class="nx"&gt;fastify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;host&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="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getPosts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;skip&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="na"&gt;take&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// fetch("/posts?skip=0&amp;amp;take=10")&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The example already works for ReScript users and would be a great addition for TypeScript users as well. In the next version of the library, I'll focus on polishing the features and making them part of the &lt;strong&gt;Sury&lt;/strong&gt; ecosystem. And thanks to the tree-shakable API they won't be included in the final bundle if you don't use them.&lt;/p&gt;

&lt;h2&gt;
  
  
  New API Features
&lt;/h2&gt;

&lt;p&gt;Coming back to the current &lt;a href="https://github.com/DZakh/sury" rel="noopener noreferrer"&gt;&lt;strong&gt;Sury&lt;/strong&gt;&lt;/a&gt; release I promised to explain some new API changes in detail.&lt;/p&gt;

&lt;h3&gt;
  
  
  S.with
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;S&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="nf"&gt;with&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="nx"&gt;min&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From the version every schema comes with the &lt;code&gt;.with&lt;/code&gt; method. It's a tiny sucrifice for the tree-shakable API, but it allows to modify schemas in a more readable way. Originally you'd write:&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;S&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="nx"&gt;S&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Might not be bad with a single modifier, but what if you need to apply multiple ones?&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;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;optional&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="nf"&gt;max&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="nf"&gt;min&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="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Damn, that's a lot of noise just to apply a few modifiers.&lt;/p&gt;

&lt;p&gt;This was a problem with a modular API, so I've added a new &lt;code&gt;.with&lt;/code&gt; method which is as simple as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;,&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="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it even works with custom helpers you define:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;even&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with&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="nx"&gt;refine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&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="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="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&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="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Expected even number&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="nx"&gt;S&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="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;even&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thanks to this, &lt;strong&gt;Sury&lt;/strong&gt; has a modular API with good tree-shaking, making it 3 times smaller than &lt;a href="https://v4.zod.dev/v4" rel="noopener noreferrer"&gt;Zod@4&lt;/a&gt; by default, while staying very readable and using simple functions.&lt;/p&gt;

&lt;p&gt;In the next version I'll also extend it to modify validation error messages:&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;// Coming soon ↓&lt;/span&gt;

&lt;span class="nx"&gt;S&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="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Your value must be a string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  S.min
&lt;/h3&gt;

&lt;p&gt;Nothing much to say here. All modifiers became polymorphic and can accept any schema as an argument:&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;S&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="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;int32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&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="nf"&gt;min&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&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="nf"&gt;min&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="nf"&gt;array&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="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  S.meta
&lt;/h3&gt;

&lt;p&gt;Inspired by &lt;a href="https://v4.zod.dev/v4" rel="noopener noreferrer"&gt;zod@4&lt;/a&gt; release I added &lt;code&gt;S.meta&lt;/code&gt; API. It's a simple way to add metadata to your schemas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;foo&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;with&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="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;User entity in our system&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;examples&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bar&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use it with &lt;code&gt;S.toJSONSchema&lt;/code&gt; or directly by accessing the schema fields:&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;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;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// "User entity in our system"&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;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;examples&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// [{ foo: "bar" }]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  S.to
&lt;/h3&gt;

&lt;p&gt;Added in the previous release as &lt;code&gt;S.coerce&lt;/code&gt; and renamed to &lt;code&gt;S.to&lt;/code&gt;. But it's worth mentioning again and again.&lt;/p&gt;

&lt;p&gt;This API quicky became the heart of &lt;strong&gt;Sury&lt;/strong&gt; and allows to transform data from one type to another. The most optimised implementation under the hood and automatic serialization support.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;S&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="nf"&gt;with&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="nx"&gt;to&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="nf"&gt;union&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="kr"&gt;number&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="nx"&gt;boolean&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="nf"&gt;parseOrThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 1&lt;/span&gt;
&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseOrThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseOrThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Failed parsing: Expected number | boolean, received "foo"&lt;/span&gt;
&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reverseConvertOrThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// "1"&lt;/span&gt;
&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reverseConvertOrThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// "true"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Might not look like a big deal, but I'm very close to make the feature even more powerful and flexible:&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;// Coming soon ↓&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;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schema&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;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bigint&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="nx"&gt;S&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="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="nf"&gt;parseOrThrow&lt;/span&gt;&lt;span class="p"&gt;(&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&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="s2"&gt;John&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;userSchema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with&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="nx"&gt;to&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="nx"&gt;jsonable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// { id: 1n, name: "John" }&lt;/span&gt;
&lt;span class="c1"&gt;// Automatically convert bigint to string&lt;/span&gt;
&lt;span class="c1"&gt;// and makes other fields compatible to JSON&lt;/span&gt;

&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reverseConvertOrThrow&lt;/span&gt;&lt;span class="p"&gt;(&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="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;n&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="s2"&gt;John&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;userSchema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with&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="nx"&gt;to&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="nx"&gt;jsonable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// { id: "1", name: "John" }&lt;/span&gt;
&lt;span class="c1"&gt;// Instead of using JSON.stringify, inlines JSON by itself in the fastest way&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Sury&lt;/strong&gt; RC is already out and ready to use. Might be early in some places, but the core API been used in production by many companies for almost 3 years and decently reliable.&lt;/p&gt;

&lt;p&gt;Definetely give it a try and leave a star if you like the library!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/DZakh/sury" rel="noopener noreferrer"&gt;https://github.com/DZakh/sury&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From my side I'll continue working on the library and adding more features.&lt;/p&gt;

&lt;p&gt;And if you have any suggestions or feedback, please let me know!&lt;/p&gt;

&lt;h2&gt;
  
  
  Q&amp;amp;A
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What about ReScript support?
&lt;/h3&gt;

&lt;p&gt;I primarily use &lt;a href="https://rescript-lang.org/" rel="noopener noreferrer"&gt;ReScript&lt;/a&gt; and am one of the core maintainers of the language. I'll definitely continue to support it. Even though TypeScript is the main focus now, ReScript users also greatly benefit from the changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  What about &lt;code&gt;new Function&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Sury&lt;/strong&gt; uses &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function" rel="noopener noreferrer"&gt;&lt;code&gt;new Function&lt;/code&gt;&lt;/a&gt; under the hood. This approach makes it the fastest schema library available, but also means it cannot be used in environments that don't allow dynamic code evaluation, like &lt;a href="https://developers.cloudflare.com/workers/runtime-apis/" rel="noopener noreferrer"&gt;CloudFlare Workers&lt;/a&gt;. Most users won't be affected, but might be an issue for some. Regarding safety of the approach you shouldn't be worried. Everything embeded to the generated code is escaped and thoroughly tested. Also, other libraries like &lt;a href="https://github.com/sinclairzx81/typebox" rel="noopener noreferrer"&gt;TypeBox&lt;/a&gt; and &lt;a href="https://v4.zod.dev/v4" rel="noopener noreferrer"&gt;Zod@4&lt;/a&gt; use &lt;code&gt;new Function&lt;/code&gt; under the hood as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  What about Zod?
&lt;/h3&gt;

&lt;p&gt;I'm a big fan of &lt;a href="https://zod.dev/" rel="noopener noreferrer"&gt;Zod&lt;/a&gt; and find it the best choice regarding the ecosystem and developer experience it provides. Although, despite its large size, it's tailored more toward the frontend world. If you're developing a backend or an application with high throughput, you should definitely give &lt;a href="https://github.com/DZakh/sury" rel="noopener noreferrer"&gt;&lt;strong&gt;Sury&lt;/strong&gt;&lt;/a&gt; a try.&lt;/p&gt;

&lt;h3&gt;
  
  
  What about TypeBox?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/sinclairzx81/typebox" rel="noopener noreferrer"&gt;TypeBox&lt;/a&gt; is another great library, but it's tailored more toward validation rather than transformations. Also, I find TypeBox's API not as readable as &lt;strong&gt;Sury&lt;/strong&gt;'s. But the library definitely has some great ideas to get inspired by, such as having JSON Schema as a first-class citizen. I strongly consider adding this for &lt;strong&gt;Sury&lt;/strong&gt; v11.&lt;/p&gt;

&lt;h3&gt;
  
  
  What about X?
&lt;/h3&gt;

&lt;p&gt;There are many other great libraries out there, and some provide features that &lt;strong&gt;Sury&lt;/strong&gt; currently lacks, such as multi-error support and global error message customization. However, &lt;strong&gt;Sury&lt;/strong&gt; stands out by &lt;strong&gt;excelling&lt;/strong&gt; across many important aspects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Readable API&lt;/li&gt;
&lt;li&gt;TypeScript inference&lt;/li&gt;
&lt;li&gt;Performance&lt;/li&gt;
&lt;li&gt;Bundle size&lt;/li&gt;
&lt;li&gt;Tree-shakable API&lt;/li&gt;
&lt;li&gt;Extensibility&lt;/li&gt;
&lt;li&gt;Serialization&lt;/li&gt;
&lt;li&gt;Asynchronous parsing&lt;/li&gt;
&lt;li&gt;Standard Schema &amp;amp; JSON Schema support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'll write a separate honest deep comparison article soon, but for now I'll leave you with a table comparing &lt;strong&gt;Sury&lt;/strong&gt; with other popular libraries:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href="https://github.com/DZakh/sury" rel="noopener noreferrer"&gt;Sury@10.0.0-rc.0&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href="https://v4.zod.dev/v4" rel="noopener noreferrer"&gt;Zod@4.0.0-beta&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href="https://github.com/sinclairzx81/typebox" rel="noopener noreferrer"&gt;TypeBox@0.34.33&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href="https://valibot.dev/" rel="noopener noreferrer"&gt;Valibot@1.0.0&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href="https://arktype.io/" rel="noopener noreferrer"&gt;ArkType@2.1.20&lt;/a&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Total size&lt;/strong&gt; (min + gzip)&lt;/td&gt;
&lt;td&gt;14.1 kB&lt;/td&gt;
&lt;td&gt;25.9 kB&lt;/td&gt;
&lt;td&gt;31.4 kB&lt;/td&gt;
&lt;td&gt;12.6 kB&lt;/td&gt;
&lt;td&gt;45.9 kB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Benchmark size&lt;/strong&gt; (min + gzip)&lt;/td&gt;
&lt;td&gt;4.27 kB&lt;/td&gt;
&lt;td&gt;13.5 kB&lt;/td&gt;
&lt;td&gt;22.8 kB&lt;/td&gt;
&lt;td&gt;1.23 kB&lt;/td&gt;
&lt;td&gt;45.8 kB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Parse with the same schema&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;94,828 ops/ms&lt;/td&gt;
&lt;td&gt;8,437 ops/ms&lt;/td&gt;
&lt;td&gt;99,640 ops/ms (No transforms)&lt;/td&gt;
&lt;td&gt;1,721 ops/ms&lt;/td&gt;
&lt;td&gt;67,552 ops/ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Create schema &amp;amp; parse once&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;166 ops/ms&lt;/td&gt;
&lt;td&gt;6 ops/ms&lt;/td&gt;
&lt;td&gt;111 ops/ms (No transforms)&lt;/td&gt;
&lt;td&gt;287 ops/ms&lt;/td&gt;
&lt;td&gt;11 ops/ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;JSON Schema&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;S.toJSONSchema&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;z.toJSONSchema&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;👑&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@valibot/to-json-schema&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;T.toJsonSchema&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Standard Schema&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Eval-free&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;⭕ opt-out&lt;/td&gt;
&lt;td&gt;⭕ opt-in&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;⭕ opt-out&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Codegen-free&lt;/strong&gt; (Doesn't need compiler)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Infered TS Type&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;S.Schema&amp;lt;{foo: string}, {foo: string}&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;z.ZodObject&amp;lt;{foo: z.ZodString}, {}&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TObject&amp;lt;{foo: TString}&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;v.ObjectSchema&amp;lt;{readonly foo: v.StringSchema&amp;lt;undefined&amp;gt;}, undefined&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Type&amp;lt;{foo: string}, {}&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ecosystem&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⭐️⭐️&lt;/td&gt;
&lt;td&gt;⭐️⭐️⭐️⭐️⭐️&lt;/td&gt;
&lt;td&gt;⭐️⭐️⭐️⭐️⭐️&lt;/td&gt;
&lt;td&gt;⭐️⭐️⭐️&lt;/td&gt;
&lt;td&gt;⭐️⭐️&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  What about me?
&lt;/h3&gt;

&lt;p&gt;I'm a full-stack developer building the &lt;a href="https://github.com/enviodev/hyperindex" rel="noopener noreferrer"&gt;fastest Block Chain indexer&lt;/a&gt;. I'm also a core maintainer of &lt;a href="https://rescript-lang.org/" rel="noopener noreferrer"&gt;ReScript&lt;/a&gt; language and creator of &lt;a href="https://github.com/DZakh" rel="noopener noreferrer"&gt;many open-source libraries&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And finally, I think it's time to wrap up and get back to building &lt;strong&gt;Sury&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;Thanks for reading and see you on &lt;a href="https://x.com/dzakh_dev" rel="noopener noreferrer"&gt;X&lt;/a&gt; 🫡&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>schema</category>
      <category>json</category>
      <category>validation</category>
    </item>
    <item>
      <title>JavaScript schema library from the Future 🧬</title>
      <dc:creator>Dmitry Zakharov</dc:creator>
      <pubDate>Fri, 21 Feb 2025 18:01:18 +0000</pubDate>
      <link>https://dev.to/dzakh/javascript-schema-library-from-the-future-5420</link>
      <guid>https://dev.to/dzakh/javascript-schema-library-from-the-future-5420</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/DZakh/rescript-schema" rel="noopener noreferrer"&gt;ReScript Schema&lt;/a&gt; - The fastest parser in the entire JavaScript ecosystem with a focus on small bundle size and top-notch DX.&lt;/p&gt;

&lt;p&gt;Why did you not hear about it then, and why should you learn about it now? I started developing the library three years ago, and today, it's at a point others have yet to achieve. I'll prove it in the article, but before we start, I'd like to answer a few questions you might already have.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's a parser?
&lt;/h3&gt;

&lt;p&gt;One of the most basic applications of ReScript Schema is parsing - Accepting unknown JavaScript data, validating it, and returning the result of your desired type. There are dozens of such libraries, and the most popular ones are &lt;a href="https://github.com/colinhacks/zod" rel="noopener noreferrer"&gt;Zod&lt;/a&gt;, &lt;a href="https://github.com/fabian-hiller/valibot" rel="noopener noreferrer"&gt;Valibot&lt;/a&gt;, &lt;a href="https://github.com/runtypes/runtypes" rel="noopener noreferrer"&gt;Runtypes&lt;/a&gt;, &lt;a href="https://github.com/arktypeio/arktype" rel="noopener noreferrer"&gt;Arktype&lt;/a&gt;, &lt;a href="https://github.com/samchon/typia" rel="noopener noreferrer"&gt;Typia&lt;/a&gt;, &lt;a href="https://github.com/ianstormtaylor/superstruct" rel="noopener noreferrer"&gt;Superstruct&lt;/a&gt;, &lt;a href="https://github.com/Effect-TS/schema" rel="noopener noreferrer"&gt;Effect Schema&lt;/a&gt;, and more. Also, even though this is slightly different, validation libraries like &lt;a href="https://github.com/ajv-validator/ajv" rel="noopener noreferrer"&gt;Ajv&lt;/a&gt;, &lt;a href="https://github.com/jquense/yup" rel="noopener noreferrer"&gt;Yup&lt;/a&gt;, and others also stand really close.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is ReScript Schema faster than all of them?
&lt;/h3&gt;

&lt;p&gt;Yes. It's ~100 times faster than &lt;a href="https://github.com/colinhacks/zod" rel="noopener noreferrer"&gt;Zod&lt;/a&gt; and on par with &lt;a href="https://github.com/samchon/typia" rel="noopener noreferrer"&gt;Typia&lt;/a&gt; or &lt;a href="https://github.com/arktypeio/arktype" rel="noopener noreferrer"&gt;Arktype&lt;/a&gt; (&lt;a href="https://moltar.github.io/typescript-runtime-type-benchmarks/" rel="noopener noreferrer"&gt;benchmark&lt;/a&gt;). But often, besides validation, you want to transform data incoming to your system, and here, ReScript Schema overperforms any solution existing in the JavaScript ecosystem.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's ReScript? Isn't the library for JavaScript/TypeScript?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://rescript-lang.org/" rel="noopener noreferrer"&gt;ReScript&lt;/a&gt; is a robustly typed language that compiles to efficient and human-readable JavaScript. And yes, ReScript Schema is written in ReScript, but it also has a really nice JavaScript API with TS types. You don't need to install or run any compiler; &lt;code&gt;npm i rescript-schema&lt;/code&gt; is all you need.&lt;br&gt;
It makes ReScript Schema support 3 languages - JavaScript, TypeScript, and ReScript. This is especially nice when you mix TypeScript and ReScript in a single codebase 👌&lt;/p&gt;
&lt;h3&gt;
  
  
  Are there trade-offs?
&lt;/h3&gt;

&lt;p&gt;Yes. To maximize DX, performance, and bundle size while keeping the library fully runtime, I've decided to use &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval" rel="noopener noreferrer"&gt;eval&lt;/a&gt; under the hood. You needn't worry about the code's dangerous execution, but some environments, like Cloudflare Workers, won't work. In 99% of cases, you don't need to be concerned about this. I just think it's my duty as a creator to let you know about the 1% beforehand.&lt;/p&gt;
&lt;h3&gt;
  
  
  What's the plan?
&lt;/h3&gt;

&lt;p&gt;I'm going to provide an overview of the basic ReScript Schema API and mental model. Then, we'll discuss what makes it stand out from millions of similar libraries (and this is not only performance). I'll also look at some more advanced use cases and discuss the ecosystem, performance, and where it stands with other libraries.&lt;/p&gt;

&lt;p&gt;I hope you'll enjoy it. 😊&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Follow me on &lt;a href="https://x.com/dzakh_dev" rel="noopener noreferrer"&gt;X&lt;/a&gt; to learn more about programming stuff I'm cooking.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Parsing / Validating
&lt;/h2&gt;

&lt;p&gt;Let's start with the most basic use case of ReScript Schema. By the way, if you don't know the difference between parsing (sometimes called decoding) and validating, here's a &lt;a href="https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/" rel="noopener noreferrer"&gt;good article&lt;/a&gt; from Zod's docs. If you're curious about when and why you need to parse data in your application, let me know in the comments. I can write a big article about it, but for now, I assume you are already familiar with the concept.&lt;/p&gt;

&lt;p&gt;Let's finally take a look at the code. I'll go with the TypeScript example first, so it's more familiar for most readers. Everything starts with defining a schema of the data you expect:&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;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;S&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;rescript-schema&lt;/span&gt;&lt;span class="dl"&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;filmSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schema&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;S&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="na"&gt;title&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="kr"&gt;string&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;S&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;S&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;rating&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="nf"&gt;union&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;G&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;PG&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;PG13&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;R&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The schema here is like a type definition that exists in runtime. If you hover over the &lt;code&gt;filmSchema&lt;/code&gt;, you'll see the following 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="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;title&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="nl"&gt;tags&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="nl"&gt;rating&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;G&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PG&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PG13&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;R&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="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a &lt;code&gt;Schema&lt;/code&gt; type that inferred the film object definition. I recommend extracting its value into its own type. This way, you'll have the schema as the source of truth and the &lt;code&gt;Film&lt;/code&gt; type always matching the schema:&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;Film&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Output&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;filmSchema&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After we've defined our &lt;code&gt;Film&lt;/code&gt; type using the schema, we can parse unknown data entering our application to guarantee that it matches what we expect:&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;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseOrThrow&lt;/span&gt;&lt;span class="p"&gt;(&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;My first film&lt;/span&gt;&lt;span class="dl"&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="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Loved&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;S&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;filmSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;//? Throws RescriptSchemaError with message `Failed parsing at ["rating"]. Reason: Expected "G" | "PG" | "PG13" | "R", received "S"`&lt;/span&gt;

&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseOrThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;validDataWithUnknownType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filmSchema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;//? Returns value of the Film type&lt;/span&gt;


&lt;span class="c1"&gt;// If you don't want to throw, you can wrap the operations in S.safe and get S.Result as a return value&lt;/span&gt;
&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safe&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseOrThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filmSchema&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Done! We have valid data here 🙌&lt;/p&gt;

&lt;p&gt;Some experienced users may have noticed that the API is similar to &lt;a href="https://github.com/fabian-hiller/valibot" rel="noopener noreferrer"&gt;Valibot&lt;/a&gt;, but with a unique flavor.&lt;/p&gt;

&lt;p&gt;You can use &lt;code&gt;S.schema&lt;/code&gt; for objects, tuples, and literals. For any kind of union, there's &lt;code&gt;S.union&lt;/code&gt;; even if it's a &lt;a href="https://github.com/DZakh/rescript-schema/blob/main/docs/js-usage.md#discriminated-unions" rel="noopener noreferrer"&gt;discriminated&lt;/a&gt; one, the parser will perform in the most optimized way. I personally have seen this kind of DX only in &lt;a href="https://arktype.io/" rel="noopener noreferrer"&gt;ArkType&lt;/a&gt; so far.&lt;/p&gt;

&lt;p&gt;Also, there are no annoying parentheses; the parse function explicitly says it can throw, and thanks to the modular design, the library tree-shaking is very good.&lt;/p&gt;

&lt;h3&gt;
  
  
  Package size
&lt;/h3&gt;

&lt;p&gt;Since I mentioned tree-shaking, I'd like to quickly note about the package size. The bundle size is an essential metric for a web application, and I'd like to share how ReScript Schema is doing here in comparison with other libraries:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href="mailto:rescript-schema@9.2.2"&gt;rescript-schema@9.2.2&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href="mailto:Zod@3.24.1"&gt;Zod@3.24.1&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href="mailto:Valibot@1.0.0-beta.14"&gt;Valibot@1.0.0-beta.14&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href="mailto:ArkType@2.0.4"&gt;ArkType@2.0.4&lt;/a&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Total size&lt;/strong&gt; (minified + gzipped)&lt;/td&gt;
&lt;td&gt;12.7 kB&lt;/td&gt;
&lt;td&gt;15.2 kB&lt;/td&gt;
&lt;td&gt;12.3 kB&lt;/td&gt;
&lt;td&gt;40.8 kB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Example size&lt;/strong&gt; (minified + gzipped)&lt;/td&gt;
&lt;td&gt;5.14 kB&lt;/td&gt;
&lt;td&gt;14.5 kB&lt;/td&gt;
&lt;td&gt;1.39 kB&lt;/td&gt;
&lt;td&gt;40.7 kB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Playground&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://bundlejs.com/?q=rescript-schema%409.2.2&amp;amp;treeshake=%5B*%5D&amp;amp;text=%22const+filmSchema+%3D+S.schema%28%7B%5Cn++id%3A+S.number%2C%5Cn++title%3A+S.string%2C%5Cn++tags%3A+S.array%28S.string%29%2C%5Cn++rating%3A+S.union%28%5B%5C%22G%5C%22%2C+%5C%22PG%5C%22%2C+%5C%22PG13%5C%22%2C+%5C%22R%5C%22%5D%29%2C%5Cn%7D%29%3B%5Cn%5CnS.parseOrThrow%28null%2C+filmSchema%29%22" rel="noopener noreferrer"&gt;Link&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://bundlejs.com/?q=zod%403.24.1&amp;amp;treeshake=%5B*%5D&amp;amp;text=%22const+filmSchema+%3D+z.object%28%7B%5Cn++id%3A+z.number%28%29%2C%5Cn++title%3A+z.string%28%29%2C%5Cn++tags%3A+z.array%28z.string%28%29%29%2C%5Cn++rating%3A+z.enum%28%5B%5C%22G%5C%22%2C+%5C%22PG%5C%22%2C+%5C%22PG13%5C%22%2C+%5C%22R%5C%22%5D%29%2C%5Cn%7D%29%3B%5Cn%5CnfilmSchema.parse%28null%29%22" rel="noopener noreferrer"&gt;Link&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://bundlejs.com/?q=valibot%401.0.0-beta.14&amp;amp;treeshake=%5B*%5D&amp;amp;text=%22const+filmSchema+%3D+v.object%28%7B%5Cn++id%3A+v.number%28%29%2C%5Cn++title%3A+v.string%28%29%2C%5Cn++tags%3A+v.array%28z.string%28%29%29%2C%5Cn++rating%3A+v.picklist%28%5B%5C%22G%5C%22%2C+%5C%22PG%5C%22%2C+%5C%22PG13%5C%22%2C+%5C%22R%5C%22%5D%29%2C%5Cn%7D%29%3B%5Cn%5Cnv.parse%28filmSchema%2C+null%29%22" rel="noopener noreferrer"&gt;Link&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://bundlejs.com/?q=arktype%402.0.4&amp;amp;treeshake=%5B*%5D&amp;amp;text=%22const+filmSchema+%3D+type%28%7B%5Cn++id%3A+%5C%22number%5C%22%2C%5Cn++title%3A+%5C%22string%5C%22%2C%5Cn++tags%3A+%5C%22string%5B%5D%5C%22%2C%5Cn++rating%3A+%60%5C%22G%5C%22+%7C+%5C%22PG%5C%22+%7C+%5C%22PG13%5C%22+%7C+%5C%22R%5C%22%60%2C%5Cn%7D%29%3B%5Cn%5CnfilmSchema%28null%29%22" rel="noopener noreferrer"&gt;Link&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;It's not as amazing as Valibot, but ReScript Schema is definitely doing good here. If we compare ReScript Schema to libraries that have similar performance, they all use the code generation approach (besides ArkType). This means it'll start small, but for every new type, more and more code will be added to your bundle, rapidly increasing the application size.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parsing using ReScript
&lt;/h3&gt;

&lt;p&gt;Even though I want to make ReScript Schema popular for TS developers, ReScript is still the library's main user base, so I'll also include examples of it.&lt;/p&gt;

&lt;p&gt;Compared to TypeScript, the type system in ReScript is much simpler; you literally can't do any type gymnastics in it. Together with &lt;a href="https://medium.com/@thejameskyle/type-systems-structural-vs-nominal-typing-explained-56511dd969f4" rel="noopener noreferrer"&gt;nominal typing&lt;/a&gt;, it's getting impossible to extract the &lt;code&gt;film&lt;/code&gt; type from the schema (even though it can infer it). But there's a built-in way to prevent boilerplate code in ReScript. You can use ReScript Schema PPX to generate schemas for your types automatically. Just annotate them with &lt;code&gt;@schema&lt;/code&gt; attribute.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rescript"&gt;&lt;code&gt;&lt;span class="nd"&gt;@schema&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;rating&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
 &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nd"&gt;@as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"G"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;GeneralAudiences&lt;/span&gt;
 &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nd"&gt;@as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"PG"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;ParentalGuidanceSuggested&lt;/span&gt;
 &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nd"&gt;@as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"PG13"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;ParentalStronglyCautioned&lt;/span&gt;
 &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nd"&gt;@as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"R"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;Restricted&lt;/span&gt;
&lt;span class="nd"&gt;@schema&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;film&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="n"&gt;id&lt;/span&gt;: &lt;span class="kt"&gt;float&lt;/span&gt;,
 &lt;span class="n"&gt;title&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;,
 &lt;span class="n"&gt;tags&lt;/span&gt;: &lt;span class="kt"&gt;array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;,
 &lt;span class="n"&gt;rating&lt;/span&gt;: &lt;span class="n"&gt;rating&lt;/span&gt;,
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Does the &lt;code&gt;rating&lt;/code&gt; type look scary to you? Don't worry, this is a ReScript &lt;a href="https://rescript-lang.org/docs/manual/v11.0.0/variant" rel="noopener noreferrer"&gt;Variant&lt;/a&gt;, which is such a nice way to describe any kind of union. Also, you can use &lt;code&gt;@as&lt;/code&gt; and give a better name to the ratings while preserving the original short values in runtime.&lt;/p&gt;

&lt;p&gt;Although PPX is nice, you can always code without it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rescript"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;rating&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
 &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nd"&gt;@as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"G"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;GeneralAudiences&lt;/span&gt;
 &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nd"&gt;@as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"PG"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;ParentalGuidanceSuggested&lt;/span&gt;
 &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nd"&gt;@as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"PG13"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;ParentalStronglyCautioned&lt;/span&gt;
 &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nd"&gt;@as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"R"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;Restricted&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;film&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="n"&gt;id&lt;/span&gt;: &lt;span class="kt"&gt;float&lt;/span&gt;,
 &lt;span class="n"&gt;title&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;,
 &lt;span class="n"&gt;tags&lt;/span&gt;: &lt;span class="kt"&gt;array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;,
 &lt;span class="n"&gt;rating&lt;/span&gt;: &lt;span class="n"&gt;rating&lt;/span&gt;,
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;filmSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&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="n"&gt;id&lt;/span&gt;: &lt;span class="n"&gt;s&lt;/span&gt;.&lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;,
  &lt;span class="n"&gt;title&lt;/span&gt;: &lt;span class="n"&gt;s&lt;/span&gt;.&lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;,
  &lt;span class="n"&gt;tags&lt;/span&gt;: &lt;span class="n"&gt;s&lt;/span&gt;.&lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;,
  &lt;span class="n"&gt;rating&lt;/span&gt;: &lt;span class="n"&gt;s&lt;/span&gt;.&lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;union&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="nc"&gt;GeneralAudiences&lt;/span&gt;,
    &lt;span class="nc"&gt;ParentalGuidanceSuggested&lt;/span&gt;,
    &lt;span class="nc"&gt;ParentalStronglyCautioned&lt;/span&gt;,
    &lt;span class="nc"&gt;Restricted&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 TS API admittedly wins here since we don't need to call &lt;code&gt;s.matches&lt;/code&gt; to make type system happy, but when it comes to parsing ReScript takes it back with the &lt;a href="https://rescript-lang.org/docs/manual/v11.0.0/pipe" rel="noopener noreferrer"&gt;Pipe Operator&lt;/a&gt; and &lt;a href="https://rescript-lang.org/docs/manual/v11.0.0/pattern-matching-destructuring#match-on-exceptions" rel="noopener noreferrer"&gt;Pattern Matching on exceptions&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rescript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"id"&lt;/span&gt;: &lt;span class="mi"&gt;1&lt;/span&gt;,
  &lt;span class="s2"&gt;"title"&lt;/span&gt;: &lt;span class="s2"&gt;"My first film"&lt;/span&gt;,
  &lt;span class="s2"&gt;"tags"&lt;/span&gt;: &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Loved"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;,
  &lt;span class="s2"&gt;"rating"&lt;/span&gt;: &lt;span class="s2"&gt;"S"&lt;/span&gt;,
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nn"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parseOrThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filmSchema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;//? Throws RescriptSchemaError with message `Failed parsing at ["rating"]. Reason: Expected "G" | "PG" | "PG13" | "R", received "S"`&lt;/span&gt;

&lt;span class="n"&gt;validDataWithUnknownType&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nn"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parseOrThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filmSchema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;//? Returns value of the film type&lt;/span&gt;

&lt;span class="c1"&gt;// If you don't want to throw, you can match on the S.Raised exception and return the result type. There's no S.safe API like in TypeScript, since you can do better with the language itself!&lt;/span&gt;
&lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nn"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parseOrThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filmSchema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;film&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;film&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;exception&lt;/span&gt; &lt;span class="nn"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Raised&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&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;
  
  
  Unique Features
&lt;/h2&gt;

&lt;p&gt;After we covered the most basic use case, let's move on to the things that make ReScript Schema special 🔥&lt;/p&gt;

&lt;h3&gt;
  
  
  Changing shape and field names
&lt;/h3&gt;

&lt;p&gt;Let's imagine working with a weird REST API with poorly named fields in PascalCase, where data is randomly nested in objects or tuples. But we can't change the backend, so at least we want to transform data to a more convenient format for our application. In ReScript Schema you can make it in a declarative way, which will result in the most possibly performant operation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filmSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;S&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="nx"&gt;s&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="na"&gt;id&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="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Id&lt;/span&gt;&lt;span class="dl"&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="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;title&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="nf"&gt;nested&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Meta&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Title&lt;/span&gt;&lt;span class="dl"&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="kr"&gt;string&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;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Tags_v2&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;array&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="kr"&gt;string&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;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Rating&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;schema&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="nf"&gt;union&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;G&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;PG&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;PG13&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;R&lt;/span&gt;&lt;span class="dl"&gt;"&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="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseOrThrow&lt;/span&gt;&lt;span class="p"&gt;(&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;My first film&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="na"&gt;Tags_v2&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="s2"&gt;Loved&lt;/span&gt;&lt;span class="dl"&gt;"&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="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;G&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;filmSchema&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;//? { id: 1, title: "My first film", tags: ["Loved"], rating: "G" }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks scary? Let's dive in. First of all, every schema has &lt;code&gt;Input&lt;/code&gt; and &lt;code&gt;Output&lt;/code&gt;. Quite often, they are equal, and during parsing, the library only validates that &lt;code&gt;Input&lt;/code&gt; has the correct type and returns it immediately. Although there are ways to change the expected &lt;code&gt;Output&lt;/code&gt; type like we do in the example above. For comparison, let's take a look at how you'd usually achieve the same with other schema libraries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filmSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transform&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="nf"&gt;schema&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;S&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="na"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;Title&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="kr"&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_v2&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="nf"&gt;array&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="kr"&gt;string&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;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schema&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="nf"&gt;union&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;G&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;PG&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;PG13&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;R&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="nx"&gt;input&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="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Title&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;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Tags_v2&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;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Rating&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;This is still ReScript Schema, but we use &lt;code&gt;S.transform&lt;/code&gt; to manually transform the &lt;code&gt;Input&lt;/code&gt; type. You can find this kind of API in many other schema libraries. What's good about the example is that you can clearly see that we use our schema to declaratively describe what the data incoming to our system looks like, and then we transform it to what's convenient for us to work with. In a way, the schema here is similar to a contract between the client and the server that returns the object in response.&lt;/p&gt;

&lt;p&gt;In the advanced &lt;code&gt;S.object&lt;/code&gt; example, which I showed first, we combine a declarative description of the &lt;code&gt;Input&lt;/code&gt; type with a transformation to the &lt;code&gt;Output&lt;/code&gt; type. And this enables one more thing besides shorter code and a performance boost.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reverse Parsing (aka serializing/decoding)
&lt;/h3&gt;

&lt;p&gt;Decoding is present in many libraries from other languages, but it's not very common in the JS ecosystem. This is a big loss because the ability to perform operations in the reverse direction is the most powerful feature I personally find.&lt;/p&gt;

&lt;p&gt;If it's unclear what I mean, in other popular JavaScript schema libraries, you can only parse &lt;code&gt;Input&lt;/code&gt; to &lt;code&gt;Output&lt;/code&gt; types. While in ReScript Schema you can easily parse &lt;code&gt;Output&lt;/code&gt; to &lt;code&gt;Input&lt;/code&gt; using the same schema. Or only perform the conversion logic since the &lt;code&gt;Output&lt;/code&gt; type usually doesn't require validation.&lt;/p&gt;

&lt;p&gt;Do you remember our &lt;code&gt;filmSchema&lt;/code&gt; using &lt;code&gt;S.object&lt;/code&gt; to rename fields? Let's say we want to send a POST request with the film entity, and the server also expects the weirdly cased data structure it initially sent to us. Here is how we deal with it:&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;// The same schema from above&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filmSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;S&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="nx"&gt;s&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="na"&gt;id&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="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Id&lt;/span&gt;&lt;span class="dl"&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="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
 &lt;span class="na"&gt;title&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="nf"&gt;nested&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Meta&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Title&lt;/span&gt;&lt;span class="dl"&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="kr"&gt;string&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;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Tags_v2&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;array&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="kr"&gt;string&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;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Rating&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;schema&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="nf"&gt;union&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;G&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;PG&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;PG13&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;R&lt;/span&gt;&lt;span class="dl"&gt;"&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="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reverseConvertOrThrow&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;My first film&lt;/span&gt;&lt;span class="dl"&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="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Loved&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;G&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;filmSchema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;//? { Id: 1, Meta: { Title: "My first film" }, Tags_v2: ["Loved"], Rating: ["G"] }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sweet! Isn't it? And even though I want to talk more about performance a little bit later, I can't stop myself from sharing the code it evaluates under the hood:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&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="o"&gt;=&amp;gt;&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;v0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tags&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Id&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Title&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&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="na"&gt;Tags_v2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v0&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="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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rating&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I think most people would write slower code by hand 😅&lt;/p&gt;

&lt;h3&gt;
  
  
  Reverse
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;S.reverseConvertOrThrow&lt;/code&gt; is one of the reverse cases I use daily in my work, but this is actually just a shorthand of &lt;code&gt;S.convertOrThrow&lt;/code&gt; and &lt;code&gt;S.reverse&lt;/code&gt; you can use separately.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;S.reverse&lt;/code&gt; - this is what allows you to take your &lt;code&gt;Schema&amp;lt;Input, Output&amp;gt;&lt;/code&gt; and turn it into &lt;code&gt;Schema&amp;lt;Output, Input&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It may sound quite dull, but compared to the commonly used parser/serializer or encoder/decoder approach, here you get an actual schema you can use the same way as the original one without any limitations.&lt;/p&gt;

&lt;p&gt;If you want, you can parse output with/without data validation, generate JSON Schema, perform optimized comparison and hashing, or use the data representation in runtime for any custom logic.&lt;/p&gt;

&lt;p&gt;As a fruit of the ability to know &lt;code&gt;Input&lt;/code&gt; and &lt;code&gt;Output&lt;/code&gt; data types in runtime, ReScript Schema has a very powerful coercion API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;coerce&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="kr"&gt;string&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="nx"&gt;bigint&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="nf"&gt;parseOrThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;//? 123n&lt;/span&gt;
&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reverseConvertOrThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;//? "123"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pass any schemas to &lt;code&gt;S.coerce&lt;/code&gt; that you want to coerce from and to, and ReScript Schema will figure out the rest.&lt;/p&gt;

&lt;p&gt;And this has not been implemented yet, but with the API, it'll also be possible to achieve 2x faster JSON.stringify(). Like &lt;a href="https://github.com/fastify/fast-json-stringify" rel="noopener noreferrer"&gt;fast-json-stringify&lt;/a&gt; does and maybe even faster 😎&lt;/p&gt;

&lt;h3&gt;
  
  
  100 Operations
&lt;/h3&gt;

&lt;p&gt;If you want the best possible performance or the built-in operations don't cover your specific use case, you can use &lt;code&gt;S.compile&lt;/code&gt; to create fine-tuned operation functions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;operation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Any&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;Assert&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;Async&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;//? (input: unknown) =&amp;gt; Promise&amp;lt;void&amp;gt;&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello world!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the example above, we've created an async assert operation, which is not available by default.&lt;/p&gt;

&lt;p&gt;With the API, you can get 100 different operation combinations, each of which might make sense for your specific use case. This is like &lt;a href="https://valibot.dev/api/parser/" rel="noopener noreferrer"&gt;parser&lt;/a&gt; in Valibot, but multiplied by 💯.&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance Comparison
&lt;/h3&gt;

&lt;p&gt;As I mentioned in the beginning, ReScript Schema is the fastest. Now I'll explain why 🔥&lt;/p&gt;

&lt;p&gt;Also, you can use the big community &lt;a href="https://moltar.github.io/typescript-runtime-type-benchmarks/" rel="noopener noreferrer"&gt;benchmark&lt;/a&gt; to confirm yourself. If you see &lt;a href="https://typia.io/" rel="noopener noreferrer"&gt;Typia&lt;/a&gt; overperforming ReScript Schema, I have a take on it too 😁&lt;/p&gt;

&lt;p&gt;First of all, the biggest advantage of ReScript Schema is its very clever library core, which builds the most possibly optimized operations using &lt;code&gt;eval&lt;/code&gt;. I have already shown before how the operation code looks for reverse conversion; here's the &lt;code&gt;filmSchema&lt;/code&gt; parse operation code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&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="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="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;7&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="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;v0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="nx"&gt;v1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Meta&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="nx"&gt;v3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Tags_v2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="nx"&gt;v7&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Rating&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;v0&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;e&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="nx"&gt;v0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;v1&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="nx"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;);&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;v2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;v2&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;e&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="nx"&gt;v2&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="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="nx"&gt;v3&lt;/span&gt;&lt;span class="p"&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;v4&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;v4&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;v3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;v4&lt;/span&gt;&lt;span class="p"&gt;)&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;v6&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;v3&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;v4&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;v6&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="nx"&gt;v6&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v5&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;v5&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;v5&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;===&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;v5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;["Tags_v2"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&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="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;v4&lt;/span&gt; &lt;span class="o"&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="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;v5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;v5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;v7&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;e&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="nx"&gt;v7&lt;/span&gt;&lt;span class="p"&gt;);&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;v8&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;v7&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&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;v8&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;G&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v8&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PG&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v8&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PG13&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v8&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;R&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;e&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="nx"&gt;v8&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="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v2&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;v3&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;v8&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;Thanks to &lt;code&gt;eval&lt;/code&gt;, we can eliminate function calls and inline all type validations using &lt;code&gt;if&lt;/code&gt; statements. Also, knowing about the &lt;code&gt;Output&lt;/code&gt; type at runtime allows us to perform transformations with zero wasteful object allocations, optimizing the operation for JavaScript engines.&lt;/p&gt;

&lt;p&gt;Interestingly, you probably think that calling &lt;code&gt;eval&lt;/code&gt; itself is slow, and I thought this myself. However, it was actually not as slow as I expected. For example, creating a simple nested object schema and calling the parser once happened to be 1.8 times faster with ReScript Schema using eval than Zod. I really put a lot of effort into making it as fast as possible, and I have to thank the &lt;a href="https://rescript-lang.org/" rel="noopener noreferrer"&gt;ReScript language&lt;/a&gt; and the people behind it for allowing me to write very performant and safe code. &lt;/p&gt;

&lt;p&gt;Talking about &lt;a href="https://arktype.io/" rel="noopener noreferrer"&gt;ArkType&lt;/a&gt;, they use the same approach with eval and have similar potential to ReScript Schema, but their evaluated code is not there yet. Currently, their operations are a little bit slower, and the schema creation is significantly slower. But I can see that it can somewhat catch up in the future.&lt;/p&gt;

&lt;p&gt;What other libraries will never be able to catch up on is the ability to reshape schema declaratively. And this is why I say that ReScript Schema is faster than Typia. Also, Typia doesn't always generate the most optimized code, e.g., for optional fields. And it doesn't come with many built-in operations specifically optimized for the desired use case. Still, this is an excellent library with Fast JSON Serialization and Protocol Buffer Encoding features, which I'm still yet to implement.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ecosystem
&lt;/h3&gt;

&lt;p&gt;When choosing a schema library for your project, where performance is not a concern, the ecosystem is the most important factor to consider. With a schema, you can do millions of things by knowing the type of representation in runtime. Such as JSON Schema generation, describing database schemas, optimized comparison and hashing, encoding to proto buff, building forms, mocking data, communicating with AI, and much more.&lt;/p&gt;

&lt;p&gt;Zod is definitely a winner here. I counted 78 libraries integrating with Zod at the moment of writing the article. There are even some where you provide a Zod schema, and it renders a Vue page with a form prompting for the data. This is just too convenient for not using it for prototyping.&lt;/p&gt;

&lt;p&gt;But if you don't need something super specific, ReScript Schema has a decent ecosystem itself, which is comparable to Valibot and ArkType. Actually, it has an even higher potential thanks to the ability to adjust Shape and automatically Reverse the schema. A good example of this is &lt;a href="https://github.com/DZakh/rescript-rest" rel="noopener noreferrer"&gt;ReScript Rest&lt;/a&gt;, which combines the DX of &lt;a href="https://trpc.io/" rel="noopener noreferrer"&gt;tRPC&lt;/a&gt; while staying unopinionated like &lt;a href="https://ts-rest.com/" rel="noopener noreferrer"&gt;ts-rest&lt;/a&gt;. I also built many powerful tools around ReScript Schema, but I have to admit that I haven't added TS support yet. Let me know if you find something interesting to use, and I'll do this asap 😁&lt;/p&gt;

&lt;p&gt;Also, ReScript Schema supports &lt;a href="https://standardschema.dev/" rel="noopener noreferrer"&gt;Standard Schema&lt;/a&gt;, a common interface for TypeScript validation libraries. It was recently designed by the creators of Zod, Valibot, and ArkType and has already been integrated into many popular libraries. This means that you can use ReScript Schema with &lt;a href="https://trpc.io/" rel="noopener noreferrer"&gt;tRPC&lt;/a&gt;, &lt;a href="https://tanstack.com/form" rel="noopener noreferrer"&gt;TanStack Form&lt;/a&gt;, &lt;a href="https://tanstack.com/router" rel="noopener noreferrer"&gt;TanStack Router&lt;/a&gt;, &lt;a href="https://hono.dev/" rel="noopener noreferrer"&gt;Hono&lt;/a&gt;, and 19+ more at the time of writing the article.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;As the title says, I wholeheartedly believe that ReScript Schema is the future of schema libraries. It offers both DX, performance, bundle size, and many innovative features. I tried to cover all of them at a high level, and I hope I managed to make you at least a little bit interested 👌&lt;/p&gt;

&lt;p&gt;I don't persuade you to choose ReScript Schema for your next project, and I actually still recommend Zod when somebody asks me. But I'll definitely appreciate a &lt;a href="https://github.com/DZakh/rescript-schema" rel="noopener noreferrer"&gt;star&lt;/a&gt; and &lt;a href="https://x.com/dzakh_dev" rel="noopener noreferrer"&gt;X follow&lt;/a&gt; 🙏&lt;/p&gt;

&lt;p&gt;Let's see how the future of schema libraries will turn out. Maybe I'll rename ReScript Schema to something dope and become more popular than Zod? Cheers 😁&lt;/p&gt;

</description>
      <category>schema</category>
      <category>typescript</category>
      <category>rescript</category>
      <category>opensource</category>
    </item>
    <item>
      <title>ReScript Schema V9 - Zod-like library to the next level 🚀</title>
      <dc:creator>Dmitry Zakharov</dc:creator>
      <pubDate>Thu, 09 Jan 2025 17:37:00 +0000</pubDate>
      <link>https://dev.to/dzakh/rescript-schema-v9-zod-like-library-to-the-next-level-1dn6</link>
      <guid>https://dev.to/dzakh/rescript-schema-v9-zod-like-library-to-the-next-level-1dn6</guid>
      <description>&lt;p&gt;The article is an overview of changes introduced in the recently released version 9. After more than 2 years of development, the ReScript Schema library became a project I'm genuinely proud of, so I plan to start creating more content and let more people know about it. A changelog is probably not a good piece of content to start with since it requires context for it to make sense, but I hope it'll still make some of you interested. If you've never heard about &lt;a href="https://github.com/DZakh/rescript-schema" rel="noopener noreferrer"&gt;ReScript Schema&lt;/a&gt;, here is a short highlight:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is the fastest parsing and validation library in the entire JavaScript ecosystem&lt;/li&gt;
&lt;li&gt;Works with plain JavaScript, TypeScript, and ReScript. You don't need to use any compiler&lt;/li&gt;
&lt;li&gt;People actually use it, and there's a decent ecosystem around it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If I made you slightly interested, you can find more on the &lt;a href="https://github.com/DZakh/rescript-schema" rel="noopener noreferrer"&gt;GitHub page&lt;/a&gt;. Don't forget to subscribe to get notifications about more content about tools I create.&lt;/p&gt;

&lt;p&gt;In the meantime, let's start the V9 changes overview 🔥&lt;/p&gt;

&lt;h1&gt;
  
  
  What's new in ReScript Schema V9?
&lt;/h1&gt;

&lt;p&gt;ReScript Schema V9 is a big step for the library. Even though there are not many changes in the ReScript API, the version comes with a completely new mental model, an internal redesign for enhanced operations flexibility, and a drastic improvement for the JS/TS API.&lt;/p&gt;

&lt;h2&gt;
  
  
  New mental model 🧬
&lt;/h2&gt;

&lt;p&gt;The main idea of ReScript Schema-like libraries is to create a schema with code that describes a desired data structure. Then, you can use the schema to parse data incoming to your application, derive types, create JSON-Schema, and many more. This is how other libraries, like Zod, Valibot, Superstruct, yup, etc, work.&lt;/p&gt;

&lt;p&gt;Even though ReScript Schema is now a mature and powerful library for JS users, I originally created it specifically to parse data in ReScript applications, with the core functionality to automatically transform data to the ReScript representation and back to the original format. This is how the advanced object API was born - the primary tool of ReScript Schema up till V9.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rescript"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;filmSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&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="n"&gt;id&lt;/span&gt;: &lt;span class="n"&gt;s&lt;/span&gt;.&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Id"&lt;/span&gt;, &lt;span class="nn"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;,
  &lt;span class="n"&gt;title&lt;/span&gt;: &lt;span class="n"&gt;s&lt;/span&gt;.&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Title"&lt;/span&gt;, &lt;span class="nn"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;,
  &lt;span class="n"&gt;tags&lt;/span&gt;: &lt;span class="n"&gt;s&lt;/span&gt;.&lt;span class="n"&gt;fieldOr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Tags"&lt;/span&gt;, &lt;span class="nn"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;, &lt;span class="p"&gt;[])&lt;/span&gt;,
  &lt;span class="n"&gt;rating&lt;/span&gt;: &lt;span class="n"&gt;s&lt;/span&gt;.&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;"Rating"&lt;/span&gt;,
    &lt;span class="nn"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;union&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
      &lt;span class="nn"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;literal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GeneralAudiences&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;,
      &lt;span class="nn"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;literal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ParentalGuidanceSuggested&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;,
      &lt;span class="nn"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;literal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ParentalStronglyCautioned&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;,
      &lt;span class="nn"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;literal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Restricted&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="n"&gt;deprecatedAgeRestriction&lt;/span&gt;: &lt;span class="n"&gt;s&lt;/span&gt;.&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Age"&lt;/span&gt;, &lt;span class="nn"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nn"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deprecate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Use rating instead"&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, you can see features like declarative validation, fields renaming, and automatic transformation/mapping in a single schema definition. And the schema will produce the most optimized function to parse or convert your value back to the original format using &lt;code&gt;eval&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's changed and why?
&lt;/h3&gt;

&lt;p&gt;As I said before, initially, the library covered two prominent use cases: parsing &amp;amp; serializing. But with time, when ReScript Schema got used more extensively, it wasn't enough anymore. For example, you'd want only to apply transformations without validation, validate the data before serializing, assert with the most performance without allocations for output value, or parse JSON data, transform it, and convert the result to JSON again. And V9 introduces the &lt;code&gt;S.compile&lt;/code&gt; function to create custom operations with 100 different combinations 💪&lt;/p&gt;

&lt;p&gt;To make things even more flexible, all operations became one-directional. This might be slightly confusing because it means there's no serializing anymore. But V9 comes with a new, powerful concept of reversing your schema. Since we have 50 different one-directional operations, we now have 50 more for the opposite direction. So I hope it's a good explanation of why &lt;code&gt;serializeOrRaiseWith&lt;/code&gt; was removed.&lt;/p&gt;

&lt;p&gt;To keep the DX on the same level, besides &lt;code&gt;S.compile&lt;/code&gt; + &lt;code&gt;S.reverse&lt;/code&gt; functions, there are some built-in operations like &lt;code&gt;reverseConvertOrThrow&lt;/code&gt;, which is equivalent to the removed &lt;code&gt;serializeOrRaiseWith&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Another hidden power of the reverse feature is that now it's possible to get the type information of the schema output, allowing one to compile even more optimized schemas and build even more advanced tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  TS to the moon 🚀
&lt;/h2&gt;

&lt;p&gt;For me, the custom compilation and reverse mental model are the biggest changes, but if you're a TypeScript user, you'll find another one to be more exciting in the v9 release. And yes, I know there are pretty many breaking changes, but creating a schema was never as smooth.&lt;/p&gt;

&lt;p&gt;Before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shapeSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;union&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="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;kind&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="nf"&gt;literal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;circle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;radius&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="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="nx"&gt;S&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;kind&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="nf"&gt;literal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;triangle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;x&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="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;y&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="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="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shapeSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;union&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;circle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;radius&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="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="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;triangle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Or you can have S.schema("triangle")&lt;/span&gt;
    &lt;span class="na"&gt;x&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="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;y&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="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="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In V9 you don't need to care whether you work with an object, tuple, or a literal - simply use &lt;code&gt;S.schema&lt;/code&gt; for all cases. If you have an enum, discriminated union, or some variadic union - simply use &lt;code&gt;S.union&lt;/code&gt;. There's a single API, and ReScript Schema will automatically compile the most optimized operation for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clean up built-in operations 🧹
&lt;/h2&gt;

&lt;p&gt;If you're familiar with ReScript Schema's older versions, the built-in operations look somehow different. For example, &lt;code&gt;serializeOrRaiseWith&lt;/code&gt; became &lt;code&gt;reverseConvertOrThrow&lt;/code&gt;. So there are changes you should know about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All operations are now throwing; there's no result-based API. As an alternative for ReScript I recommend using try/catch with the S.Raised exception. For TS users, there's a new S.safe API for convenient error handling.&lt;/li&gt;
&lt;li&gt;TS and ReScript API are now the same, which is easier to maintain, interop between languages and work in mixed codebases.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Raise&lt;/code&gt; suffix is renamed to &lt;code&gt;Throw&lt;/code&gt; since this makes more sense in Js ecosystems, and there are plans to rename the &lt;code&gt;raise&lt;/code&gt; function to &lt;code&gt;throw&lt;/code&gt; in the next ReScript versions.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;With&lt;/code&gt; suffix is removed to save some space. Note that the operation is still data-first, and the schema should be a second argument.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Other changes you should know about 👀
&lt;/h2&gt;

&lt;p&gt;In the release, &lt;code&gt;S.schema&lt;/code&gt; got a noticeable boost. Besides becoming TS users' main API now, I recommend using it in ReScript by default when you don't need transformations. Previously, it was simply a wrapper over advanced &lt;code&gt;S.object&lt;/code&gt; and &lt;code&gt;S.tuple&lt;/code&gt;, but it got its own implementation, which allows creating schemas faster, as well as including optimizations for compiled operations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you use &lt;code&gt;S.schema&lt;/code&gt; for strict objects without transformed fields, the incoming object won't be recreated.&lt;/li&gt;
&lt;li&gt;If you use &lt;code&gt;S.schema&lt;/code&gt; for tuples without transformed items, the incoming array won't be recreated.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, you can pass the object schema created with &lt;code&gt;S.schema&lt;/code&gt; to the new Advanced Object API for nested fields coming in as a replacement for &lt;code&gt;s.nestedField&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rescript"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;entityDataSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&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="n"&gt;name&lt;/span&gt;: &lt;span class="n"&gt;s&lt;/span&gt;.&lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;,
 &lt;span class="n"&gt;age&lt;/span&gt;: &lt;span class="n"&gt;s&lt;/span&gt;.&lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;,
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;entitySchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&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="c1"&gt;// Schema created with S.object wouldn't work here 👌&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;, &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;.&lt;span class="n"&gt;nested&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;.&lt;span class="n"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entityDataSchema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="n"&gt;id&lt;/span&gt;: &lt;span class="n"&gt;s&lt;/span&gt;.&lt;span class="n"&gt;nested&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;.&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;, &lt;span class="nn"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;,
 &lt;span class="n"&gt;name&lt;/span&gt;,
 &lt;span class="n"&gt;age&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;Besides the runtime improvements for operations of &lt;code&gt;S.schema&lt;/code&gt;, there are a few more, which were possible thanks to the ability to know the output type information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The union of objects will use literal fields to choose which schema it should use for parsing. Previously, it used to run operations for every schema of the union and return the result of the first non-failing one. The approach was slow, provided worse error messages, and didn't work for reversed operations. In V9 all the problems are solved.&lt;/li&gt;
&lt;li&gt;Improved optional operations by doing the &lt;code&gt;Caml_option.valFromOption&lt;/code&gt; call only when it's truly needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a cherry on top of the cake, schema names became more readable, making error messages even better:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;-Failed parsing at root. Reason: Expected Object({"foo":Option(String)}), received 123
&lt;/span&gt;&lt;span class="gi"&gt;+Failed parsing at root. Reason: Expected { foo: string | undefined; }, received 123
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thanks for reading the high-level overview of the release. Contact me on &lt;a href="https://github.com/DZakh/rescript-schema/issues" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; or &lt;a href="https://x.com/dzakh_dev" rel="noopener noreferrer"&gt;X&lt;/a&gt; if you have any questions 😁 And enjoy using ReScript Schema 🚀&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>zod</category>
      <category>opensource</category>
      <category>rescript</category>
    </item>
    <item>
      <title>The ultimate answer to Belt vs Js in ReScript</title>
      <dc:creator>Dmitry Zakharov</dc:creator>
      <pubDate>Sun, 29 Jan 2023 21:50:18 +0000</pubDate>
      <link>https://dev.to/dzakh/the-ultimate-answer-to-belt-vs-js-in-rescript-4dlp</link>
      <guid>https://dev.to/dzakh/the-ultimate-answer-to-belt-vs-js-in-rescript-4dlp</guid>
      <description>&lt;p&gt;Probably every developer that comes to &lt;a href="https://rescript-lang.org" rel="noopener noreferrer"&gt;ReScript&lt;/a&gt; stumbles upon a dilemma about which module from the standard library they should use to work with JavaScript API.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Should I take the &lt;a href="https://rescript-lang.org/docs/manual/latest/api/js" rel="noopener noreferrer"&gt;Js&lt;/a&gt; module with a familiar design for JavaScript developers and runtime-free bindings? 🧐&lt;/li&gt;
&lt;li&gt;Or should I take the more powerful &lt;a href="https://rescript-lang.org/docs/manual/latest/api/belt" rel="noopener noreferrer"&gt;Belt&lt;/a&gt;? 🤔&lt;/li&gt;
&lt;li&gt;Wait, I heard something about &lt;a href="https://github.com/bloodyowl/rescript-js" rel="noopener noreferrer"&gt;rescript-js&lt;/a&gt;! Maybe that’s what I need? 😅&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are pretty familiar thoughts, right? I’ve seen a lot of discussions about which one you should use, but there were never solid answers, and the result was either “it depends” or “I personally like one over another because of X”.&lt;/p&gt;

&lt;p&gt;My lovely &lt;a href="https://www.carla.se/" rel="noopener noreferrer"&gt;Carla&lt;/a&gt; colleague &lt;a href="https://twitter.com/daggala" rel="noopener noreferrer"&gt;Daggala&lt;/a&gt; has written a very detailed &lt;a href="https://www.daggala.com/belt_vs_js_array_in_rescript/" rel="noopener noreferrer"&gt;article&lt;/a&gt; about the problem, so I won’t repeat her and continue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem 2: Multiple sources of truth
&lt;/h2&gt;

&lt;p&gt;No matter what we choose, sometimes we still need to use another module. It’s because some functions which exist in &lt;code&gt;Js&lt;/code&gt; don’t exist in &lt;code&gt;Belt&lt;/code&gt; and vise-versa.&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%2Fth8acu2bs0hjwghvnfq4.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%2Fth8acu2bs0hjwghvnfq4.png" alt="Multiple sources of truth" width="800" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But it might happen that a function doesn’t exist in both of the modules, eg infamous &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart" rel="noopener noreferrer"&gt;padStart&lt;/a&gt;. At &lt;a href="https://www.carla.se/" rel="noopener noreferrer"&gt;Carla&lt;/a&gt;, we used to solve the problem by creating a lot of &lt;code&gt;StringExtra&lt;/code&gt;, &lt;code&gt;OptionExtra&lt;/code&gt;, and &lt;code&gt;WhateverExtra&lt;/code&gt; modules to add missing helper functions.&lt;/p&gt;

&lt;p&gt;This leads to another problem when developers don’t know where they can find the desired helper:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Will it be in &lt;code&gt;Belt&lt;/code&gt; that we agreed to use as default, or the helper doesn’t exist there, and I should use &lt;code&gt;Js&lt;/code&gt; instead, or maybe &lt;code&gt;StringExtra&lt;/code&gt;?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A few times, we even end up using &lt;code&gt;Pervasives&lt;/code&gt; by mistake.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem 3: Creating guidelines
&lt;/h2&gt;

&lt;p&gt;Depending on the project, we may have rules for the team to follow. For example, &lt;code&gt;Belt.Option.getExn&lt;/code&gt; raises a ReScript exception which is very difficult to trace, so at &lt;a href="https://www.carla.se/" rel="noopener noreferrer"&gt;Carla&lt;/a&gt; we decided to use our own &lt;code&gt;OptionExtra.getExnWithMessage&lt;/code&gt; instead. Although there's an agreement, it's very easy to forget about and continue using &lt;code&gt;Belt.Option.getExn&lt;/code&gt;. We want a way to prevent the usage of &lt;code&gt;Belt.Option.getExn&lt;/code&gt; whatsoever.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;I will not create intrigue and say that the solution I suggest is to create your own vendored standard library and enforce its usage across the codebase. You can reuse existing modules like &lt;code&gt;Js&lt;/code&gt;, &lt;code&gt;Belt&lt;/code&gt;, or &lt;code&gt;RescriptJs&lt;/code&gt;, and adjust them for our needs.&lt;/p&gt;

&lt;p&gt;The enforcing is the most crucial part here because if we don't automate it with CI, our colleagues and even ourselves will continue using all different modules instead of the single vendored one.&lt;/p&gt;

&lt;p&gt;And to solve the problem, I've created &lt;a href="https://github.com/DZakh/rescript-stdlib-vendorer" rel="noopener noreferrer"&gt;rescript-stdlib-vendorer&lt;/a&gt;, an easy-to-use linter to support the usage of a vendored standard library.&lt;/p&gt;

&lt;h3&gt;
  
  
  Linter
&lt;/h3&gt;

&lt;p&gt;The linter does a straightforward thing - It checks all project files and detects the usage of  &lt;code&gt;Js&lt;/code&gt;, &lt;code&gt;Belt&lt;/code&gt;, and &lt;code&gt;ReScriptJs&lt;/code&gt; modules. So you can easily find and replace them with your &lt;code&gt;Stdlib&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To start using the linter in your project, install it as a dev dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; rescript-stdlib-vendorer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's also add an npm run script for convenience and immediately use it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm pkg &lt;span class="nb"&gt;set &lt;/span&gt;scripts.lint:stdlib&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"rescript-stdlib-vendorer lint"&lt;/span&gt;
npm run lint:stdlib
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So if we have a project created from the &lt;a href="https://github.com/rescript-lang/rescript-project-template" rel="noopener noreferrer"&gt;official template repository&lt;/a&gt;, which has a single ReScript file with &lt;code&gt;Js.log("Hello, World!")&lt;/code&gt;, we’ll get the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/rescript-project-template/src/Demo.res:1
  Found "Js" module usage.

Use the vendored standard library instead. Read more at: https://github.com/DZakh/rescript-stdlib-vendorer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To fix it, let’s create a directory &lt;code&gt;stdlib&lt;/code&gt; and add the first vendored module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;src
&lt;span class="nb"&gt;mkdir &lt;/span&gt;stdlib
&lt;span class="nb"&gt;cd &lt;/span&gt;stdlib
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'include Js.Console'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; Console.res
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now update the &lt;code&gt;Demo.res&lt;/code&gt; by replacing &lt;code&gt;Js.log&lt;/code&gt; with &lt;code&gt;Console.log&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let’s run the linter again and see that there’s no error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run lint:stdlib
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the following way, you can create reexports for other modules and adjust them to make them better suit you.&lt;/p&gt;

&lt;p&gt;For example, for the &lt;code&gt;Array&lt;/code&gt; module, you can redefine some functions with &lt;code&gt;Belt&lt;/code&gt;’s implementation to make the code safer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/stdlib/Array.res&lt;/span&gt;
&lt;span class="k"&gt;include&lt;/span&gt; &lt;span class="nn"&gt;Js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Array&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Belt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way, the usage of square brackets to get an array item will return &lt;code&gt;option&lt;/code&gt;, which’s more correct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rescript"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;, &lt;span class="mi"&gt;2&lt;/span&gt;, &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;// The item will have the option&amp;lt;int&amp;gt; type instead of the default int one&lt;/span&gt;
&lt;span class="nn"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Returning to the &lt;code&gt;Belt.Option.getExn&lt;/code&gt;, mentioned in the third problem, instead of reexporting all functions from &lt;code&gt;Js&lt;/code&gt;/&lt;code&gt;Belt&lt;/code&gt;, we can explicitly reexport only the functions we need and replace the ones we consider harmful:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/stdlib/Option.res&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;forEach&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Belt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;forEach&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;mapWithDefault&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Belt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapWithDefault&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Belt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;flatMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Belt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flatMap&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;isSome&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Belt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isSome&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;isNone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Belt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isNone&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;getExnWithMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;, &lt;span class="n"&gt;message&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;switch&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Exn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;raiseError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&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;
  
  
  Reusing the vendored stdlib
&lt;/h2&gt;

&lt;p&gt;I’ve shown how to start vendoring stdlib in a short way. But having multiple projects following the same guidelines, you’d soon want to start reusing the vendored stdlib. It’s very easy to do by moving the code from the &lt;code&gt;stdlib&lt;/code&gt; directory featured above to a separate package.&lt;/p&gt;

&lt;p&gt;For my personal projects, I copied &lt;a href="https://twitter.com/___zth___" rel="noopener noreferrer"&gt;Gabriel&lt;/a&gt;'s repository with a proposal for a new ReScript stdlib, which did not work out. Afterward, I modified it to suit my taste better and published it to npm, making it easy to use.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I recommend taking a look at it as a reference &lt;a href="https://github.com/DZakh/rescript-stdlib" rel="noopener noreferrer"&gt;@dzakh/rescript-stdlib&lt;/a&gt;, but I don’t bring it to your own projects. You'll lose a very good part of vendoring - full control over the code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At &lt;a href="https://www.carla.se/" rel="noopener noreferrer"&gt;Carla&lt;/a&gt;, we had a different situation. Having a huge codebase with &lt;code&gt;Js&lt;/code&gt;, &lt;code&gt;Belt&lt;/code&gt;, and &lt;code&gt;WhateverExtra&lt;/code&gt; all over the place, it would be too much work to take some existing customized stdlibs like &lt;a href="https://github.com/bloodyowl/rescript-js" rel="noopener noreferrer"&gt;rescript-js&lt;/a&gt; or &lt;a href="https://github.com/DZakh/rescript-stdlib" rel="noopener noreferrer"&gt;@dzakh/rescript-stdlib&lt;/a&gt; and update old code with them.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;First of all, since at &lt;a href="https://www.carla.se/" rel="noopener noreferrer"&gt;Carla&lt;/a&gt; we have different guidelines compared to my personal projects, it would be a bad idea to use &lt;a href="https://github.com/DZakh/rescript-stdlib" rel="noopener noreferrer"&gt;@dzakh/rescript-stdlib&lt;/a&gt; from the get-go. As I said before, it'd lose the whole point of vendoring.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, to migrate the whole codebase, we’ve started with creating a small stdlib package in our pnpm mono repository that simply reexported functions from &lt;code&gt;Belt&lt;/code&gt; or &lt;code&gt;Js&lt;/code&gt;. And started updating file by file, replacing &lt;code&gt;open Belt&lt;/code&gt; with &lt;code&gt;open Stdlib&lt;/code&gt; and &lt;code&gt;Js.Array2&lt;/code&gt; with &lt;code&gt;Array&lt;/code&gt;, etc.&lt;/p&gt;

&lt;p&gt;Also, you can use a tool like &lt;a href="https://comby.dev/" rel="noopener noreferrer"&gt;Comby&lt;/a&gt; to transform the whole codebase in one go.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Before you start the migration to the vendored stdlib, I highly recommend replacing &lt;code&gt;Js.String&lt;/code&gt; and &lt;code&gt;Pervasives.String&lt;/code&gt; with &lt;code&gt;Js.String2&lt;/code&gt;. Since some of the functions have the same API, but different logic, there’s a chance of missing one of them and getting a runtime error.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But we didn’t rush with the migration, and to avoid regressions of the process, we ran the linter script in CI with the &lt;code&gt;--ignore-without-stdlib-open&lt;/code&gt; flag to skip files not containing &lt;code&gt;open Stdlib&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After every file was updated, we removed &lt;code&gt;open Stdlib&lt;/code&gt; from the beginning of the files and opened it globally via &lt;code&gt;bsconfig.json&lt;/code&gt;. When it was done, we could finally start gradually adjusting the &lt;code&gt;Stdlib&lt;/code&gt; to make the process of writing ReScript code more convenient and reliable.&lt;/p&gt;

&lt;p&gt;If you have any questions feel free to write them in the comment section or ping me on &lt;a href="https://twitter.com/dzakh_dev" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>rescript</category>
      <category>stdlib</category>
      <category>lint</category>
      <category>vendors</category>
    </item>
    <item>
      <title>Testing in ReScript</title>
      <dc:creator>Dmitry Zakharov</dc:creator>
      <pubDate>Mon, 02 Jan 2023 11:25:02 +0000</pubDate>
      <link>https://dev.to/dzakh/testing-in-rescript-5e7l</link>
      <guid>https://dev.to/dzakh/testing-in-rescript-5e7l</guid>
      <description>&lt;p&gt;Hello, I’m Dmitry Zakharov, a Frontend-engineer at &lt;a href="https://www.carla.se" rel="noopener noreferrer"&gt;Carla&lt;/a&gt; and author of &lt;a href="https://github.com/DZakh/rescript-struct" rel="noopener noreferrer"&gt;rescript-struct&lt;/a&gt;. After using ReScript for two years, of which 9 months are in production, I’ve gotten some insights about testing I wanted to share. I use ReScript not only for FE but for different areas, so if you’re a BE engineer or create a CLI tool, this article will also be useful for you.&lt;/p&gt;

&lt;p&gt;Testing in ReScript is not much different from testing JavaScript. That’s because we don’t test ReScript directly, but the code it generates. But the difference is that thanks to the great type-safety that ReScript provides, it’s almost unnecessary to test the connection between modules inside of your system. My experience with ReScript has shown that having unit tests for domain/business logic and a few end-to-end (e2e) tests to check that the whole system works correctly when modules are combined - is more than enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  Unit tests
&lt;/h2&gt;

&lt;p&gt;Regarding unit testing, the library you use doesn't really matter. Choose any that works for you. My personal preference is to use bindings for &lt;a href="https://github.com/DZakh/rescript-ava" rel="noopener noreferrer"&gt;AVA&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Another popular option is to use bindings for &lt;a href="https://github.com/glennsl/rescript-jest" rel="noopener noreferrer"&gt;Jest&lt;/a&gt;. However, in my opinion, Jest is a monstrous combine that is slow, has a hacky mock system, bad ESM support, and purely tries to mimic DOM API with JSDOM. AVA literally doesn't have any of these. Also, the available Jest bindings don't allow doing multiple assertions per test, which is often useful to me.&lt;/p&gt;

&lt;p&gt;Besides bindings for JavaScript libraries, there is &lt;a href="https://github.com/bloodyowl/rescript-test" rel="noopener noreferrer"&gt;rescript-test&lt;/a&gt; - a lightweight test framework written in ReScript for ReScript. I have heard that some people like it, but for me, it lacks coverage output and &lt;a href="https://wallabyjs.com/" rel="noopener noreferrer"&gt;Wallaby&lt;/a&gt; support.&lt;/p&gt;

&lt;h2&gt;
  
  
  E2E
&lt;/h2&gt;

&lt;p&gt;Even though I claim that integration tests are unnecessary, we still need to ensure that the application works correctly when all the modules are combined. For this purpose, I like having a few E2E tests that imitate an end user of my application and ensure that it works as intended.&lt;/p&gt;

&lt;p&gt;For FE, it’s usually &lt;a href="https://www.cypress.io/" rel="noopener noreferrer"&gt;Cypress&lt;/a&gt; or &lt;a href="https://playwright.dev/" rel="noopener noreferrer"&gt;Playwright&lt;/a&gt;; for BE, it’s to run a server and start sending requests; for CLI, I like the tool called &lt;a href="https://github.com/sindresorhus/execa" rel="noopener noreferrer"&gt;execa&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The idea is to imitate a real user. And using ReScript for this kind of test is good but not mandatory. You can have Cypress tests in Js, Selenium, Postman, or other external tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tip 1: Wallaby
&lt;/h2&gt;

&lt;p&gt;Up till now, I have mostly shown my worldview. But now, I would like to share with you some tips. And the first one is &lt;a href="https://wallabyjs.com/" rel="noopener noreferrer"&gt;Wallaby&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It is a JavaScript test runner that provides real-time code quality feedback. Even though in ReScript we don't get all the benefits, I still find it useful when it comes to diving into JavaScript to debug some failing tests. The stories feature is particularly helpful, where you can visually see how the generated code is executed.&lt;/p&gt;

&lt;p&gt;It helps to have tests always running and brings enjoyment to the process of working with them.&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%2Fh13b813hktazmfr8vl7w.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%2Fh13b813hktazmfr8vl7w.png" alt="Wallaby test story" width="800" height="649"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tip 2: Interfaces and opaque types
&lt;/h2&gt;

&lt;p&gt;Opaque types in ReScript are types whose implementation is hidden from other parts of the program, allowing for information hiding, modularity, type safety, and improved performance. They provide a clear interface for interacting with the type and can help ensure that the internal state of the type is consistent and well-formed. Shortly speaking, they are wonderful!&lt;/p&gt;

&lt;p&gt;Although when we write tests, we want to be able to assert that an opaque entity has an exact value. But it’s impossible to create the exact value for assertion since the implementation is hidden behind an interface.&lt;/p&gt;

&lt;p&gt;To workaround the problem, I’ve started creating &lt;code&gt;TestData&lt;/code&gt; modules with functions to create a value of the opaque type directly. And have a convention that it’s only allowed for use inside of the test files. Here’s an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ModuleName.res&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;


&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;fromBscFlag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bscFlag&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;// Some logic&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;TestData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;make&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;moduleName&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;moduleName&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ModuleName.resi&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;fromBscFlag&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;TestData&lt;/span&gt;: &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;make&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ModuleName_test.res&lt;/span&gt;
&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"fromBscFlag works with Belt"&lt;/span&gt;, &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nn"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deepEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nn"&gt;ModuleName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromBscFlag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"-open Belt"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;,
    &lt;span class="nc"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ModuleName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;TestData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Belt"&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;I’m very satisfied with the approach. Although if you develop a library and don’t control the end user of your code, it’s not a good idea to expose innards like this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tip 3: Place tests close to the tested code
&lt;/h2&gt;

&lt;p&gt;Another tip that won’t work for libraries while definitely worthy for applications. And it’s to place test files close to the code they are testing. Which can improve organization, maintainability, and convenience.&lt;/p&gt;

&lt;p&gt;That’s pretty common for Js, but in ReScript, the most common approach is to connect a testing library via the &lt;code&gt;bs-dev-dependencies&lt;/code&gt; and put tests in the &lt;code&gt;__tests__&lt;/code&gt; directory, marking it as &lt;code&gt;dev&lt;/code&gt; in the &lt;code&gt;bsconfig.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I think that the value of placing tests close to the code is much higher than using &lt;code&gt;bs-dev-dependencies&lt;/code&gt;, so I moved a testing library to the &lt;code&gt;bs-dependencies&lt;/code&gt; and removed the &lt;code&gt;__tests__&lt;/code&gt; directory.&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%2Fwxlhkamjwdili3fwha6z.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%2Fwxlhkamjwdili3fwha6z.png" alt="File structure with tests placed close to the tested code" width="622" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are afraid of test code leaking into the application. You can use &lt;a href="https://eslint.org/" rel="noopener noreferrer"&gt;eslint&lt;/a&gt; to prevent this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;rules&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="s2"&gt;no-restricted-imports&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&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="na"&gt;patterns&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="na"&gt;group&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="s2"&gt;*_test.bs.mjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
              &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;It's not allowed to import test modules.&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;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;h2&gt;
  
  
  Tip 4: Mocking
&lt;/h2&gt;

&lt;p&gt;Especially when talking about unit testing, we often have external dependencies that should be replaced during a test. And me saying that Jest’s mocking is a bad thing (the same goes for Vitest) might sound a bit controversial.&lt;br&gt;
One reason is that mocking in Jest heavily relies on JavaScript’s module system, which differs from ReScript’s. Also, the insanely hacky implementation and terrible DX make it even worse. And the main reason is that DI (Dependency Injection) solves the problem much better.&lt;/p&gt;

&lt;p&gt;Implementation depends on your architecture and will vary from project to project. I’ll share some examples of how I do it myself.&lt;/p&gt;

&lt;p&gt;I find that in most cases, DI can be done by simply passing dependencies as function arguments without any abstractions. And then return an implementation which is usually a single function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Lint.res&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;make&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="c1"&gt;// Dependencies&lt;/span&gt;
  ~&lt;span class="n"&gt;loadBsConfig&lt;/span&gt;,
  ~&lt;span class="n"&gt;loadSourceDirs&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="c1"&gt;// Implementation of the lint logic&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;. ~&lt;span class="n"&gt;config&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="c1"&gt;// Some code&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;blockquote&gt;
&lt;p&gt;The example is taken from my &lt;a href="https://github.com/DZakh/rescript-stdlib-vendorer" rel="noopener noreferrer"&gt;rescript-stdlib-vendorer&lt;/a&gt; project, which uses the approach as the core of the architecture. I recommend you take a look at the source code if you want to see a bigger picture.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But sometimes, implementation needs to be a whole module. And for this, we can use ReScript functors (functions that create modules).&lt;/p&gt;

&lt;p&gt;The idea is the following: when you are working on module &lt;code&gt;A&lt;/code&gt;, instead of calling &lt;code&gt;B.doSomething&lt;/code&gt; directly, you have an &lt;code&gt;AFactory.Make&lt;/code&gt; functor that accepts &lt;code&gt;doSomething&lt;/code&gt; as an argument and returns the &lt;code&gt;A&lt;/code&gt; module. This way, you can create the &lt;code&gt;A&lt;/code&gt; module with different dependencies for testing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// AFactory.res&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;Make&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;: &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;doSomething&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="p"&gt;}&lt;/span&gt;,
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;T&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;()&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// A.res&lt;/span&gt;
&lt;span class="k"&gt;include&lt;/span&gt; &lt;span class="nn"&gt;AFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Make&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;doSomething&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;B&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;doSomething&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// A_test.res&lt;/span&gt;
&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Calls doSomething once"&lt;/span&gt;, &lt;span class="n"&gt;t&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;let&lt;/span&gt; &lt;span class="n"&gt;doSomethingCallsCountRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;ref&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="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;A&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;AFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Make&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;doSomething&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;doSomethingCallsCountRef&lt;/span&gt; :&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;doSomethingCallsCountRef&lt;/span&gt;.&lt;span class="n"&gt;contents&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&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="nn"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nn"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doSomethingCallsCountRef&lt;/span&gt;.&lt;span class="n"&gt;contents&lt;/span&gt;, &lt;span class="mi"&gt;1&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 approach with functor has a few problems, usually not critical ones, though.&lt;/p&gt;

&lt;p&gt;One problem is that it suggests creating &lt;code&gt;A.res&lt;/code&gt;, which is publicly available from other application parts. It’s usually fine for FE apps, but for developing BE and CLIs, I prefer to follow the &lt;a href="https://en.wikipedia.org/wiki/Hexagonal_architecture_(software)" rel="noopener noreferrer"&gt;hexagonal architecture&lt;/a&gt; by creating the implementation inside the &lt;code&gt;Main.res&lt;/code&gt; and passing it to other modules via function arguments. Once again, an example from &lt;a href="https://github.com/DZakh/rescript-stdlib-vendorer" rel="noopener noreferrer"&gt;rescript-stdlib-vendorer&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Main.res&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;runCli&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;RunCli&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  ~&lt;span class="n"&gt;runLintCommand&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nn"&gt;RunLintCommand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    ~&lt;span class="n"&gt;lint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nn"&gt;Lint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      ~&lt;span class="n"&gt;loadBsConfig&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nn"&gt;LoadBsConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;,
      ~&lt;span class="n"&gt;loadSourceDirs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nn"&gt;LoadSourceDirs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make&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="n"&gt;runHelpCommand&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nn"&gt;RunHelpCommand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;,
  ~&lt;span class="n"&gt;runHelpLintCommand&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nn"&gt;RunHelpLintCommand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;,
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;runCli&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;Another problem with ReScript functors is that the created module is not tree-shakable. But if it’s really a problem, you probably already know how to work around it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tip 5: Test bindings
&lt;/h2&gt;

&lt;p&gt;Bindings with JavaScript is the most dangerous part of any application - the bridge with an unsafe and unpredictable world. And it’s definitely a bad idea to trust yourself that you’ve done it correctly. Especially considering the fact that it might become outdated during the next release.&lt;/p&gt;

&lt;p&gt;Honestly, I’m guilty myself of neglecting binding tests. So I can’t give you any advice on how to handle it in a good way. But at least I want you to be more careful and consider writing tests for bindings when you see that it might become a problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tip 6: Coverage
&lt;/h2&gt;

&lt;p&gt;Lastly, I want to share a small personal thing. I really like numbers and as well as seeing the result of my work in numbers. One of the ways to achieve this is to configure a test coverage analytics tool like &lt;a href="https://codecov.com/" rel="noopener noreferrer"&gt;Codecov&lt;/a&gt; in your CI. And although being over-concentrated on coverage is not a healthy and useful thing, I really noticed that it makes writing tests more exciting. It probably won’t work for everyone, but at least I recommend trying it out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project examples
&lt;/h2&gt;

&lt;p&gt;You can see how I follow the described ideas in my open-source projects. If you're interested, I recommend checking out the most model ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/DZakh/rescript-struct" rel="noopener noreferrer"&gt;rescript-struct&lt;/a&gt; - a good example of testing a library&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/DZakh/rescript-stdlib-vendorer" rel="noopener noreferrer"&gt;rescript-stdlib-vendorer&lt;/a&gt; - a good example of testing an application&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Discussion is encouraged
&lt;/h2&gt;

&lt;p&gt;I realize that I’ve mentioned quite a few contradictory things without going into detail. If you have any questions, suggestions or want to argue, feel free to speak up in the comment section or ping me on &lt;a href="https://twitter.com/dzakh_dev" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>rescript</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
