<?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: Ryan Kahn</title>
    <description>The latest articles on DEV Community by Ryan Kahn (@shiftyp).</description>
    <link>https://dev.to/shiftyp</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%2F110019%2Fbd609532-0400-4b6b-91d4-0d040e82d011.jpeg</url>
      <title>DEV Community: Ryan Kahn</title>
      <link>https://dev.to/shiftyp</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shiftyp"/>
    <language>en</language>
    <item>
      <title>Supercharge Your Observability: How OTEL MCP Server Unlocks AI-Powered Insights</title>
      <dc:creator>Ryan Kahn</dc:creator>
      <pubDate>Tue, 27 May 2025 14:43:40 +0000</pubDate>
      <link>https://dev.to/shiftyp/supercharge-your-observability-how-otel-mcp-server-unlocks-ai-powered-insights-5dii</link>
      <guid>https://dev.to/shiftyp/supercharge-your-observability-how-otel-mcp-server-unlocks-ai-powered-insights-5dii</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;"What caused the outage last night, and how do we fix it?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Imagine typing this question and getting an immediate, comprehensive answer – not from a human on call, but from an AI that has already analyzed your telemetry data, identified the root cause, and prepared a detailed explanation and recommendations with supporting evidence.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/shiftyp/otel-mcp-server" rel="noopener noreferrer"&gt;&lt;strong&gt;OTEL MCP Server&lt;/strong&gt;&lt;/a&gt; makes this possible today by connecting AI systems directly to your OpenTelemetry data. While we've solved the collection problem with OpenTelemetry, we're still facing a critical gap – mountains of telemetry data that remain largely untapped because our tools can't keep pace with the volume and complexity. OTEL MCP Server bridges this gap, transforming raw telemetry into actionable intelligence through the power of AI.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://opentelemetry.io/" rel="noopener noreferrer"&gt;OpenTelemetry&lt;/a&gt; (OTEL) is an open-source observability framework that provides a standardized way to collect telemetry data from your applications and infrastructure. It's a CNCF (Cloud Native Computing Foundation) project that has quickly become the industry standard for instrumentation.&lt;/p&gt;

&lt;p&gt;The key components of OpenTelemetry include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Traces&lt;/strong&gt;: Distributed tracing that tracks requests as they flow through your services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metrics&lt;/strong&gt;: Numerical measurements collected at regular intervals&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logs&lt;/strong&gt;: Time-stamped records of discrete events&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Baggage&lt;/strong&gt;: Contextual metadata that enriches your telemetry data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;OpenTelemetry solves the instrumentation problem by providing a vendor-neutral API and SDK that works across languages and frameworks. This means you can instrument your code once and send the data to any compatible backend – whether that's Elasticsearch, Prometheus, Jaeger, or commercial observability platforms.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is OTEL MCP Server?
&lt;/h2&gt;

&lt;p&gt;OTEL MCP Server is an open-source tool that implements the Model Context Protocol (MCP) to provide AI systems with direct access to your OpenTelemetry data in Elasticsearch. It acts as a specialized connector that allows AI assistants to query and analyze your traces, metrics, and logs in real-time, regardless of your application architecture - from monoliths to microservices, cloud-native to on-premises deployments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Capabilities
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Direct Elasticsearch Queries&lt;/strong&gt;: Execute powerful, custom queries against your telemetry data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service-Aware Tools&lt;/strong&gt;: Filter all operations by service or query across multiple services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Field Discovery&lt;/strong&gt;: Automatically identify available fields to construct effective queries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dual Mapping Support&lt;/strong&gt;: Works with both native OTEL and Elastic Common Schema (ECS) mapping modes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-Platform&lt;/strong&gt;: Runs on Windows, macOS, and Linux&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why It Matters
&lt;/h2&gt;

&lt;p&gt;OpenTelemetry has solved the data collection problem by providing a vendor-neutral way to instrument applications and gather telemetry data. However, traditional observability tools still require humans to manually navigate dashboards and query interfaces. &lt;/p&gt;

&lt;p&gt;While paid observability platforms offer features like anomaly detection and alerting, they often come with significant costs and lock you into proprietary ecosystems. These services are valuable but can be expensive as your data volume grows, and they still require manual investigation when issues arise.&lt;/p&gt;

&lt;p&gt;OTEL MCP Server offers a complementary approach by enabling AI systems to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Investigate Incidents Autonomously&lt;/strong&gt;: AI can query traces during outages, find error patterns, and correlate issues across your entire application landscape&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provide Context-Aware Assistance&lt;/strong&gt;: When reviewing code, AI can simultaneously analyze related production telemetry data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Answer Complex Questions&lt;/strong&gt;: "Show me all errors from the authentication flow in the last 24 hours" becomes a simple natural language request&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Detect Anomalies On-Demand&lt;/strong&gt;: Instead of configuring static thresholds, ask the AI to identify unusual patterns in your data&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As developers, we know that a well-designed system reveals its own inner workings. A truly complete software system teaches developers how it functions, how to troubleshoot it, and how to extend it. By connecting OpenTelemetry data to AI, OTEL MCP Server amplifies this natural learning process, making the system's behavior more transparent and accessible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;Before you begin, you'll need:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Elasticsearch&lt;/strong&gt; (version 8.x or higher) with OpenTelemetry data&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OTEL MCP Server works with both OTEL and ECS mapping modes&lt;/li&gt;
&lt;li&gt;For development, you can use Elasticsearch with security disabled&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;OpenTelemetry Data&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Either from your own instrumented applications&lt;/li&gt;
&lt;li&gt;Or from the &lt;a href="https://github.com/open-telemetry/opentelemetry-demo" rel="noopener noreferrer"&gt;OpenTelemetry Demo&lt;/a&gt; for testing&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Windsurf Integration
&lt;/h3&gt;

&lt;p&gt;To use OTEL MCP Server with Windsurf, add this to your &lt;a href="https://docs.windsurf.com/windsurf/cascade/mcp" rel="noopener noreferrer"&gt;Windsurf MCP configuration&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"servers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"otel-mcp-server"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"otel-mcp-server"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Replace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Elasticsearch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;URL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;credentials&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"ELASTICSEARCH_URL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:9200"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"ELASTICSEARCH_USERNAME"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"elastic"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"ELASTICSEARCH_PASSWORD"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"changeme"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"SERVER_NAME"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"otel-mcp-server"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"LOGLEVEL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OFF"&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once running, the server automatically detects available telemetry types in your Elasticsearch instance and registers the appropriate tools for your environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Applications
&lt;/h2&gt;

&lt;p&gt;The true power of OTEL MCP Server emerges when integrated with AI-powered tools like &lt;a href="https://windsurf.com/editor" rel="noopener noreferrer"&gt;Windsurf&lt;/a&gt;, creating a seamless bridge between code and telemetry data.&lt;/p&gt;

&lt;h3&gt;
  
  
  End-to-End Incident Management
&lt;/h3&gt;

&lt;p&gt;In a recent demonstration, OTEL MCP Server was used with Windsurf and the OpenTelemetry Demo application to generate an entire incident report based on the demo application's test features, along with issues injected by Chaos Mesh. With just a few natural language prompts, the AI was able to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Discover and analyze error patterns across multiple services&lt;/li&gt;
&lt;li&gt;Correlate logs, traces, and metrics to identify the root cause&lt;/li&gt;
&lt;li&gt;Generate a comprehensive incident report with supporting evidence&lt;/li&gt;
&lt;li&gt;Provide actionable recommendations for fixing the issues&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can see the &lt;a href="https://www.dropbox.com/scl/fi/o960wzw1p1zrnea8wqbhd/Recording-2025-05-26-214156.mp4?rlkey=c1sr4p31d54i48zmo6m4sav2j&amp;amp;st=cq1zxuez&amp;amp;dl=0" rel="noopener noreferrer"&gt;process in action&lt;/a&gt; and view the &lt;a href="https://gist.github.com/shiftyp/ebb1cc49196ddffd04d8c9709eb01c54" rel="noopener noreferrer"&gt;end result&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Unified Code and Telemetry Experience
&lt;/h3&gt;

&lt;p&gt;With OTEL MCP Server, developers can interact with both their code and production telemetry through a single interface. This creates a powerful feedback loop where you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Analyze Performance Bottlenecks&lt;/strong&gt;: "Find slow database queries in our payment service and suggest code improvements"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debug with Production Context&lt;/strong&gt;: "Show me recent error traces related to this authentication function I'm debugging"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate Fixes&lt;/strong&gt;: "After my recent fix to the checkout service, did the error rate decrease?"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Understand System Behavior&lt;/strong&gt;: "How does this microservice interact with others in production?"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Identify Patterns&lt;/strong&gt;: "Show me the memory usage metrics for our application over the past day"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compare Deployments&lt;/strong&gt;: "Compare response times before and after our recent deployment"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The AI can directly access this data, analyze it, combine it with code context, and provide insights - all without you having to switch contexts or manually query your observability platform.&lt;/p&gt;

&lt;p&gt;This unified view creates a development environment where the system itself becomes self-documenting and self-healing through its telemetry data, teaching developers how it works, how to fix issues, and how to extend it effectively.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Future of AI-Powered Observability
&lt;/h2&gt;

&lt;p&gt;OpenTelemetry has democratized telemetry data collection across the industry. Now, OTEL MCP Server takes the next step by democratizing access to this data for AI systems. This represents a fundamental shift in how we interact with observability data, moving toward a future where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Incident response becomes more automated and efficient&lt;/li&gt;
&lt;li&gt;Developers get real-time feedback based on production telemetry&lt;/li&gt;
&lt;li&gt;The gap between code and runtime behavior narrows significantly&lt;/li&gt;
&lt;li&gt;Observability becomes a natural part of every developer's workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ready to try it yourself? Check out the &lt;a href="https://github.com/shiftyp/otel-mcp-server" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; and start connecting your AI tools to your OpenTelemetry!&lt;/p&gt;

</description>
      <category>opentelemetry</category>
      <category>observability</category>
      <category>ai</category>
      <category>elasticsearch</category>
    </item>
    <item>
      <title>My Type of Coding: Who reads what AI writes?</title>
      <dc:creator>Ryan Kahn</dc:creator>
      <pubDate>Fri, 11 Apr 2025 03:43:46 +0000</pubDate>
      <link>https://dev.to/shiftyp/my-type-of-coding-who-reads-what-ai-writes-2i7e</link>
      <guid>https://dev.to/shiftyp/my-type-of-coding-who-reads-what-ai-writes-2i7e</guid>
      <description>&lt;p&gt;One of the detractors of AI in the software development field is the idea that it does and will inhibit critical thinking and creativity among the next generation of developers. While I agree that this is possible, I don't see it as inevitable. In fact, AI in coding has the potential to usher in a new era of competency and understanding of the underlying principles of programming.&lt;/p&gt;

&lt;p&gt;Much of the focus around AI has been on it being a tool to write code, to fill in blanks that a human developer would otherwise have to fill in. This begs an interesting question though, who is &lt;strong&gt;reading&lt;/strong&gt; that code? Who is the critical thinker who reviews the changes and handles integration?&lt;/p&gt;

&lt;p&gt;One story is that its nobody, that we all just vibe code and &lt;code&gt;git commit -m yolo&lt;/code&gt;. Another story is that there's an overworked senior developer who is driven mad chasing the inconsistencies in the patchwork quilt that is vibe code. Neither of these stories is particularly positive or appealing from the standpoint of professional growth.&lt;/p&gt;

&lt;p&gt;When I was starting out in software development, there was StackOverflow, there was Google and blog posts, and there were API Docs. There was a fourth, and lesser known resource that I found invaluable, and that is code. Yes that stuff we write, and more importantly the stuff &lt;em&gt;other&lt;/em&gt; people write. I often say the point where my growth accelerated was when I started critically reading other peoples code.&lt;/p&gt;

&lt;p&gt;This isn't something I have seen many developers, junior or otherwise, doing. Yes they participate in code reviews, yes they read the code around what they need to modify and extend, but no they don't read with the express purpose of learning new patterns, and new ways of doing things. This is where AI offers us an incredible opportunity.&lt;/p&gt;

&lt;p&gt;Rather than using AI to simply write code for you, read the code it creates, engage your critical thinking skills, and learn from the patterns and practices it represents. In many ways it is the spirit of everyone else's code the model has gobbled up that you're conjuring when you hit enter on your well crafted prompt. AI offers us a learning experience tailored to the particular problems we're trying to solve.&lt;/p&gt;

&lt;p&gt;AI certainly has the potential to drain our brains, to deprive us of opportunities to learn and think in the name of efficiency. Now that you're more efficient though, take the time to read through the code, understand the patterns, the API's involved. Find the mistakes and inconsistencies hidden like Easter Eggs throughout AI generated code. Become that senior developer who is not just reading code, but learning from it.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ai</category>
      <category>beginners</category>
      <category>advice</category>
    </item>
    <item>
      <title>My Type of Coding: Who reads what AI writes?</title>
      <dc:creator>Ryan Kahn</dc:creator>
      <pubDate>Fri, 11 Apr 2025 03:43:46 +0000</pubDate>
      <link>https://dev.to/shiftyp/my-type-of-coding-who-reads-what-ai-writes-5c1d</link>
      <guid>https://dev.to/shiftyp/my-type-of-coding-who-reads-what-ai-writes-5c1d</guid>
      <description>&lt;p&gt;One of the detractors of AI in the software development field is the idea that it does and will inhibit critical thinking and creativity among the next generation of developers. While I agree that this is possible, I don't see it as inevitable. In fact, AI in coding has the potential to usher in a new era of competency and understanding of the underlying principles of programming.&lt;/p&gt;

&lt;p&gt;Much of the focus around AI has been on it being a tool to write code, to fill in blanks that a human developer would otherwise have to fill in. This begs an interesting question though, who is &lt;strong&gt;reading&lt;/strong&gt; that code? Who is the critical thinker who reviews the changes and handles integration?&lt;/p&gt;

&lt;p&gt;One story is that its nobody, that we all just vibe code and &lt;code&gt;git commit -m yolo&lt;/code&gt;. Another story is that there's an overworked senior developer who is driven mad chasing the inconsistencies in the patchwork quilt that is vibe code. Neither of these stories is particularly positive or appealing from the standpoint of professional growth.&lt;/p&gt;

&lt;p&gt;When I was starting out in software development, there was StackOverflow, there was Google and blog posts, and there were API Docs. There was a fourth, and lesser known resource that I found invaluable, and that is code. Yes that stuff we write, and more importantly the stuff &lt;em&gt;other&lt;/em&gt; people write. I often say the point where my growth accelerated was when I started critically reading other peoples code.&lt;/p&gt;

&lt;p&gt;This isn't something I have seen many developers, junior or otherwise, doing. Yes they participate in code reviews, yes they read the code around what they need to modify and extend, but no they don't read with the express purpose of learning new patterns, and new ways of doing things. This is where AI offers us an incredible opportunity.&lt;/p&gt;

&lt;p&gt;Rather than using AI to simply write code for you, read the code it creates, engage your critical thinking skills, and learn from the patterns and practices it represents. In many ways it is the spirit of everyone else's code the model has gobbled up that you're conjuring when you hit enter on your well crafted prompt. AI offers us a learning experience tailored to the particular problems we're trying to solve.&lt;/p&gt;

&lt;p&gt;AI certainly has the potential to drain our brains, to deprive us of opportunities to learn and think in the name of efficiency. Now that you're more efficient though, take the time to read through the code, understand the patterns, the API's involved. Find the mistakes and inconsistencies hidden like Easter Eggs throughout AI generated code. Become that senior developer who is not just reading code, but learning from it.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ai</category>
      <category>beginners</category>
      <category>advice</category>
    </item>
    <item>
      <title>My Type of Coding: Express Route Param Types</title>
      <dc:creator>Ryan Kahn</dc:creator>
      <pubDate>Mon, 07 Apr 2025 02:34:07 +0000</pubDate>
      <link>https://dev.to/shiftyp/my-type-of-coding-express-route-param-types-3ll9</link>
      <guid>https://dev.to/shiftyp/my-type-of-coding-express-route-param-types-3ll9</guid>
      <description>&lt;p&gt;How does express extract the route parameters in TypeScript, such that we can read them off the &lt;code&gt;request.params&lt;/code&gt; object in a typesafe way? It's quite ingenious, and uses template literal types, conditional types, and the &lt;code&gt;infer&lt;/code&gt; keyword to recursively process the parameter names out of the path string and apply required and optional parameters to the params object in route handlers. Here are those types extracted into a CodeSandbox. If you open it on CodeSandbox you can see the intellisense at work!&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/68jxl8?module=/src/index.ts&amp;amp;view=editor"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>javascript</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>My Type of Coding: TypeScript is for Humans</title>
      <dc:creator>Ryan Kahn</dc:creator>
      <pubDate>Thu, 03 Apr 2025 13:55:37 +0000</pubDate>
      <link>https://dev.to/shiftyp/my-type-of-coding-typescript-is-for-humans-33fo</link>
      <guid>https://dev.to/shiftyp/my-type-of-coding-typescript-is-for-humans-33fo</guid>
      <description>&lt;p&gt;When I started writing JavaScript, I loved it. I often compared it to painting, as opposed to a language like Java where it felt like I was building with blocks. These analogies represents spaces where patterns emerge. and what shape they will take.&lt;/p&gt;

&lt;p&gt;In the painting world of JavaScript the patterns are innumerable. Almost any design pattern you like is possible, and you can make values essentially do whatever you please. This creates a problem though, and the problem is the "you" in those statements.&lt;/p&gt;

&lt;p&gt;You are not alone! The design patterns you like may be obscure to someone else, and the tricks you do with values may not be amusing to someone else who has to figure them out. And so the beauty of the painting is in the mind of the coder who created it, and the mess is for everyone else.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Patterns are in the Painting
&lt;/h3&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%2Fokg1yuuoxrxrmigjm4ax.jpg" 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%2Fokg1yuuoxrxrmigjm4ax.jpg" alt="A photograph of the painting " width="600" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;"The Sacrament of the Last Supper" by Salvador Dali contains a myriad of patterns. Geometry, Perspective, Symbolism, Realism, Symmetry...but underneath it all what does it mean? In order to understand the genesis of these patterns, we would need the thousand words the picture is worth to tell us what the artist was thinking during the creation of a masterpiece.&lt;/p&gt;

&lt;p&gt;Coding, however, is not painting. There is no "last" supper, it's an everlasting supper. We continue to add fruit to the table then use the painting as a recipe for dinner. So where do I add the fruit? Should it be an apple or a pear? Should I add the fruit at all? If I add a fruit should I remove a disciple? These are the questions we have to answer when we code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Now we come to TypeScript
&lt;/h2&gt;

&lt;p&gt;TypeScript is the language we use to describe patterns in our JavaScript. We're still painting, but while we're painting we're forced to explain the patterns in our mind to those who must carry the patterns in the code into the future. The patterns it elucidates are not obscure, ideally they should be what you were thinking anyway if you were just writing JavaScript.&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;Frame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Element&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;FrameElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Frame&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;Element&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can describe the patterns in the same way as we're thinking about them as well. In the example above we have the concept of a &lt;code&gt;Frame&lt;/code&gt;, which contains a &lt;code&gt;src: URL&lt;/code&gt; property, and an &lt;code&gt;Element&lt;/code&gt;, which can recursively have &lt;code&gt;children?: Element[]&lt;/code&gt;. This explains something particular about how we were thinking about the code. That there are separate ways to look at the same object, and maybe separate objects that answer similar requirements. Like a pear in the painting having both a real and symbolic meaning, or an apple and a pear both being fruit.&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;NotFound&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;OK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;StatusCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;NotFound&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;OK&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;Err&lt;/span&gt;

&lt;span class="c1"&gt;// Or&lt;/span&gt;

&lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;StatusCode&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;NotFound&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;OK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&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;Sometimes we're presented with a choice, two ways to explain the same thing. Here we have a representation of HTTP status codes in an application. The first way of representing the code exists only in TypeLand®️. The second creates an &lt;code&gt;enum&lt;/code&gt;, which is both a type and an object at runtime. Which you choose depends on the requirements but also how you view the pattern you're trying to create, and how you see that pattern being used in the future as well. As in all coding, there's a preference for simpler answers.&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;observeSymbol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Symbol&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;observe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Unsubscribe&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="k"&gt;void&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Observer&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="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Subscribe&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="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Observer&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Unsubscribe&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Observable&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="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;observeSymbol&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;Subscribe&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="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ObservableMap&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="o"&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;K&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;K&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="nf"&gt;extends &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Observable&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="nx"&gt;K&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That said, sometimes things get complicated. Remember that TypeScript is essentially two programming languages in one. The one that rules TypeLand®️ and the one that runs the runtime. The type side has its own variables (aliases), functions (generics), logic (conditional types), comprehensions (mapped types), ect. We can make the complexity simpler just like we do in JavaScript or any programming language however.&lt;/p&gt;

&lt;p&gt;In the example above, I could have just written out the &lt;code&gt;ObservableMap&lt;/code&gt; type without the intermediate types. But, by factoring them out, as we would factor out references and functions, makes the code more readable. It also makes the patterns reusable. For example I might find myself wanting to subscribe to each key on an &lt;code&gt;ObservableMap&lt;/code&gt;, in which case I might later add this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ObserverMap&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;K&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;K&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="nf"&gt;extends &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Observer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;U&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;const&lt;/span&gt; &lt;span class="nx"&gt;ObserveObject&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;map&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ObservableMap&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;obs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ObserverMap&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;Unsubscribe&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;return&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Unsubscribe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;k&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;ObservableMap&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="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;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&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;function&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;unsubscribe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;observableSymbol&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="nx"&gt;obs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="nf"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
              &lt;span class="nf"&gt;unsubscribe&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="o"&gt;=&amp;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;Just like smaller more general functions in JavaScript, these smaller more general types or patterns in TypeScript allow us to reuse ideas to create new ones. Like Dali borrows patterns from da Vinci. While the above may seem complex (it is), in the end it's merely describing what we are actually doing in JavaScript. The real complexity is &lt;em&gt;not&lt;/em&gt; documenting the patterns, and forcing everyone who looks at the code to re-invent them to figure out how to extend them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we didn't cover
&lt;/h2&gt;

&lt;p&gt;We covered the human reason for TypeScript, as a form of documentation for how we as developers think of the JavaScript we write. What we didn't cover are the automation implications of this translation. Now not only can humans read the painting, but we've structured it in a way that machines can understand it as well. The ideas that we expressed in the original work can be checked for consistency, and this leads to a more reliable codebase. There are certain refactors as well that can be performed automatically, which is an added bonus.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Painting is an art, and so is coding — whether in JavaScript, TypeScript, or &lt;code&gt;__________&lt;/code&gt;. However you choose to write your JavaScript, consider the person in the future how has to figure out what exists only in your mind as you write it. Unlike a painting, the mystery is not the point of the art, the function is. Leave the mystery to Dali, and write code that is understandable, verifiable, and extensible.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>programming</category>
    </item>
    <item>
      <title>My Type of Library: Axios TypeScript Conversion</title>
      <dc:creator>Ryan Kahn</dc:creator>
      <pubDate>Tue, 01 Apr 2025 13:39:38 +0000</pubDate>
      <link>https://dev.to/shiftyp/my-type-of-library-axios-typescript-conversion-3m6j</link>
      <guid>https://dev.to/shiftyp/my-type-of-library-axios-typescript-conversion-3m6j</guid>
      <description>&lt;p&gt;This is the first post in an ad-hoc series where I convert libraries to TypeScript, and analyze the results. In the same vein as my &lt;a href=""&gt;re-conversion of the Turbo Framework&lt;/a&gt; to TypeScript, I decided to take a popular JavaScript library, this time one that had always been in JavaScript, and see what converting the codebase to TypeScript revealed. I chose &lt;a href="https://github.com/axios/axios" rel="noopener noreferrer"&gt;Axios&lt;/a&gt;, because it’s:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Popular, well used, extensively developed&lt;/li&gt;
&lt;li&gt;Written in JavaScript, with a manually generated TypeScript definitions file&lt;/li&gt;
&lt;li&gt;Small enough to be easily convertible, and large enough to be interesting&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The functional issues revealed during the conversion show the flexibility of JavaScript and the clever “tricks” we often perform with it. In highlighting the issues, I utilized AI agents through the &lt;a href="https://codeium.com/windsurf" rel="noopener noreferrer"&gt;Windsurf&lt;/a&gt; editor, and when it offered suggestions for fixes, I realized that even with the extensive context provided by the codebase, its solutions were still sub-par and illustrative of the limits of how AI can help with TypeScript problems.&lt;/p&gt;

&lt;p&gt;To that end, I’ll highlight some of the issues found in the conversion that necessitate functional changes, referencing both &lt;a href="https://github.com/shiftyp/axios/tree/v1.x" rel="noopener noreferrer"&gt;my converted code&lt;/a&gt; and the Axios main branch as of this writing. I’ll also compare the solutions to these issues that were provided by &lt;a href="https://claude.ai/" rel="noopener noreferrer"&gt;Claude 3.7&lt;/a&gt;, and contrast them with my preferred solutions to show how Claude conceives of the code and solutions, and how this differs from the way a skilled developer (if I do say so myself) views them.&lt;/p&gt;

&lt;p&gt;To be clear, there are many errors in my conversion. Where I left things there are 300 odd errors, and the majority of these are probably not hinting at actual issues in the library. My goal in all this is to gain insight into how people write JavaScript utilizing TypeScript as a lens, not port the entire library and tests. &lt;/p&gt;

&lt;p&gt;Instead of doing that conversion writing a book, I'll write these posts about files where the Axios rubber meets the road. The first is the &lt;a href="https://github.com/axios/axios/blob/5a9134e0995b08cc0817548ed44e79c420c620b7/lib/adapters/fetch.js" rel="noopener noreferrer"&gt;"fetch adapter"&lt;/a&gt;, which integrates the modern &lt;code&gt;fetch&lt;/code&gt; browser API into Axios. Although it's not Axios's default adapter, it still provides some elucidating errors it when converted. It was also created mostly in &lt;a href="https://github.com/axios/axios/pull/6371" rel="noopener noreferrer"&gt;one PR&lt;/a&gt; by a core maintainer, so it highlights what TypeScript can do on the single developer, small scale. In the next post I'll take a look at another file, one that can tell us more about TypeScript in an active historical codebase, and when looked at in concert with the &lt;code&gt;git blame&lt;/code&gt; and the associated pull requests, can give us an idea of how issues organically come about.&lt;/p&gt;

&lt;h2&gt;
  
  
  TS2322: Type is not assignable
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Error:&lt;/strong&gt; Type 'string' is not assignable to type &lt;code&gt;'ResponseType | undefined'&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JS File&lt;/strong&gt; &lt;a href="https://github.com/axios/axios/blob/5a9134e0995b08cc0817548ed44e79c420c620b7/lib/adapters/fetch.js#L114" rel="noopener noreferrer"&gt;&lt;code&gt;lib/adapters/fetch.js:114&lt;/code&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;TS File:&lt;/strong&gt; &lt;a href="https://github.com/shiftyp/axios/blob/28e707d97d21a799ad72212bd11371e160edc5ae/lib/adapters/fetch.ts#L131" rel="noopener noreferrer"&gt;&lt;code&gt;lib/adapters/fetch.ts:131&lt;/code&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;responseType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;responseType&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;responseType&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&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;To begin with this error, let's start with the type in question: &lt;code&gt;ResponseType&lt;/code&gt;, which represents the different kinds of response formats that might be received from a fetch request. This comes from the &lt;a href="https://github.com/axios/axios/blob/5a9134e0995b08cc0817548ed44e79c420c620b7/index.d.ts#L208-L215" rel="noopener noreferrer"&gt;index.d.ts&lt;/a&gt; file manually generated by the project itself:&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;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ResponseType&lt;/span&gt; &lt;span class="o"&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;arraybuffer&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;blob&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;document&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;json&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;text&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;stream&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;formdata&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;This error comes from the fact that we take a response type, and convert it from the more specific union type to a general string by concatenating it with an empty string converting it to lowercase. The "obvious" solution, and the one recommended by Claude, is to simply say "This was a possibly incorrectly cased &lt;code&gt;ResponseType&lt;/code&gt;, it's now a &lt;code&gt;ResponseType&lt;/code&gt;" using &lt;code&gt;as&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;responseType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;responseType&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;responseType&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ResponseType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's think about this for a second though, why was it incorrectly cased in the first place? And if it was incorrectly cased, is it definitely a &lt;code&gt;ResponseType&lt;/code&gt; like value in the first place? The answer is no, it might not be. This value comes directly from the user, and tracing the path from input to this point I was unable to find any validation that ensures this value is a valid response type. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/axios/axios/blob/5a9134e0995b08cc0817548ed44e79c420c620b7/lib/adapters/fetch.js#L199" rel="noopener noreferrer"&gt;Later on in the file&lt;/a&gt; we see that the &lt;code&gt;responseType&lt;/code&gt; is used to access a &lt;code&gt;resolver&lt;/code&gt;, which in turn accesses a &lt;code&gt;Response&lt;/code&gt; method to deserialize the &lt;code&gt;responseData&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;responseData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;resolvers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;utils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolvers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;responseType&lt;/span&gt;&lt;span class="p"&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;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="nx"&gt;response&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the type of response provided was incorrect, it wouldn't be found in the map of possible resolvers, and default to 'text'. What if I just typed &lt;code&gt;bloob&lt;/code&gt; instead of &lt;code&gt;blob&lt;/code&gt; though, does this lead to a logical error and a good user experience, as the library tries to parse my binary file as text? Probably not, and there is a better way (besides using types).&lt;/p&gt;

&lt;p&gt;A friendlier solution that is hinted at by this error is to create some validation, and implement it much earlier on when we're processing the configuration from the user. There is already some generic code for &lt;a href="https://github.com/axios/axios/blob/5a9134e0995b08cc0817548ed44e79c420c620b7/lib/core/mergeConfig.js" rel="noopener noreferrer"&gt;merging configuration values with defaults&lt;/a&gt;, it seems that a similar &lt;code&gt;validateConfig.js&lt;/code&gt; file could be created that follows a similar pattern, and provides functions for validating each value in the passed configuration. This could throw errors at the point where the configuration is passed, improving the developer experience in my example case and others involving similarly incorrect configuration values.&lt;/p&gt;

&lt;h3&gt;
  
  
  TS2345: Argument is not assignable to parameter
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&lt;/strong&gt; Argument of type &lt;code&gt;'{ body: ReadableStream; method: string; readonly duplex: string; }'&lt;/code&gt; is not assignable to parameter of type &lt;code&gt;'RequestInit'&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JS File:&lt;/strong&gt; &lt;a href="https://github.com/axios/axios/blob/5a9134e0995b08cc0817548ed44e79c420c620b7/lib/adapters/fetch.js#L34" rel="noopener noreferrer"&gt;&lt;code&gt;lib/adapters/fetch.js:31&lt;/code&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;TS File&lt;/strong&gt;: &lt;a href="https://github.com/shiftyp/axios/blob/28e707d97d21a799ad72212bd11371e160edc5ae/lib/adapters/fetch.ts#L39" rel="noopener noreferrer"&gt;&lt;code&gt;lib/adapters/fetch.ts:39&lt;/code&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;supportsRequestStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;isReadableStreamSupported&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;test&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;duplexAccessed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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;hasContentType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ReadableStream&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="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;duplex&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;duplexAccessed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;half&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;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&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="nx"&gt;duplexAccessed&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;hasContentType&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;What's wrong with this picture? Trick question, there's nothing fundamentally wrong in my mind with this JavaScript. It's an example of feature detection, where stream request capability (as of this writing supported by Chrome, Edge, and Opera, and Node LTS and on) is supported by the current runtime. The method is actually quite creative, and twofold. First, pass an instance of &lt;code&gt;ReadableStream&lt;/code&gt; as the request body, and check that the generated request does not have a content-type, which tells us we're not in FireFox for example which will return a content-type of &lt;code&gt;"text/plain"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There is a problem with this in node environments however, in that older versions of Node will also return an object without a content type header, even though they don't implement streaming requests. So the final bit is to add a getter for the duplex property, and see if its actually read by the runtime's &lt;code&gt;Request&lt;/code&gt; implementation. Brilliant! JavaScript allows us to pass objects with unused properties, so it won't tell us if anyone cares about them or not. This does, very cool!&lt;/p&gt;

&lt;p&gt;While JavaScript allows us to pass these unused properties, TypeScript does not. Since the duplex property is not completely supported across major browsers, its not included in the &lt;code&gt;RequestInit&lt;/code&gt; type that's expected as the second argument to the &lt;code&gt;Request&lt;/code&gt; constructor. So now that we've established its all good, how do we tell TypeScript not to worry. Claude AI comes up with what I think is a wrong answer given the context. Basically, let's create our own type that isn't in the standard library, and tell TypeScript its that instead. Something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ExtendedRequestInit&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;RequestInit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;duplex&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="c1"&gt;// Then use it:&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ExtendedRequestInit&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why is this a bad idea? Well someday that type will no longer be needed, as the duplex property and functionality becomes more widely supported. If that happens, and the standard library is updated, this band-aid will still be there, and become unneeded. A better solution to this problem is to use the &lt;code&gt;@ts-expect-error&lt;/code&gt; directive, which tells TypeScript "I know this is wrong, trust me". And if one day it becomes correct, TypeScript will return the favor and say "Actually, you're wrong, it's right." When it becomes right we remove the directive, and all is 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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;supportsRequestStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;isReadableStreamSupported&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;test&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;duplexAccessed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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;hasContentType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ReadableStream&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="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// @ts-expect-error duplex is an experimental property&lt;/span&gt;
    &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;duplex&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;duplexAccessed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;half&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;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&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="nx"&gt;duplexAccessed&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;hasContentType&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;Directives are a feature and they're useful, &lt;code&gt;@ts-ignore&lt;/code&gt; over &lt;code&gt;any&lt;/code&gt; as the former is documenting, &lt;code&gt;@ts-expect-error&lt;/code&gt; is even better as it will tell you if its fixed. My personal opinion is that &lt;code&gt;any&lt;/code&gt; is not a type its a type-hole, its not a feature of TypeScript its a compiler error, don't use it.&lt;/p&gt;

&lt;h3&gt;
  
  
  TS2367: This comparison appears to be unintentional
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&lt;/strong&gt; This comparison appears to be unintentional because the types have no overlap.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JS File:&lt;/strong&gt; &lt;a href="https://github.com/axios/axios/blob/5a9134e0995b08cc0817548ed44e79c420c620b7/lib/adapters/fetch.js#L172" rel="noopener noreferrer"&gt;&lt;code&gt;lib/adapters/fetch.js:172&lt;/code&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;TS File:&lt;/strong&gt; &lt;a href="https://github.com/shiftyp/axios/blob/28e707d97d21a799ad72212bd11371e160edc5ae/lib/adapters/fetch.ts#L189" rel="noopener noreferrer"&gt;&lt;code&gt;lib/adapters/fetch.ts:189&lt;/code&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isStreamResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;supportsResponseStream&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;responseType&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stream&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;responseType&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;response&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;Take a look at this one, and thinking back to what we know about &lt;code&gt;ResponseType&lt;/code&gt;, what might be the issue? If you said &lt;code&gt;'response'&lt;/code&gt; isn't a valid &lt;code&gt;ResponseType&lt;/code&gt;, you'd be right! We know this because it's in the type definitions that exist already in Axios. I also know this because a search of the codebase reveals that the string doesn't appear in any other context in relation to a &lt;code&gt;ResponseType&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This line existed in the original PR, and was actually modified in a &lt;a href="https://github.com/axios/axios/pull/6406" rel="noopener noreferrer"&gt;second pr&lt;/a&gt; to fix a bug, but in both cases this comparison appears. Maybe it was an addition for an idea that was never completed or wasn't needed by the end of the PR, but either way TypeScript tells us its probably a mistake. Tell us Claude, how should we fix this? What's that, create a new &lt;code&gt;CustomResponseType&lt;/code&gt; to include 'response'?&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;CustomResponseType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ResponseType&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;response&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;This is a solution, but it completely misses the reality of this kind of error. Its pointing to code that should be removed, not simply a compiler error to be silenced. We sometimes get into this habit ourselves as developers, especially when we're the second developer to touch a piece of code. It can be scary to delete from the codebase. Isn't it "safer" to put a bandaid on the error in TypeLand®️, rather than risk it all in the real world?&lt;/p&gt;

&lt;p&gt;This is where TypeScript gives us an edge, because a) it would have prevented the problem in the first place. That's it actually, there is no b. And, when things do come up, TypeScript and properly typed code gives us the confidence to refactor without fear of breaking the contracts that the types define.&lt;/p&gt;

&lt;h2&gt;
  
  
  Up Next!
&lt;/h2&gt;

&lt;p&gt;Stay tuned for my next post in this series, where I'll break down a second file in the Axios codebase!&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Type-Casting Doubt: How A TypeScript Re-Migration Revealed The Truth About Turbo</title>
      <dc:creator>Ryan Kahn</dc:creator>
      <pubDate>Sun, 30 Mar 2025 01:15:32 +0000</pubDate>
      <link>https://dev.to/shiftyp/type-casting-doubt-how-a-typescript-re-migration-revealed-the-truth-about-turbo-2ajf</link>
      <guid>https://dev.to/shiftyp/type-casting-doubt-how-a-typescript-re-migration-revealed-the-truth-about-turbo-2ajf</guid>
      <description>&lt;p&gt;In September 2023, &lt;a href="https://world.hey.com/dhh/turbo-8-is-dropping-typescript-70165c01" rel="noopener noreferrer"&gt;David Heinemeier Hansson announced&lt;/a&gt; that "Turbo 8 is dropping TypeScript." This decision generated significant discussion in the development community regarding the merits of static versus dynamic typing systems.&lt;/p&gt;

&lt;p&gt;The Turbo framework represents an ideal case study because it previously underwent a full JavaScript-to-TypeScript migration before reverting back to JavaScript. This prior conversion was not merely superficial; it involved substantial refactoring throughout the codebase.&lt;/p&gt;

&lt;p&gt;This analysis presents empirical evidence regarding TypeScript's effectiveness in the context of the Turbo framework, based on a reimplementation of Turbo using TypeScript (&lt;a href="https://github.com/shiftyp/ts-turbo" rel="noopener noreferrer"&gt;github.com/shiftyp/ts-turbo&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Historical Context of TypeScript in Turbo&lt;/li&gt;
&lt;li&gt;
What TypeScript Actually Fixed

&lt;ul&gt;
&lt;li&gt;Null References&lt;/li&gt;
&lt;li&gt;Interface Implementation Inconsistencies&lt;/li&gt;
&lt;li&gt;Coercion Issues&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

What TypeScript Couldn't Fix

&lt;ul&gt;
&lt;li&gt;Logical Errors&lt;/li&gt;
&lt;li&gt;Browser Compatibility Issues&lt;/li&gt;
&lt;li&gt;Memory Management Issues&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

TypeScript Done Wrong

&lt;ul&gt;
&lt;li&gt;Configuration Problems&lt;/li&gt;
&lt;li&gt;Any Type Misuse&lt;/li&gt;
&lt;li&gt;Error Suppression&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Best Practices for TypeScript Migration&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Historical Context of TypeScript in Turbo
&lt;/h2&gt;

&lt;p&gt;DHH stated that "TypeScript just gets in the way for me," and "things that should be easy become hard, and things that are hard become &lt;code&gt;any&lt;/code&gt;." This perspective merits examination in the specific context of Turbo's implementation.&lt;/p&gt;

&lt;p&gt;The commit history indicates that Turbo's previous TypeScript implementation was partial and inconsistent. The codebase exhibited signs of Type-Script-as-retrofit rather than TypeScript-by-design. Specifically, it showed patterns of:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Extensive use of &lt;code&gt;any&lt;/code&gt; types to bypass type checking&lt;/li&gt;
&lt;li&gt;Disabled strict null checks to avoid addressing potential null references&lt;/li&gt;
&lt;li&gt;Inconsistent application of type safety across related components&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What TypeScript Actually Fixed
&lt;/h2&gt;

&lt;p&gt;When properly implemented with strict settings, TypeScript revealed a number of genuine issues in the Turbo codebase:&lt;/p&gt;

&lt;h3&gt;
  
  
  Null References
&lt;/h3&gt;

&lt;p&gt;Null reference errors can cause applications to crash with messages like "Cannot read property 'getAttribute' of null". These issues are particularly dangerous because they can work during testing but fail in production under specific conditions. They result in blank pages, non-functioning controls, or broken navigation for users.&lt;/p&gt;

&lt;p&gt;In frame controllers, element references were frequently accessed without checking for existence. Consider this innocent-looking JavaScript code from the original Turbo implementation:&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;// From commit 9f3aad7: src/core/frames/frame_controller.js&lt;/span&gt;
&lt;span class="nf"&gt;getRootLocation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;meta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ownerDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`meta[name="turbo-root"]`&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;root&lt;/span&gt; &lt;span class="o"&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;content&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;expandURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;root&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;What happens when &lt;code&gt;this.element&lt;/code&gt; is null? JavaScript crashes at runtime with a null reference error. The TypeScript version in ts-turbo makes this safer:&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;// Current implementation in ts-turbo: src/core/frames/frame_controller.ts&lt;/span&gt;
&lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;rootLocation&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;Location&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;element&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;expandURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&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;meta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ownerDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLMetaElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`meta[name="turbo-root"]`&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;root&lt;/span&gt; &lt;span class="o"&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;content&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;expandURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;root&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;When this pattern appears throughout your codebase, users might experience seemingly random errors that would be difficult to reproduce and debug.&lt;/p&gt;

&lt;h3&gt;
  
  
  Interface Implementation Inconsistencies
&lt;/h3&gt;

&lt;p&gt;Inconsistent interface implementations create "ghost bugs" where components work in normal conditions but fail in edge cases. These failures manifest as seemingly random crashes or functionality that works inconsistently. Interface mismatches create problems with missing parameters, unexpected return types, and dependencies on non-existent properties.&lt;/p&gt;

&lt;p&gt;JavaScript's loose nature allowed Turbo components to implement interfaces inconsistently. A particularly dangerous interface implementation issue found during the migration involves the delegate pattern and missing method implementations. Turbo uses a system where &lt;code&gt;FrameElement&lt;/code&gt; has a delegate, but there's no explicit interface defining what methods this delegate must implement.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://github.com/hotwired/turbo/blob/9f3aad7772ba8ef4080538e4e5fb175a8ad550f1/src/core/frames/frame_controller.js#L238-L238" rel="noopener noreferrer"&gt;frame_controller.js (lines 238)&lt;/a&gt;, we see a call to a method that may not exist:&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;// In frame_controller.js - The method is called on the frame's delegate&lt;/span&gt;
&lt;span class="nx"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;delegate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;proposeVisitIfNavigatedWithAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;submitter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Turbo has a delegate pattern where &lt;code&gt;FrameElement&lt;/code&gt; creates its delegate from a constructor class:&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;// In frame_element.js - The delegate is created but with no explicit interface&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FrameElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;delegateConstructor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;  &lt;span class="c1"&gt;// Set elsewhere in the codebase&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&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="nx"&gt;delegate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;FrameElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delegateConstructor&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="c1"&gt;// The only delegate methods actually called are:&lt;/span&gt;
  &lt;span class="nf"&gt;connectedCallback&lt;/span&gt;&lt;span class="p"&gt;()&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="nx"&gt;delegate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;disconnectedCallback&lt;/span&gt;&lt;span class="p"&gt;()&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="nx"&gt;delegate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;attributeChangedCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&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;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;loading&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;delegate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadingStyleChanged&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;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;src&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;delegate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sourceURLChanged&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;delegate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disabledChanged&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;The problem is that in JavaScript, there's no explicit interface requiring &lt;code&gt;proposeVisitIfNavigatedWithAction&lt;/code&gt; to be implemented by whatever class gets assigned to &lt;code&gt;FrameElement.delegateConstructor&lt;/code&gt;. This creates an implicit, undocumented contract between components.&lt;/p&gt;

&lt;p&gt;This is a particularly severe issue that would cause the application to crash with a "method not found" error when a frame navigation occurs. Since the error only happens during specific navigation sequences, it would be difficult to detect during testing. In TypeScript, this problem is solved by requiring explicit interfaces that define exactly what methods must be implemented.&lt;/p&gt;

&lt;h3&gt;
  
  
  Confusing Type Coercion
&lt;/h3&gt;

&lt;p&gt;JavaScript's implicit type coercion creates bugs that appear to work correctly but produce incorrect results under specific conditions. These issues are difficult to debug because they often appear to be environmental or timing-related problems.&lt;/p&gt;

&lt;p&gt;JavaScript's implicit type coercion created subtle bugs that often went unnoticed until they caused production failures. In  &lt;a href="https://github.com/hotwired/turbo/blob/9f3aad7772ba8ef4080538e4e5fb175a8ad550f1/src/core/drive/history.js#L104" rel="noopener noreferrer"&gt;src/core/drive/history.js&lt;/a&gt; we find code that raises questions about intent:&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;// Original JavaScript implementation - implicit type coercion&lt;/span&gt;
&lt;span class="nf"&gt;restore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;position&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;restorationIdentifier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&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="nx"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;
    &lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pushState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// Number comparison with string using == (not ===)&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentIndex&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scrollTo&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="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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The TypeScript implementation in ts-turbo makes the comparison explicit and prevents potential coercion issues:&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;// TypeScript implementation in ts-turbo&lt;/span&gt;
&lt;span class="nf"&gt;restore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &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="nx"&gt;restorationIdentifier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&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="nx"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;
    &lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pushState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// Explicit comparison with proper typing&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentIndex&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scrollTo&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="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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Is this checking for equality or accidentally converting types?&lt;br&gt;
return this.pageLoaded || document.readyState == "complete". This might execute when you don't expect it to due to coercion. This pattern was found throughout the history management module. The deeper issue is more complex than it appears:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Mismatched comparison patterns: Throughout the codebase, comparisons mixed strict equality (&lt;code&gt;===&lt;/code&gt;) and loose equality (&lt;code&gt;==&lt;/code&gt;), creating inconsistent behavior. &lt;code&gt;document.readyState == "complete"&lt;/code&gt; uses loose equality while &lt;code&gt;this.restorationIdentifier === identifier&lt;/code&gt; uses strict equality elsewhere.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;State tracking issues: The history system tracks various types of data: URL paths (strings), restoration identifiers (mixed types), and state objects. When compared with loose equality, URL paths like "/404" could accidentally match numeric identifiers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Browser implementation differences: Browsers handle history state objects inconsistently - some preserve types exactly, others serialize and deserialize (changing types), and Safari might convert numeric strings to numbers in some contexts.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Another example comes from the form submission handling:&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://github.com/hotwired/turbo/blob/9f3aad7772ba8ef4080538e4e5fb175a8ad550f1/src/core/drive/form_submission.js#L52-L55" rel="noopener noreferrer"&gt;form submission handling&lt;/a&gt;, we see problematic implicit conversions:&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;// Actual code from form_submission.js after TypeScript removal (commit 9f3aad7772ba8ef4080538e4e5fb175a8ad550f1)&lt;/span&gt;
&lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;method&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;submitter&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;formmethod&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;formElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;method&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;fetchMethodFromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;FetchMethod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern creates potential issues where an empty string method attribute (which is falsy in JavaScript) would be treated differently than a non-existent method, when both should result in the default method&lt;/p&gt;

&lt;p&gt;This form submission example illustrates multiple coercion challenges that affected real user interactions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Attribute handling inconsistencies: &lt;code&gt;getAttribute()&lt;/code&gt; returns null for non-existent attributes while empty attributes return "". The logical OR (&lt;code&gt;||&lt;/code&gt;) treats both as falsy, but these cases should be handled differently.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Form method determination: Web forms behave differently based on GET vs POST methods, affecting navigation, caching, and data handling. The wrong method can cause data loss or security issues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cascading defaults problem: The chain of OR operations creates complex logic paths where &lt;code&gt;getAttribute()&lt;/code&gt; might return null, undefined, or empty string, each requiring different handling.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;TypeScript's strict checking would have enforced explicit handling of these different cases.&lt;/p&gt;

&lt;p&gt;TypeScript flagged dozens of instances where loose equality checks (&lt;code&gt;==&lt;/code&gt;) were used instead of strict equality (&lt;code&gt;===&lt;/code&gt;), where string values were implicitly converted to numbers, and where empty strings were treated as falsy values.&lt;/p&gt;

&lt;h2&gt;
  
  
  What TypeScript Couldn't Fix
&lt;/h2&gt;

&lt;p&gt;Static typing isn't a silver bullet, and certain categories of bugs remained immune to TypeScript's protection:&lt;/p&gt;

&lt;h3&gt;
  
  
  Logical Errors Persisted
&lt;/h3&gt;

&lt;p&gt;While TypeScript catches type-related errors effectively, it offers little protection against logical errors in algorithm design and control flow. Users might experience inconsistent application state, operations executing in the wrong order, and race conditions. These bugs often work during testing but fail in production, particularly with asynchronous operations where timing becomes critical.&lt;/p&gt;

&lt;p&gt;The migration revealed that TypeScript couldn't prevent flawed algorithms or incorrect control flow. For example, in the stream message renderer implementation, even with TypeScript, logical errors in the processing sequence remained:&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;// From src/core/drive/navigator.ts in the TypeScript codebase&lt;/span&gt;
&lt;span class="nf"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Partial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;VisitOptions&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;navigate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Event&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isHashChangeEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;))&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="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;replace&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;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Event and navigation handling run in separate async paths&lt;/span&gt;
    &lt;span class="c1"&gt;// TypeScript can't detect logical flow problems between these methods&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;linkClicked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getVisitOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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="nf"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;linkClicked&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;linkClicked&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&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 example illustrates a logical flow error that TypeScript cannot detect:&lt;/p&gt;

&lt;p&gt;In Turbo's navigation system, there are multiple asynchronous execution paths:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The navigate() method responds to click events&lt;/li&gt;
&lt;li&gt;getVisitOptions() performs DOM operations (reading data attributes) that are implicitly async&lt;/li&gt;
&lt;li&gt;visit() initiates async operations (network requests, history API calls)&lt;/li&gt;
&lt;li&gt;Multiple event listeners at different levels (window, document, elements) can trigger competing
navigation requests simultaneously&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The race condition occurs because between events, the URL can be modified by other code, multiple clicks create competing navigation requests, and these events execute in different event loop cycles that TypeScript cannot analyze.&lt;/p&gt;

&lt;p&gt;These timing issues led to bugs where users would click a link but end up at a different URL, back/forward navigation would break after rapid interactions, and the browser and Turbo would get into inconsistent states about the "current" URL.&lt;/p&gt;

&lt;p&gt;TypeScript can only validate parameter and return types, but cannot detect timing-related problems across event loops, verify coordination between async execution paths, or ensure state consistency in a multi-event environment.&lt;/p&gt;

&lt;p&gt;These issues required specialized testing (like rapid-click testing) and event sequence analysis during code reviews - things that static type checking cannot provide.&lt;/p&gt;

&lt;h3&gt;
  
  
  Memory Management Still Required Discipline
&lt;/h3&gt;

&lt;p&gt;Memory management issues cause gradual performance degradation, increasing memory consumption, and browser crashes after extended use. Applications become slower over time with no obvious solution except reloading. These problems are especially severe in single-page applications where resources aren't naturally cleaned up by page navigation.&lt;/p&gt;

&lt;p&gt;Some of the most insidious bugs in Turbo involved memory management. Analysis of stream_source_element.js revealed significant issues:&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;// From commit 9f3aad7: src/elements/stream_source_element.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StreamSourceElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;streamSource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;

  &lt;span class="nf"&gt;connectedCallback&lt;/span&gt;&lt;span class="p"&gt;()&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="nx"&gt;streamSource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^ws&lt;/span&gt;&lt;span class="se"&gt;{1,2}&lt;/span&gt;&lt;span class="sr"&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;new&lt;/span&gt; &lt;span class="nc"&gt;WebSocket&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="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EventSource&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="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;connectStreamSource&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="nx"&gt;streamSource&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;disconnectedCallback&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;streamSource&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;disconnectStreamSource&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="nx"&gt;streamSource&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="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;src&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;src&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;TypeScript can't catch memory leaks if disconnect logic is incomplete. The pattern above requires careful tracking of all resources across multiple components.&lt;/p&gt;

&lt;p&gt;To better understand the context, let's look at my TypeScript implementation and several key components involved:&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;// src/elements/stream_source_element.ts (TypeScript implementation)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StreamSourceElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;streamSource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;StreamSource&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;

  &lt;span class="nf"&gt;connectedCallback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Creates either WebSocket or EventSource based on URL pattern&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;streamSource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^ws&lt;/span&gt;&lt;span class="se"&gt;{1,2}&lt;/span&gt;&lt;span class="sr"&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;new&lt;/span&gt; &lt;span class="nc"&gt;WebSocket&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="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EventSource&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="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;connectStreamSource&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="nx"&gt;streamSource&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;disconnectedCallback&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;streamSource&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;disconnectStreamSource&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="nx"&gt;streamSource&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Example from observer pattern in frame_controller.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FrameController&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;LinkClickObserverDelegate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="nx"&gt;AppearanceObserverDelegate&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;FrameElement&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;FormSubmitObserverDelegate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Multiple observers that need to be started/stopped at the right times&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;appearanceObserver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AppearanceObserver&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;FrameElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;formLinkClickObserver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FormLinkClickObserver&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;formSubmitObserver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FormSubmitObserver&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Create observers that register event listeners&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appearanceObserver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AppearanceObserver&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;element&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="nx"&gt;formLinkClickObserver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormLinkClickObserver&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;element&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="nx"&gt;formSubmitObserver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormSubmitObserver&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&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="nx"&gt;appearanceObserver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&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="nx"&gt;formLinkClickObserver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&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="nx"&gt;formSubmitObserver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;()&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="nx"&gt;appearanceObserver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&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="nx"&gt;formLinkClickObserver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&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="nx"&gt;formSubmitObserver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&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 example represents a much larger memory management challenge in Turbo. The deeper context reveals:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Multi-layer resource ownership: The StreamSource element creates controllers that register observers, attach event listeners, and establish network connections. In the FrameController example, three different observer types are instantiated, each requiring proper start/stop coordination.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Disconnection gaps: The disconnectedCallback only calls hostDisconnected(), but that method might not clean up all event listeners, verify closed connections, or handle interruptions during active operations. TypeScript can't enforce proper cleanup at runtime.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cross-component coordination: TypeScript has no way to ensure resources allocated by one component are cleaned up by another, observers are properly stopped, or event listeners are removed correctly. TypeScript can verify methods exist but cannot enforce they're called at the right time or sequence.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Typescript couldn't solve these fundamental design challenges because memory management in web components requires a holistic approach rather than just type checking. The issue wasn't just about having the correct type signatures, but about enforcing a complete resource lifecycle management system.&lt;/p&gt;

&lt;p&gt;Other memory issues included event listeners not being properly removed, circular references preventing garbage collection, and resources not being released after use.&lt;/p&gt;

&lt;p&gt;TypeScript offered little protection against these problems. Memory management in JavaScript requires disciplined coding practices regardless of the type system, and TypeScript's compile-time checks couldn't enforce proper cleanup.&lt;/p&gt;

&lt;h2&gt;
  
  
  TypeScript Done Wrong
&lt;/h2&gt;

&lt;p&gt;A partial, inconsistent TypeScript implementation creates a situation where a codebase incurs all the costs of TypeScript (build complexity, learning curve, type definitions) while receiving few of its benefits. This can create a false sense of security and add friction to development, with team members spending time fighting the type system rather than leveraging it.&lt;/p&gt;

&lt;p&gt;Perhaps the most revealing finding was that Turbo never truly implemented TypeScript correctly in the first place.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuration Shortcomings
&lt;/h3&gt;

&lt;p&gt;The TypeScript configuration used in previous attempts was fundamentally flawed. From tsconfig.json prior to removal in &lt;a href="https://github.com/hotwired/turbo/blob/0826b8152c0e97f19d459c1a1c364fa89cc62829/tsconfig.json" rel="noopener noreferrer"&gt;commit 0826b8152c0e97f19d459c1a1c364fa89cc62829&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"esModuleInterop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lib"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dom"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dom.iterable"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"esnext"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"es2015"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"moduleResolution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"noUnusedLocals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"rootDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"strict"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"es2017"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"noEmit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"removeComments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"skipLibCheck"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"isolatedModules"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exclude"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/tests/fixtures"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"playwright.config.ts"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key issues included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;tsconfig.json&lt;/code&gt; lacked crucial strict settings despite claiming to be "strict"&lt;/li&gt;
&lt;li&gt;Important type checking flags like &lt;code&gt;strictNullChecks&lt;/code&gt; were functionally disabled through type assertions&lt;/li&gt;
&lt;li&gt;The compiler was configured to be permissive rather than strict in practice&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The "Any" Escape Hatch
&lt;/h3&gt;

&lt;p&gt;When TypeScript flagged potential issues, the common solution was to use the &lt;code&gt;any&lt;/code&gt; type to silence the compiler rather than fix the underlying problem:&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;// From stream_message.ts prior to removal&lt;/span&gt;
&lt;span class="c1"&gt;// Instead of properly typing this:&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;processMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Access properties without type checking&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;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Type safety completely bypassed&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 typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// From error_renderer.ts prior to removal&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;renderError&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="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Using any to avoid properly modeling the error hierarchy&lt;/span&gt;
  &lt;span class="k"&gt;return&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;message&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unknown error&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Using any to bypass element type checking&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;processNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Directly accessing element properties without verification&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;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasAttribute&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// This crashes if node is a Text node, not an Element&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 typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Promise error handling with any type&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&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;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Generic error handling that loses error type information&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&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;request&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="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&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;error&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;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// May fail if error isn't an Error object&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 pattern appeared throughout the codebase, effectively neutralizing TypeScript's benefits.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Means for the TypeScript vs. JavaScript Debate
&lt;/h2&gt;

&lt;p&gt;DHH's frustration with TypeScript makes more sense when viewed through this lens. If your experience with TypeScript involves fighting a poorly configured system, inconsistent type enforcement, and partial implementations, then you're getting all of the friction with few of the benefits.&lt;/p&gt;

&lt;p&gt;A lint check on the code right before TypeScript removal revealed 36 instances of the &lt;code&gt;any&lt;/code&gt; type across the codebase. These were found in critical files like history management, rendering, HTTP request handling, and DOM manipulation.&lt;/p&gt;

&lt;p&gt;TypeScript was implemented with significant escape hatches that undermined its safety benefits. Interestingly, while there were 36 instances of &lt;code&gt;any&lt;/code&gt; types, there were no instances of directive comments like &lt;code&gt;@ts-ignore&lt;/code&gt;, &lt;code&gt;@ts-expect-error&lt;/code&gt;, or &lt;code&gt;@ts-nocheck&lt;/code&gt; in the codebase. This suggests that developers were primarily using the &lt;code&gt;any&lt;/code&gt; type as their main escape hatch rather than suppressing specific errors with directives.&lt;/p&gt;

&lt;p&gt;This pattern reveals a TypeScript implementation that was likely using loose configuration settings rather than strict mode, with developers defaulting to &lt;code&gt;any&lt;/code&gt; when encountering typing challenges instead of properly solving type issues. The result was a codebase with all of TypeScript's friction but few of its safety benefits.&lt;/p&gt;

&lt;p&gt;However, this doesn't mean TypeScript itself is flawed. The evidence from the Turbo migration suggests that TypeScript can be valuable when:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It's implemented completely and consistently&lt;/li&gt;
&lt;li&gt;Strict compiler settings are enabled from the start&lt;/li&gt;
&lt;li&gt;Type definitions are treated as design tools, not afterthoughts&lt;/li&gt;
&lt;li&gt;The escape hatches (&lt;code&gt;any&lt;/code&gt;, type assertions) are used sparingly&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Lessons From the Re-Migration
&lt;/h2&gt;

&lt;p&gt;My experience remigrating Turbo to TypeScript (available at &lt;a href="https://github.com/shiftyp/ts-turbo" rel="noopener noreferrer"&gt;github.com/shiftyp/ts-turbo&lt;/a&gt;) has given me a unique perspective on this debate. By applying TypeScript properly—with strict null checks enabled, minimal use of &lt;code&gt;any&lt;/code&gt;, and thorough interface definitions—I was able to uncover and fix numerous issues that had previously gone undetected.&lt;/p&gt;

&lt;p&gt;The re-migration effort revealed several key improvements that TypeScript can bring when implemented correctly:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Explicit Interface Contracts
&lt;/h3&gt;

&lt;p&gt;In the original codebase, interfaces were implicit. Looking at the remigrated &lt;code&gt;session.ts&lt;/code&gt; file, proper interface definitions now explicitly state the contract between components:&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;// From src/core/session.ts in the remigration&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;PageViewDelegate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;allowsImmediateRender&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Element&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RenderOptions&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="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;LinkPrefetchDelegate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;canPrefetchRequestToLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLAnchorElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;URL&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="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Session&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;PageViewDelegate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;LinkPrefetchDelegate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;FrameRedirectorSession&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Implementation now contractually bound to fulfill these interfaces&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern ensures that when one component expects another to have certain capabilities, that contract is verified at compile time rather than failing mysteriously at runtime.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Exhaustive State Handling
&lt;/h3&gt;

&lt;p&gt;Form submissions in Turbo can exist in various states. The re-migration properly types these states using TypeScript's const assertions:&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;// From src/core/drive/form_submission.ts in the remigration&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;FormSubmissionState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;initialized&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;initialized&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;requesting&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;requesting&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;waiting&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;waiting&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;receiving&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;receiving&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;stopping&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stopping&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;stopped&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stopped&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;FormSubmissionStateType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;FormSubmissionState&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;FormSubmissionState&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern ensures that state transitions are checked exhaustively, preventing invalid states that could lead to runtime errors.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Browser Compatibility Safeguards
&lt;/h3&gt;

&lt;p&gt;In the remigration, TypeScript forces explicit checks:&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;// From src/core/drive/history.ts in the remigration&lt;/span&gt;
&lt;span class="nf"&gt;assumeControlOfScrollRestoration&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scrollRestoration&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;)&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="nx"&gt;previousScrollRestoration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollRestoration&lt;/span&gt;
    &lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollRestoration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;manual&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;By making these checks explicit in the type system, it's impossible to accidentally access APIs that might not exist in all browsers without first verifying their presence.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Form Data Safety
&lt;/h3&gt;

&lt;p&gt;There were several issues with null references when processing form data. The remigration adds proper null checking:&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;// From src/core/drive/form_submission.ts in the remigration&lt;/span&gt;
&lt;span class="nf"&gt;buildFormData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formElement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLFormElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;submitter&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;FormData&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;URLSearchParams&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;formData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formElement&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;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;submitter&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;submitter&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;value&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;name&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern of optional chaining (&lt;code&gt;?.&lt;/code&gt;) combined with explicit null checks ensures that form processing is robust against missing data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The Turbo TypeScript migration offers a nuanced perspective on the JavaScript vs. TypeScript debate. TypeScript isn't inherently good or bad—its value depends entirely on implementation. The remigration efforts have tangibly demonstrated both the costs and benefits of TypeScript when properly applied.&lt;/p&gt;

&lt;p&gt;The question isn't "Should you use TypeScript or JavaScript?" but rather "Are you willing to implement TypeScript properly?" If you're going to disable strict null checks, use &lt;code&gt;any&lt;/code&gt; types liberally, and only partially convert your codebase, you might be better off with JavaScript's simplicity.&lt;/p&gt;

&lt;p&gt;DHH's announcement about dropping TypeScript from Turbo 8 might be less about TypeScript itself and more about recognizing that Turbo never gave TypeScript a fair chance to begin with. The history of previous attempts shows a pattern of half-measures and workarounds rather than embracing what TypeScript had to offer.&lt;/p&gt;

&lt;p&gt;My re-migration project took a different approach. Rather than treating TypeScript as an afterthought or a simple type annotation layer, I integrated it as a fundamental design tool. By defining precise interfaces first and then implementing against those interfaces, TypeScript became a collaborator in the development process rather than an obstacle to be worked around. The result was a more robust codebase with fewer edge cases and more predictable behavior.&lt;/p&gt;

&lt;p&gt;What's particularly revealing is how many issues were resolved not by adding complex type annotations, but by the simple act of forcing explicit handling of edge cases. For instance, many potential errors were eliminated by adding a few strategic null checks that TypeScript required but JavaScript didn't enforce.&lt;/p&gt;

&lt;p&gt;Perhaps the right move isn't abandoning TypeScript, but implementing it correctly. Or perhaps, as DHH suggests, JavaScript itself has evolved enough that for some teams and projects, the additional safety net of TypeScript simply isn't worth the friction it introduces.&lt;/p&gt;

&lt;p&gt;What's your experience? Has TypeScript saved your project from critical bugs, or has it primarily introduced friction? The debate continues, but hopefully with more nuance and less zealotry than before.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Code Mind, Beginners Mind</title>
      <dc:creator>Ryan Kahn</dc:creator>
      <pubDate>Tue, 25 Mar 2025 01:18:17 +0000</pubDate>
      <link>https://dev.to/shiftyp/code-mind-beginners-mind-5d3d</link>
      <guid>https://dev.to/shiftyp/code-mind-beginners-mind-5d3d</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;“If your mind is empty, it is always ready for anything; it is open to everything. In the beginner’s mind there are many possibilities; in the expert’s mind there are few.”&lt;br&gt;&lt;br&gt;
— Shunryu Suzuki&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Software development is an engineering discipline, a technical craft, and an art. It can also be a form of meditation, and in the style of zen practice, it can also be zen practice. Those who have sat with code long enough—through the long nights, through bugs, through flow states where time dissolves—know that writing software is as much about the mind as it is about the machine.&lt;/p&gt;

&lt;p&gt;A calm, open, beginner’s mind is the most powerful tool a developer can cultivate. To write good code, it helps to let go of ego, of attachment, of the illusion of mastery. We are not building only systems—we are building awareness.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;I. Syntax and Emptiness&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;When we first learn to program, everything is an obstacle. Syntax errors are indecipherable, types are byzantine, indentation and symbols are in a foreign tongue. Our minds are full of questions and doubt. And yet, in that state of confusion, there is a sense clarity and balance if we look for it. We are present. We are paying attention.&lt;/p&gt;

&lt;p&gt;A more experienced developer may work in smoother fashion. They no longer think about where semicolons go, or how to declare a variable. But this fluency can be an impediment as well. They may assume understanding where there is none. They stop asking why.&lt;/p&gt;

&lt;p&gt;Both stages have value (even if the salary doesn't reflect the value of a beginner), but it is the beginner’s curiosity that must be preserved.&lt;/p&gt;

&lt;p&gt;When you can see syntax, look for structure. When you no longer think about loops and conditionals, think about clarity, intent, and flow. You must see through the code to the mind that wrote it and the mind that will read it. You must read code like a poem, not just a plan. A helpful note from all the developers who have come before and will come after, a lineage of knowledge.&lt;/p&gt;

&lt;p&gt;In this way, code isn't code, its a product of countless minds, a thread in a fabric of existence, stretching from before computers to the present, into future technologies. From a Buddhist lens we might say it's empty of its own existence, and exists only in concert with the minds that envision it, the computers that run it, and the world that created it.&lt;/p&gt;

&lt;p&gt;Seeing code this way may seem abstract, but if you really try you may find that its the most useful way to view it. It frees you of attachment to a particular commit, a particular way of doing things. Knowing that today's innovation is tomorrows legacy. It frees us to work, and create and iterate seeing each commit as a fresh start and at the same time nothing special.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;II. The Simplicity of Understanding&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The beginners focus is on making things work. This is everyone's focus of course (we spend 99.9% of our time in a broken state after all), but for the beginner the search lacks focus. We pile on logic, conditions, flags. Our code is often verbose. This is not wrong. It is part of the path. But over time, we develop simplicity—not the kind that comes from cleverness, but the kind that comes from understanding.&lt;/p&gt;

&lt;p&gt;When we deeply understand a problem, our search is more focused, and we write less code. We remove more than we add. We avoid the temptation of premature abstraction. We leave helpful hooks, not voluminous frameworks. We write just enough and then stop.&lt;/p&gt;

&lt;p&gt;This is difficult. Our culture rewards complexity. Job titles grow with the number of systems a person has touched. But in zen, as in code, the simplest solution is a sign of understanding. Simplicity is not a lack of ambition—it is a sign of maturity.&lt;/p&gt;

&lt;p&gt;Write code as if you will come back to it in a year, tired and under pressure. Will you understand what it does? Will it still feel obvious? If not, there's an opportunity to make it simpler.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;III. Version Control and Non-Attachment&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Git is a wonderful tool. It can be maddening, but when used artfully and mindfully it gives us an opportunity to practice non-attachment. We write, we commit, we rewrite. We branch and merge. We make mistakes and revert. We learn to let go of what we wrote yesterday.&lt;/p&gt;

&lt;p&gt;Code is not permanent. Systems evolve. Files disappear. Sometimes, entire architectures are thrown away in favor of something simpler, more modern, or more correct to the current problems.&lt;/p&gt;

&lt;p&gt;To a beginner this might seem scary, difficult to swallow. When we've put our hearts into our work we may clutch code and the ideas behind it tightly, hoping it will be judged well, hoping it will stay, and we will stay. But a mature developer will take a different tact. Good code must be willing to die. It serves its purpose, and then it fades.&lt;/p&gt;

&lt;p&gt;Don’t be precious about your solutions. Don’t resist a better one. Don’t argue for something just because you wrote it.&lt;/p&gt;

&lt;p&gt;You are not your code.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;IV. Bugs as Teachers&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Bugs are not a sign of imbalance. A computer can never be out balance actually. If a cosmic ray flips a bit, that cascade of failures is the new balance. If you unplug it, its empty, and balanced. When things appear chaotic, this is an opportunity to learn. Every bug is a mirror. It shows us how we misunderstood the system, how we made assumptions, how we moved quickly.&lt;/p&gt;

&lt;p&gt;To debug is not just to fix. It is to sit, to watch, to question. It is a meditation. You stare at the screen, not in frustration, but in patience. You trace the paths. You follow the data. You look for what you missed.&lt;/p&gt;

&lt;p&gt;The bug does not lie. It does not hide. It is there, waiting to be seen. Most of the time, it's our perception of a solution that's clouded. We attach to the same path, checking the same solutions to see if "this time" they'll work. Sometimes magic happens, other times it takes time.&lt;/p&gt;

&lt;p&gt;Sometimes, we debug in a frenzy. We change things randomly. We panic. But this generally pushes a solution farther away. It waits until we are calm again.&lt;/p&gt;

&lt;p&gt;When we are still, when we go for a walk, or eat an orange, we see.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;V. Architecture and the Space Between&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A good system has room to breathe. It has space for change, for growth. It does not predict every requirement. It is not overbuilt. Like a mind with open doors, it invites what is needed, and lets go of what is not.&lt;/p&gt;

&lt;p&gt;When we begin we might build with fear. Wanting to account for every possibility. Adding ideas no one has asked for. Creating abstractions in case “we need to change this later.”&lt;/p&gt;

&lt;p&gt;But abstraction is not preparation. It is debt. Build the thing you know you need. Do it cleanly. When the need arises to generalize, you will know.&lt;/p&gt;

&lt;p&gt;Systems emerge. They are not planned into existence. They evolve with attention and care.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;VI. Collaboration and the Ego&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;When we code alone, we are gods in our own tiny worlds. But when we code in teams, we are students again.&lt;/p&gt;

&lt;p&gt;In the beginning its natural to feel unsure in a team. We fear being wrong, we feel the perception of failure and the judgment of our peers. But the wise developer can adopt the stance of knowing they're wrong, knowing they don't have the full picture and that a team can correct and expand the vision of each member.&lt;/p&gt;

&lt;p&gt;In pair programming, and code review, the best interactions feel like a conversation between equals, even if one person has a clearer vision of the problem. The joy comes not from proving knowledge, but from discovering something together.&lt;/p&gt;

&lt;p&gt;In code reviews, we must be gentle. We must ask, not demand. Suggest, not declare. Teach, but also be taught.&lt;/p&gt;

&lt;p&gt;The ego is the enemy of learning. The moment you think you are beyond beginner’s mind, you stop growing.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;VII. Refactoring and Renewal&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;We don't make mistakes. We make one continuous mistake, and then we refactor to create a better mistake. Refactoring deepens our understanding. The first version of any system is like a sketch—useful, but incomplete. As we work with it, we see new shapes. We learn its rhythms.&lt;/p&gt;

&lt;p&gt;To refactor is to clean the temple. It is not necessarily glamorous. It is necessary. It might even be fun.&lt;/p&gt;

&lt;p&gt;We remove duplication because it hides patterns. We rename variables for clarity. We split functions to clear out confusion for that big brain that is the current and future team.&lt;/p&gt;

&lt;p&gt;A well-factored codebase feels light. You want to work in it. You trust it.&lt;/p&gt;

&lt;p&gt;This is not achieved once. It is a practice. Like tending a garden.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;VIII. Always Returning&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Every system will break. Every technique will grow old. Every library will be deprecated. In this way, software humbles us. It reminds us that we are not building forever. We are building &lt;em&gt;for now&lt;/em&gt;. And we are part of that forever.&lt;/p&gt;

&lt;p&gt;Beginner’s mind is not naivety. It is the courage to return. Again and again. To look at the same problem as if it were new. To let go of dogma. To let go of pride. To be curious again.&lt;/p&gt;

&lt;p&gt;The code will always change. The tools will evolve. But the practice remains.&lt;/p&gt;

&lt;p&gt;We come to the editor. We breathe. We write.&lt;/p&gt;

&lt;p&gt;We begin again, awaiting input.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;~# _&lt;/code&gt;&lt;/p&gt;

</description>
      <category>learning</category>
      <category>programming</category>
      <category>productivity</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Backbone for React Devs: The Song Remains the Same</title>
      <dc:creator>Ryan Kahn</dc:creator>
      <pubDate>Mon, 03 Apr 2023 16:15:37 +0000</pubDate>
      <link>https://dev.to/shiftyp/backbone-for-react-devs-the-song-remains-the-same-5hi2</link>
      <guid>https://dev.to/shiftyp/backbone-for-react-devs-the-song-remains-the-same-5hi2</guid>
      <description>&lt;h2&gt;
  
  
  Why should you care?
&lt;/h2&gt;

&lt;p&gt;Maybe you shouldn't! However, if you have to work in Backbone and come from a React background, or if you're interested in some historical changes in the world of the web, read on! Even if you only care about React or modern DOM diffing frameworks, this comparison will give you a new perspective. So what is the difference between Backbone and React?&lt;/p&gt;

&lt;h2&gt;
  
  
  Imperative vs Declarative
&lt;/h2&gt;

&lt;p&gt;The primary difference between React and Backbone is that React uses the Declarative (and "Reactive") way to define mutations or changes in a webpage, where as Backbone uses the Imperative way. Consider HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;The title!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Some &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bold"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;content!&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;HTML declares elements on the page, and sets an initial state and content for the page. It says what things are, and where the go, but does not (without a script block) define any interactions or changes to the page.&lt;/p&gt;

&lt;p&gt;React is like this too, only it defines a cycle between a State, or values that change, and translates that through functions to a declarative snapshot of what the page looks like in reaction to that change (what you see in JSX). React looks at changes in the snapshots and through that diff mutates DOM elements. Sort of like a diff in git, "Add this Line", "Remove this Line", only its "Add this class", "Remove this element" ect... So in the end you declare the page, and React modifies it imperatively.&lt;/p&gt;

&lt;p&gt;Backbone takes an older approach prior to DOM diffing frameworks appearance on the scene. Back then, JQuery was the hot library, and essentially it would do the same thing React does in the end. "Add this class" or "Remove this class". The approach is imperative directly, as in you select elements and mutate the DOM.&lt;/p&gt;

&lt;h2&gt;
  
  
  State and Components vs MVC
&lt;/h2&gt;

&lt;p&gt;In React, you have state, and components. These fit the data flow and declarative style, where you're constantly translating state into DOM snapshots through nested function calls. Backbone has Models, Views. and Controllers. Because it's imperative, it needs to take an Object Oriented as opposed to a Functional approach. You need long lived references to DOM elements, the View objects, so that you can change them over time. The data flow isn't one way like React, so you need persistent models that handle the state of the program, and trigger changes in the Views through subscriptions (this is sort of like the reactivity in React). Then there are Controllers, that handle the business logic of the program. In React these kinds of changes would live mostly in effects with React hooks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Navigation
&lt;/h2&gt;

&lt;p&gt;Backbone can seem a bit like a mess compared to the clean functional programming of React. Persistent objects messaging each other willy nilly, mutating the DOM in a complex fashion...is less comprehensible than the one way data flow and declarative nature of React. And this is true! It is a bit of a mess! The key to comprehending it is to trace dependencies, just like you would look at nested components in React. Start with the DOM mutation, or model update that you care about. Then trace the function calls, the subscriptions to the model, or the other way from the view with the updates that lead to the change...follow this around and you'll be able to modify these interactions safely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I am a strong believer that nothing really changes in software development. In the end we're doing the same thing programmers did on the &lt;a href="https://en.wikipedia.org/wiki/ENIAC" rel="noopener noreferrer"&gt;ENIAC&lt;/a&gt; machine, translating input to output through commands and switches. In that same vein, web development hasn't really changed fundamentally between the Backbone years and the React years. We're still declaring the DOM in HTML, mutating it through JavaScript commands.&lt;/p&gt;

&lt;p&gt;What has changed is how we divvy up these actions, how we generate the commands, and the resulting power and developer experience that gives us a bigger lever to move those bits around. Keep this in mind as you compare any two frameworks or programs, or languages, or any abstraction that we use in programming. There is nothing all that new fundamentally, and at the same time what does change is the power we have as developers to take in complex inputs and create ever more complex outputs.&lt;/p&gt;

&lt;p&gt;Best of luck, and long live the web! &lt;/p&gt;

</description>
      <category>react</category>
      <category>backbone</category>
      <category>legacy</category>
      <category>webdev</category>
    </item>
    <item>
      <title>React Server Components Without a Framework</title>
      <dc:creator>Ryan Kahn</dc:creator>
      <pubDate>Wed, 29 Mar 2023 16:15:03 +0000</pubDate>
      <link>https://dev.to/shiftyp/react-server-components-without-a-framework-2jpf</link>
      <guid>https://dev.to/shiftyp/react-server-components-without-a-framework-2jpf</guid>
      <description>&lt;h2&gt;
  
  
  Server Components are Cool! Frameworks may or may not be.
&lt;/h2&gt;

&lt;p&gt;I, along with many ReactJS devs, have been following the development of the "new" &lt;a href="https://react.dev/blog/2020/12/21/data-fetching-with-react-server-components" rel="noopener noreferrer"&gt;React Server Components API&lt;/a&gt; with excitement. It solves some common problems in React around data fetching and efficiency in client side applications. If you're interested in learning more about the API I'd recommend you listen to the recent &lt;a href="https://jsparty.fm/267" rel="noopener noreferrer"&gt;JS Party podcast with Dan Abramov and Joe Savona from the React Team&lt;/a&gt; on the future of React, and then check out the &lt;a href="https://github.com/reactjs/server-components-demo" rel="noopener noreferrer"&gt;React Server Components Demo&lt;/a&gt; on GitHub.&lt;/p&gt;

&lt;p&gt;One aspect of the planned Server Components API that I personally dislike is the reasoning from the core team that in order to make use of them, you &lt;em&gt;should&lt;/em&gt; utilize a framework like Next or Remix. If you're already using one of these, then great! You will get this feature in time. But, if you're like me and would rather assemble right size tools on your own, or you have a large application you'd rather not completely refactor, this leaves something to be desired. I want there to be a way to use Server Components without a framework.&lt;/p&gt;

&lt;p&gt;With this in mind, I've started building a compiler of sorts on top of &lt;a href="https://ts-morph.com/" rel="noopener noreferrer"&gt;TS-Morph&lt;/a&gt; that will allow you to write your client and server components together, and split them into two bundles automatically for deployment to your server of choice and client application. Ideally, this would work with both Typescript and JavaScript codebases. So far it does, but as I get deeper I can't make too many promises on this feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  A tool for creating Server Components without a Framework
&lt;/h2&gt;

&lt;p&gt;The emphasis of this tool would be to minimize both code and configuration, while introducing the least new dependencies into a code base. This starts with how you write your components. While the plan in Next, for example, is for developers to identify server and client components explicitly, their documentation points to a straightforward set of rules for how to divvy them up:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What do you need to do?&lt;/th&gt;
&lt;th&gt;Server Component&lt;/th&gt;
&lt;th&gt;Client Component&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fetch data.&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;⚠️&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Access backend resources (directly)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Keep sensitive information on the server (access tokens, API keys, etc)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Keep large dependencies on the server / Reduce client-side JavaScript&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Add interactivity and event listeners (onClick(), onChange(), etc)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use State and Lifecycle Effects (useState(), useReducer(), useEffect(), etc)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use browser-only APIs&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use custom hooks that depend on state, effects, or browser-only APIs&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use React Class components&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;p&gt;With these rules as a guide, it should be possible to separate server and client components into categories automatically based on their dependencies. In fact, I've already completed some of this work which you can check out on &lt;a href="https://github.com/shiftyp/react-component-compiler" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. The remainder of the work is more testing and implementation around third party dependencies, then creating and compiling the server and client bundles.&lt;/p&gt;

&lt;p&gt;This would be exposed as a command line tool, as well as a Node API, to allow developers to integrate this optimization step into their build processes without adding additional dependencies outside of the compiler, some types if you're not using Typescript already, and some new React core packages for the client and server coordination.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's "Next" and how you can help
&lt;/h2&gt;

&lt;p&gt;At the very least, this would provide a way for all React devs to try out server components without the overhead of learning and implementing applications in a heavy framework. At the very best, it would allow application developers to integrate server components in a lightweight and seamless fashion into new and existing codebases. Overall I'm excited about the possibilities. Are you?&lt;/p&gt;

&lt;p&gt;If so, I'd love ideas and contributions! I'd be happy to chat about:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Would you use this tool?&lt;/li&gt;
&lt;li&gt;If so, or if not, what would make it a better fit?&lt;/li&gt;
&lt;li&gt;What might you do to contribute to this open source project?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let me know in the comments, I'd love to chat and work with y'all!  &lt;/p&gt;

</description>
      <category>react</category>
      <category>typescript</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Space Themed Site with Mini Game!</title>
      <dc:creator>Ryan Kahn</dc:creator>
      <pubDate>Sat, 19 Dec 2020 04:11:11 +0000</pubDate>
      <link>https://dev.to/shiftyp/space-themed-site-with-mini-game-1bc4</link>
      <guid>https://dev.to/shiftyp/space-themed-site-with-mini-game-1bc4</guid>
      <description>&lt;h2&gt;
  
  
  What I built
&lt;/h2&gt;

&lt;p&gt;Hi all! For this hackathon (and for myself) I've built a space themed site for my work as an engineering mentor and trainer, with a collaborative mini-game! 🚀&lt;/p&gt;

&lt;h3&gt;
  
  
  Category Submission:
&lt;/h3&gt;

&lt;p&gt;This falls under the personal site category!&lt;/p&gt;

&lt;h3&gt;
  
  
  App Link
&lt;/h3&gt;

&lt;p&gt;You can find the application here at &lt;a href="https://rkahn.dev" rel="noopener noreferrer"&gt;https://rkahn.dev&lt;/a&gt;!&lt;/p&gt;

&lt;h3&gt;
  
  
  Screenshots
&lt;/h3&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%2Fi%2Foimoqzbf5hebvhpqeaub.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Foimoqzbf5hebvhpqeaub.gif" alt="App Screen Capture" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Description
&lt;/h3&gt;

&lt;p&gt;This site showcases some aspects and reviews of my work, as well as some personal information and thoughts. It also contains an astronaut themed mini-game. Launch an astronaut, and everyone can see it for a time!&lt;/p&gt;

&lt;h3&gt;
  
  
  Link to Source Code
&lt;/h3&gt;

&lt;p&gt;The static site is built from &lt;a href="https://github.com/shiftyp/rkahn.dev" rel="noopener noreferrer"&gt;this&lt;/a&gt; repo, and the golang / docker backend from &lt;a href="https://github.com/shiftyp/add-astronaut" rel="noopener noreferrer"&gt;this&lt;/a&gt; repo!&lt;/p&gt;

&lt;h3&gt;
  
  
  Permissive License
&lt;/h3&gt;

&lt;p&gt;The code is all released under an MIT license. Feel free to remix it for your own purposes. The imagery on the site is not included in the repository, and is either copyrighted by myself or under the proprietary image license included in the static repo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;I've been in need of a personal site for some time, but I haven't been inspired to come up with a design. I came across these space graphics a while back, and I've been meaning to incorporate them into a personal project. My personal site turned into a perfect opportunity!&lt;/p&gt;

&lt;h3&gt;
  
  
  How I built it
&lt;/h3&gt;

&lt;p&gt;The static site and the backend are both built and deployed on Digital Ocean Apps. The images are separately hosted on a Digital Ocean Spaces CDN. The images are premium content from freepik.com and flaticon.com. I've purchased a license to them, so please don't use them without permission of their respective owners. &lt;/p&gt;

&lt;p&gt;I tried to keep the code approachable, so the majority of the Frontend is built using just HTML and CSS and compiled with &lt;a href="https://parceljs.org/" rel="noopener noreferrer"&gt;Parcel&lt;/a&gt;. The astronaut minigame is built using &lt;a href="https://preactjs.com/" rel="noopener noreferrer"&gt;Preact&lt;/a&gt; with hooks!&lt;/p&gt;

&lt;p&gt;The backend is built with &lt;a href="https://golang.org/" rel="noopener noreferrer"&gt;Golang&lt;/a&gt;, mainly because this is what the Digital Ocean Docker example started as, and it seemed like a good idea! It stores all the data in memory and doesn't utilize a database. Go ahead and take a look! (see what I did there)&lt;/p&gt;

&lt;p&gt;I had wanted to add an analytics server that used the Apps Postgres database component...but I ran into various issues involving SSL and decided to throw my hands in the air and use Segment. 🤷‍♂️&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional Resources/Info
&lt;/h3&gt;

&lt;p&gt;I really enjoyed using Digital Ocean Apps, it made the simpler things (for me) straightforward. I ran into some issues as stated with using the database component, but overall I had a great experience, and look forward to keeping the site on DO Apps for the foreseeable future!&lt;/p&gt;

</description>
      <category>dohackathon</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Introducing Object Hooks: Mutable State for React 🤯</title>
      <dc:creator>Ryan Kahn</dc:creator>
      <pubDate>Tue, 21 Apr 2020 14:21:11 +0000</pubDate>
      <link>https://dev.to/shiftyp/introducing-object-hooks-mutable-state-for-react-56db</link>
      <guid>https://dev.to/shiftyp/introducing-object-hooks-mutable-state-for-react-56db</guid>
      <description>&lt;p&gt;The idea I started with is: What if we could write state changes in react as mutations on a persistent reference? 🤔 So I &lt;a href="https://github.com/shiftyp/object-hooks-test" rel="noopener noreferrer"&gt;wrote the code&lt;/a&gt; to make it work! The basic concept is summed up in the code from the banner image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&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;useObject&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="s1"&gt;../hooks/useObject&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useObject&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
      &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;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 this case, state is a persistent reference that you can mutate (&lt;code&gt;state.count++&lt;/code&gt;). The consequences of this change are fairly interesting. For one, you can create these objects with a class! So the above example could be rewritten as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&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;useInstance&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="s1"&gt;../hooks/useInstance&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&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="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;()&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="nx"&gt;count&lt;/span&gt;&lt;span class="o"&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useInstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Counter&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
      &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;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;Also, if you need to subscribe to to changes in the objects outside of a component function, they implement the &lt;code&gt;AsyncIterable&lt;/code&gt; interface, so you can &lt;code&gt;await&lt;/code&gt; their next state, or &lt;code&gt;for await&lt;/code&gt; future states in an &lt;code&gt;async&lt;/code&gt; function. Here's a logger class that subscribes to a counter!&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;class&lt;/span&gt; &lt;span class="nc"&gt;CounterLogger&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Counter&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;AsyncIterable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Counter&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="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="s2"&gt;`Count is: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;async&lt;/span&gt; &lt;span class="nf"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;()&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="nf"&gt;log&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="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;await &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;)&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&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;There's more to it, and there will be more as the project progresses. I wanted to share what I have in its current "state" 😉 to give everyone a chance to contribute their thoughts and ideas! If you're interested, &lt;a href="https://github.com/shiftyp/object-hooks-test" rel="noopener noreferrer"&gt;clone the repo&lt;/a&gt; and try it for yourself! Or leave a comment or question on this post! All are contributions, all are welcome. 🔰&lt;/p&gt;

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