<?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: Alvin Bellero</title>
    <description>The latest articles on DEV Community by Alvin Bellero (@shiftescape).</description>
    <link>https://dev.to/shiftescape</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%2F1022348%2F251be80f-6894-4b18-a52e-3c815c89bd37.png</url>
      <title>DEV Community: Alvin Bellero</title>
      <link>https://dev.to/shiftescape</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shiftescape"/>
    <language>en</language>
    <item>
      <title>📦 I Built 3 Astro Integrations and Shipped Them to npm — Here's What They Do</title>
      <dc:creator>Alvin Bellero</dc:creator>
      <pubDate>Sat, 28 Mar 2026 02:23:32 +0000</pubDate>
      <link>https://dev.to/shiftescape/i-built-3-astro-integrations-and-shipped-them-to-npm-heres-what-they-do-1873</link>
      <guid>https://dev.to/shiftescape/i-built-3-astro-integrations-and-shipped-them-to-npm-heres-what-they-do-1873</guid>
      <description>&lt;p&gt;I've been building with Astro for a while now, and the same pain points kept showing up across projects. I got tired of patching around them and just built integrations instead.&lt;/p&gt;

&lt;p&gt;Three packages. All on npm. All open source.&lt;/p&gt;

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




&lt;h2&gt;
  
  
  🎯 What's an Astro Integration, anyway?
&lt;/h2&gt;

&lt;p&gt;If you've never built one, an &lt;a href="https://docs.astro.build/en/reference/integrations-reference/" rel="noopener noreferrer"&gt;Astro Integration&lt;/a&gt; is a package that hooks into Astro's build lifecycle or dev toolbar. You return a &lt;code&gt;name&lt;/code&gt; and a &lt;code&gt;hooks&lt;/code&gt; object, and Astro calls your callbacks at the right moments.&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="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;myIntegration&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;AstroIntegration&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-integration&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;hooks&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;astro:build:done&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;pages&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// runs after build&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;That's basically it. It's how &lt;code&gt;@astrojs/react&lt;/code&gt;, Tailwind, Partytown, and most of the ecosystem slot into your config without you wiring anything up yourself.&lt;/p&gt;




&lt;h2&gt;
  
  
  📦 &lt;code&gt;@shiftescape/astro-bundle-budget&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;You build your site, ship it, and two weeks later someone notices a page is pulling in 400KB of JS. No warnings. No CI failure. Just a slow page and a mild sense of shame.&lt;/p&gt;

&lt;p&gt;This integration hooks into &lt;code&gt;astro:build:done&lt;/code&gt;, reads the output, and compares your JS/CSS sizes against budgets you set. Exceed them? Build fails.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;bundleBudget&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;@shiftescape/astro-bundle-budget&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="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;integrations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;bundleBudget&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;budgets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;budget&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;  &lt;span class="c1"&gt;// 100KB JS max&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;budget&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;   &lt;span class="c1"&gt;// 50KB CSS max&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;Set it once, forget about it. The pipeline tells you when something bloats.&lt;/p&gt;

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

&lt;p&gt;📥 &lt;a href="https://www.npmjs.com/package/@shiftescape/astro-bundle-budget" rel="noopener noreferrer"&gt;npm&lt;/a&gt; · &lt;a href="https://github.com/shiftEscape/astro-integrations/tree/main/packages/astro-bundle-budget" rel="noopener noreferrer"&gt;source&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🔍 &lt;code&gt;@shiftescape/astro-env-inspector&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;You've got env vars spread across &lt;code&gt;.env&lt;/code&gt;, &lt;code&gt;.env.local&lt;/code&gt;, &lt;code&gt;.env.production&lt;/code&gt;. You're debugging something weird and you genuinely don't know which value Astro is actually reading or if the variable is even loaded at all.&lt;/p&gt;

&lt;p&gt;This adds a panel to the Astro Dev Toolbar. All your env vars, grouped, masked by default, searchable. Dev only and nothing touches your production build.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;envInspector&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;@shiftescape/astro-env-inspector&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="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;integrations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;envInspector&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;Beats staring at &lt;code&gt;console.log(import.meta.env)&lt;/code&gt; output and trying to remember which file overrides which.&lt;/p&gt;

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

&lt;p&gt;📥 &lt;a href="https://www.npmjs.com/package/@shiftescape/astro-env-inspector" rel="noopener noreferrer"&gt;npm&lt;/a&gt; · &lt;a href="https://github.com/shiftEscape/astro-integrations/tree/main/packages/astro-env-inspector" rel="noopener noreferrer"&gt;source&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🗺️ &lt;code&gt;@shiftescape/astro-toolbar-routes&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Once a project gets past a dozen pages, you're either memorizing routes or digging through &lt;code&gt;src/pages/&lt;/code&gt; every time. Neither is fun.&lt;/p&gt;

&lt;p&gt;This puts a route map in the dev toolbar. Every page in your project, grouped, searchable, clickable. It reads your file-based routes at build time and lists them out.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;toolbarRoutes&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;@shiftescape/astro-toolbar-routes&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="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;integrations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;toolbarRoutes&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;Click a route, go there. That's the whole thing.&lt;/p&gt;

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

&lt;p&gt;📥 &lt;a href="https://www.npmjs.com/package/@shiftescape/astro-toolbar-routes" rel="noopener noreferrer"&gt;npm&lt;/a&gt; · &lt;a href="https://github.com/shiftEscape/astro-integrations/tree/main/packages/astro-toolbar-routes" rel="noopener noreferrer"&gt;source&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Using all three together
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @shiftescape/astro-bundle-budget @shiftescape/astro-env-inspector @shiftescape/astro-toolbar-routes
&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;// astro.config.mjs&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;defineConfig&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;astro/config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;bundleBudget&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;@shiftescape/astro-bundle-budget&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;envInspector&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;@shiftescape/astro-env-inspector&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;toolbarRoutes&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;@shiftescape/astro-toolbar-routes&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="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;integrations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;bundleBudget&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;budgets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;budget&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="nf"&gt;envInspector&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nf"&gt;toolbarRoutes&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 toolbar panels are dev-only. The budget check runs at build time and exits non-zero on failure, so CI picks it up without any extra config.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Learned Building These
&lt;/h2&gt;

&lt;p&gt;A few things stood out while building these integrations:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Astro Integration API is genuinely pleasant to work with.&lt;/strong&gt; The lifecycle hooks are well-named and the docs are thorough. The hardest part is usually understanding exactly which hook fires when but after building a couple integrations you get a feel for it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Dev Toolbar API opens up a lot.&lt;/strong&gt; Being able to inject custom UI panels into the development browser experience is really powerful. Two of these three integrations live in the toolbar, and the API for building toolbar apps is surprisingly straightforward.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monorepo setup with npm workspaces is worth the upfront investment.&lt;/strong&gt; Having a single &lt;code&gt;demo/&lt;/code&gt; project that uses all three integrations locally made testing so much easier. I can build a package, start the demo, and see everything working together without publishing first.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;All three are at &lt;code&gt;v0.1.x&lt;/code&gt; — functional and tested, but still early. There's a lot more I want to do with each of them&lt;/p&gt;

&lt;p&gt;If any of these save you some time, a ⭐ on &lt;a href="https://github.com/shiftEscape/astro-integrations" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; goes a long way!&lt;/p&gt;

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




&lt;p&gt;&lt;em&gt;Built with the &lt;a href="https://docs.astro.build/en/reference/integrations-reference/" rel="noopener noreferrer"&gt;Astro Integration API&lt;/a&gt; and listed in the &lt;a href="https://astro.build/integrations/?search=%40shiftescape" rel="noopener noreferrer"&gt;Astro Integrations Library&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>astro</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>opensource</category>
    </item>
    <item>
      <title>🌟 Extensions Dump: Key Tracer!</title>
      <dc:creator>Alvin Bellero</dc:creator>
      <pubDate>Mon, 09 Mar 2026 00:15:46 +0000</pubDate>
      <link>https://dev.to/shiftescape/extensions-dump-key-tracer-4dof</link>
      <guid>https://dev.to/shiftescape/extensions-dump-key-tracer-4dof</guid>
      <description>&lt;p&gt;Hey there, developers! 👋&lt;/p&gt;

&lt;p&gt;Back with another &lt;strong&gt;Extensions Dump&lt;/strong&gt; — where I spotlight the VSCode extensions I have been building and why they exist in the first place.&lt;/p&gt;

&lt;p&gt;This one is a throwback. I built &lt;strong&gt;Key Tracer&lt;/strong&gt; a while back, quietly published it, and never really gave it the introduction it deserved. So here we are. Let me fix that. 😄&lt;/p&gt;

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




&lt;h2&gt;
  
  
  🔍 Key Tracer: Never Lose Your Place in a JSON File Again
&lt;/h2&gt;

&lt;p&gt;If you have ever worked with a deeply nested JSON file — config files, API responses, translation files, massive &lt;code&gt;package.json&lt;/code&gt; schemas — you know the pain.&lt;/p&gt;

&lt;p&gt;You are staring at a value buried five levels deep and you need to know its exact key path. So you squint. You mentally count the brackets. You scroll up and down. You get it wrong the first time. You try again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Tracer&lt;/strong&gt; makes that entire experience disappear.&lt;/p&gt;

&lt;p&gt;Just hover over any JSON key and the full path shows up instantly. One click and it is in your clipboard. Done.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;👉 &lt;a href="https://marketplace.visualstudio.com/items?itemName=AlvinJamesBellero.key-tracer" rel="noopener noreferrer"&gt;Key Tracer on VSCode Marketplace&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  ✨ What It Does
&lt;/h2&gt;

&lt;p&gt;Given a JSON file like this:&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;"app"&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;"settings"&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;"theme"&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;"color"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"indigo"&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;Hover over &lt;code&gt;"color"&lt;/code&gt; and you get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Full Path: app.settings.theme.color  [Copy Path]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Click &lt;strong&gt;Copy Path&lt;/strong&gt; and &lt;code&gt;app.settings.theme.color&lt;/code&gt; lands straight in your clipboard. No manual typing. No counting brackets. No mistakes.&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🔍 &lt;strong&gt;Hover to reveal&lt;/strong&gt; — shows the full dot notation path of any JSON key instantly&lt;/li&gt;
&lt;li&gt;📋 &lt;strong&gt;One-click copy&lt;/strong&gt; — copy the full path to clipboard right from the hover tooltip&lt;/li&gt;
&lt;li&gt;⚙️ &lt;strong&gt;Custom delimiter&lt;/strong&gt; — not a fan of dots? Change the separator to &lt;code&gt;/&lt;/code&gt;, &lt;code&gt;-&amp;gt;&lt;/code&gt;, &lt;code&gt;::&lt;/code&gt;, or whatever fits your project&lt;/li&gt;
&lt;li&gt;🎯 &lt;strong&gt;Deep nesting? No problem&lt;/strong&gt; — accurately resolves paths no matter how many levels in you go&lt;/li&gt;
&lt;li&gt;🧪 &lt;strong&gt;Comprehensive test coverage&lt;/strong&gt; — built with Mocha and a custom VS Code API mock so it actually behaves&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📦 Installation
&lt;/h2&gt;

&lt;p&gt;Two ways to get it:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option 1 — VS Code Quick Open:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Ctrl+P
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then paste:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ext install AlvinJamesBellero.key-tracer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Option 2 — Extensions panel:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open Extensions (&lt;code&gt;Cmd+Shift+X&lt;/code&gt; or &lt;code&gt;Ctrl+Shift+X&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Search for &lt;strong&gt;Key Tracer&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Hit &lt;strong&gt;Install&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Open any JSON file and start hovering 🎉&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://marketplace.visualstudio.com/items?itemName=AlvinJamesBellero.key-tracer" rel="noopener noreferrer"&gt;Key Tracer on the VS Code Marketplace&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  ⚙️ Configuring the Delimiter
&lt;/h2&gt;

&lt;p&gt;By default Key Tracer uses a dot &lt;code&gt;.&lt;/code&gt; as the path separator. But if your project uses a different convention, you can change it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open Settings (&lt;code&gt;Cmd+,&lt;/code&gt; or &lt;code&gt;Ctrl+,&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Search for &lt;strong&gt;Key Tracer&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Update the &lt;strong&gt;Delimiter&lt;/strong&gt; field to whatever you need&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So if you prefer &lt;code&gt;parent/child/target&lt;/code&gt; over &lt;code&gt;parent.child.target&lt;/code&gt;, you are covered.&lt;/p&gt;




&lt;h2&gt;
  
  
  💡 When Does This Actually Help?
&lt;/h2&gt;

&lt;p&gt;More than you would think. Here are the situations where I reach for it constantly:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;i18n / translation files&lt;/strong&gt; — these get deeply nested fast. Knowing the exact key path when referencing it in your component matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API response debugging&lt;/strong&gt; — pasting a large JSON response into VS Code and needing to reference a specific nested field path in your code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Config files&lt;/strong&gt; — environment configs, feature flag configs, anything with deeply layered structure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sending paths to a teammate&lt;/strong&gt; — "hey can you check &lt;code&gt;app.settings.theme.color&lt;/code&gt;?" is a lot cleaner than "it is like... four levels in, under settings, then theme."&lt;/p&gt;

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




&lt;h2&gt;
  
  
  🚀 Give It a Try
&lt;/h2&gt;

&lt;p&gt;Key Tracer is free, lightweight, and does exactly one thing well. If you spend any meaningful time in JSON files, it will save you a few mental cycles every single day. Those add up.&lt;/p&gt;

&lt;p&gt;Install it, hover over something deeply nested, and let me know what you think. Feedback, feature ideas, and bug reports all go straight to the GitHub repo.&lt;/p&gt;

&lt;p&gt;And if it saves you from squinting at brackets at 2am, a ⭐ on the marketplace goes a long way. 😄&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=AlvinJamesBellero.key-tracer" rel="noopener noreferrer"&gt;Install Key Tracer — marketplace.visualstudio.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>extensions</category>
      <category>productivity</category>
      <category>developers</category>
    </item>
    <item>
      <title>🧠 TypeScript: Type vs Interface — Stop Guessing, Start Knowing</title>
      <dc:creator>Alvin Bellero</dc:creator>
      <pubDate>Mon, 09 Mar 2026 00:11:37 +0000</pubDate>
      <link>https://dev.to/shiftescape/typescript-type-vs-interface-stop-guessing-start-knowing-2if9</link>
      <guid>https://dev.to/shiftescape/typescript-type-vs-interface-stop-guessing-start-knowing-2if9</guid>
      <description>&lt;p&gt;If you have written TypeScript for more than a week, someone has asked you this question. Or you have asked it yourself while staring at a blank file wondering why the language gives you two ways to do what looks like the same thing.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Should I use &lt;code&gt;type&lt;/code&gt; or &lt;code&gt;interface&lt;/code&gt;?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The Stack Overflow answers are all over the place. The blog posts are mostly outdated. The official docs say "use interface by default" and then immediately show you a dozen things interface cannot do. And your senior colleague says "we just use types for everything" while the other senior colleague says "always interfaces for objects, types for everything else."&lt;/p&gt;

&lt;p&gt;Both of them are right. Both of them are incomplete. Let me give you the full picture.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;📌 This article is written against &lt;strong&gt;TypeScript 5.7&lt;/strong&gt; (the current stable release as of early 2026). TypeScript 6.0 is in beta as the final JavaScript-based release, and TypeScript 7 (rewritten in Go) is in active preview. Notes on what changes with 7 are included where relevant.&lt;/p&gt;

&lt;p&gt;📖 &lt;a href="https://devblogs.microsoft.com/typescript/" rel="noopener noreferrer"&gt;TypeScript Blog — devblogs.microsoft.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  First, What Do They Actually Do?
&lt;/h2&gt;

&lt;p&gt;Both &lt;code&gt;type&lt;/code&gt; and &lt;code&gt;interface&lt;/code&gt; let you describe the shape of a value. In their most basic form, they look almost identical.&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;// With interface&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;email&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;// With type&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;email&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this use case — describing the shape of an object — they are functionally equivalent. The compiled JavaScript output is identical. The editor behavior is the same. The error messages are nearly the same.&lt;/p&gt;

&lt;p&gt;So why does the choice matter? Because they diverge in meaningful ways the moment your types get more complex.&lt;/p&gt;




&lt;h2&gt;
  
  
  What interface Can Do That type Cannot 🏗️
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Declaration Merging
&lt;/h3&gt;

&lt;p&gt;This is the most important feature exclusive to &lt;code&gt;interface&lt;/code&gt; and the one that most developers either love or get burned by.&lt;/p&gt;

&lt;p&gt;If you declare the same interface name twice in the same scope, TypeScript merges them automatically.&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;Window&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;userLocale&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="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Window&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;analyticsReady&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="c1"&gt;// TypeScript sees this as:&lt;/span&gt;
&lt;span class="c1"&gt;// interface Window {&lt;/span&gt;
&lt;span class="c1"&gt;//   userLocale: string;&lt;/span&gt;
&lt;span class="c1"&gt;//   analyticsReady: boolean;&lt;/span&gt;
&lt;span class="c1"&gt;// }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is how you extend third party library types without modifying their source. If you have ever added custom properties to Express's &lt;code&gt;Request&lt;/code&gt; object or extended global browser types, this is how it works.&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;// Extending Express Request in a .d.ts file&lt;/span&gt;
&lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nx"&gt;Express&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;Request&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;requestId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;type&lt;/code&gt; this is impossible. Declaring the same type name twice is a compile error.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://www.typescriptlang.org/docs/handbook/declaration-merging.html" rel="noopener noreferrer"&gt;Declaration Merging — typescriptlang.org&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Implements in Classes
&lt;/h3&gt;

&lt;p&gt;Classes can implement interfaces directly, and the compiler enforces the contract cleanly.&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;Repository&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="nf"&gt;findById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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="nx"&gt;T&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;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;entity&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="nb"&gt;Promise&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="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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="k"&gt;void&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserRepository&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;Repository&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&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;async&lt;/span&gt; &lt;span class="nf"&gt;findById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&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;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can technically &lt;code&gt;implement&lt;/code&gt; a &lt;code&gt;type&lt;/code&gt; alias too, but only when it represents an object shape. The moment the type is a union, the compiler refuses.&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;PrimaryKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Error — cannot implement a union type&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Entity&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;PrimaryKey&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://www.typescriptlang.org/docs/handbook/2/objects.html" rel="noopener noreferrer"&gt;Interfaces — typescriptlang.org&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What type Can Do That interface Cannot 🎯
&lt;/h2&gt;

&lt;p&gt;This is where &lt;code&gt;type&lt;/code&gt; genuinely pulls ahead. Everything listed here is impossible with &lt;code&gt;interface&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Union Types
&lt;/h3&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;Status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pending&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;active&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;suspended&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;deleted&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;ApiResponse&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="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;data&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;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;error&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Interfaces cannot express OR. Only AND. If you need to say "this is one of these things," you need &lt;code&gt;type&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Discriminated Unions (The Pattern You Should Be Using More)
&lt;/h3&gt;

&lt;p&gt;This is one of the most powerful patterns in TypeScript and it requires &lt;code&gt;type&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;type&lt;/span&gt; &lt;span class="nx"&gt;Shape&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;circle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rectangle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;triangle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;height&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getArea&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Shape&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="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;circle&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="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rectangle&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;shape&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;triangle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&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 compiler knows exactly which properties are available in each branch. You cannot accidentally access &lt;code&gt;shape.radius&lt;/code&gt; in the rectangle branch. This is making impossible states unrepresentable, and it is one of the best things TypeScript can do for you.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions" rel="noopener noreferrer"&gt;Discriminated Unions — typescriptlang.org&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Conditional Types
&lt;/h3&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;IsArray&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="kd"&gt;extends&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="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Unwrap&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="kd"&gt;extends&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="nx"&gt;infer&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="nx"&gt;U&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="c1"&gt;// Usage&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Unwrap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// string&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;B&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Unwrap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;          &lt;span class="c1"&gt;// number&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the foundation of every utility type in TypeScript. &lt;code&gt;Partial&lt;/code&gt;, &lt;code&gt;Required&lt;/code&gt;, &lt;code&gt;ReturnType&lt;/code&gt;, &lt;code&gt;Awaited&lt;/code&gt; — all built with conditional types. None of it is possible with &lt;code&gt;interface&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://www.typescriptlang.org/docs/handbook/2/conditional-types.html" rel="noopener noreferrer"&gt;Conditional Types — typescriptlang.org&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Mapped Types
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Make all properties optional and readonly&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Snapshot&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="k"&gt;readonly&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="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Extract only string properties from an object type&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;StringProperties&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="k"&gt;as&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="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;K&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;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="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;age&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="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;UserStrings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;StringProperties&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Result: { name: string; email: string }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://www.typescriptlang.org/docs/handbook/2/mapped-types.html" rel="noopener noreferrer"&gt;Mapped Types — typescriptlang.org&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Template Literal Types
&lt;/h3&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;EventName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&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;focus&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;blur&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;Handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`on&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Capitalize&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;EventName&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;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Result: 'onClick' | 'onFocus' | 'onBlur'&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;CSSProperty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;margin&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;padding&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;CSSDirection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Top&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;Right&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;Bottom&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;Left&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;CSSLonghand&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;CSSProperty&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;CSSDirection&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="c1"&gt;// 'marginTop' | 'marginRight' | ... | 'paddingLeft'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html" rel="noopener noreferrer"&gt;Template Literal Types — typescriptlang.org&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Tuple Types
&lt;/h3&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;Pair&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;B&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;A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;B&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;RGB&lt;/span&gt; &lt;span class="o"&gt;=&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="kr"&gt;number&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="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;HTTPMethod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&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;POST&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;PUT&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;DELETE&lt;/span&gt;&lt;span class="dl"&gt;'&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Performance Question. Honestly Answered. ⚡
&lt;/h2&gt;

&lt;p&gt;This comes up in every conversation and the answer is nuanced so I am going to give it the space it deserves.&lt;/p&gt;

&lt;h3&gt;
  
  
  Interfaces with extends are cached. Type intersections are not.
&lt;/h3&gt;

&lt;p&gt;When TypeScript evaluates a type intersection (&lt;code&gt;&amp;amp;&lt;/code&gt;), it recomputes the merged type every time it encounters it. When TypeScript evaluates an interface that extends another, it checks the named interface reference, which it has already cached.&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;// Recomputed on every use — not cached&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;AdminUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;permissions&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="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;department&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Cached by name — more efficient for repeated use&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;AdminUser&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nl"&gt;department&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A real benchmark across 10,000 intersection types vs 10,000 extended interfaces showed the interface variant compiling in 1 minute 26 seconds versus 2 minutes 33 seconds for the intersection variant. That is almost half the compile time.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://github.com/microsoft/TypeScript/wiki/Performance#preferring-interfaces-over-intersections" rel="noopener noreferrer"&gt;TypeScript Performance Wiki — github.com/microsoft/TypeScript&lt;/a&gt;&lt;br&gt;
📖 &lt;a href="https://www.mykytam.com/blog/type-vs-interface/" rel="noopener noreferrer"&gt;Benchmark — mykytam.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  But context matters
&lt;/h3&gt;

&lt;p&gt;For most apps this difference will not show up in your daily work. Where it genuinely matters is in large monorepos with hundreds of deeply composed types, running full compilation in CI. If your CI builds are creeping up toward ten minutes, auditing your intersection types is a legitimate optimization worth doing.&lt;/p&gt;

&lt;p&gt;For local development, the TypeScript language server evaluates types per file rather than the whole project, so the day to day impact is much smaller.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://blog.logrocket.com/types-vs-interfaces-typescript/" rel="noopener noreferrer"&gt;LogRocket — Types vs Interfaces Performance&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  TypeScript 7 changes the picture
&lt;/h3&gt;

&lt;p&gt;TypeScript 7 is a complete rewrite of the compiler in Go, targeting up to 10 times faster compile times across the board. Compile times should shrink from minutes to seconds, and language service operations like completions, renames, and refactorings become more responsive. When TypeScript 7 becomes the default, the performance gap between &lt;code&gt;type&lt;/code&gt; and &lt;code&gt;interface&lt;/code&gt; narrows significantly because the baseline is so much faster. The right choice for the compile time benefit today is &lt;code&gt;interface extends&lt;/code&gt; over &lt;code&gt;&amp;amp;&lt;/code&gt; intersections — but do not stress about it if you are on small to medium projects.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://devblogs.microsoft.com/typescript/progress-on-typescript-7-december-2025/" rel="noopener noreferrer"&gt;Progress on TypeScript 7 — devblogs.microsoft.com&lt;/a&gt;&lt;br&gt;
📖 &lt;a href="https://developer.microsoft.com/blog/typescript-7-native-preview-in-visual-studio-2026" rel="noopener noreferrer"&gt;TypeScript 7 Native Preview — developer.microsoft.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Declaration Merging Trap 🪤
&lt;/h2&gt;

&lt;p&gt;This is the caveat that Matt Pocock at Total TypeScript correctly calls out as the main reason to be careful with interfaces as your default.&lt;/p&gt;

&lt;p&gt;If you accidentally declare the same interface name twice in the same scope, TypeScript silently merges them. No error. No warning. Just a type that now has extra properties you did not intend.&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;// Somewhere in your codebase&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&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;// Somewhere else — maybe a different file, maybe a library you imported&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;internalFlag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// silently merged in&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Now User requires internalFlag everywhere. Good luck debugging that.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Jane&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="c1"&gt;// Error: Property 'internalFlag' is missing&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;type&lt;/code&gt;, the second declaration is an immediate compiler error. You find the problem at the point of declaration, not at the point of use.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://www.totaltypescript.com/type-vs-interface-which-should-you-use" rel="noopener noreferrer"&gt;Total TypeScript — type vs interface&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Tips and Tricks That Actually Matter in Production 🛠️
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Use discriminated unions to make invalid states impossible
&lt;/h3&gt;

&lt;p&gt;Stop using optional properties to represent different states of the same object. Model each state explicitly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The fragile way — optional properties everywhere&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pending&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;shipped&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;delivered&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;trackingNumber&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// only when shipped&lt;/span&gt;
  &lt;span class="nl"&gt;deliveredAt&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// only when delivered&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// The correct way — each state is its own type&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pending&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shipped&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;trackingNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;delivered&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;trackingNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;deliveredAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// The compiler now enforces what belongs where&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Order&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;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shipped&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trackingNumber&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// always defined here, no ?. needed&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://www.typescriptlang.org/docs/handbook/2/narrowing.html" rel="noopener noreferrer"&gt;Narrowing — typescriptlang.org&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Use &lt;code&gt;satisfies&lt;/code&gt; to get both inference and validation
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;satisfies&lt;/code&gt; operator (stable since TypeScript 4.9) lets you validate that a value matches a type while keeping the most specific inferred type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Palette&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&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="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Without satisfies — TypeScript infers the wide type&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;colors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;red&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;255&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="na"&gt;green&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#00ff00&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;satisfies&lt;/span&gt; &lt;span class="nx"&gt;Palette&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Now colors.red is inferred as [number, number, number], not string | [number, number, number]&lt;/span&gt;
&lt;span class="c1"&gt;// And colors.green is string, not string | [number, number, number]&lt;/span&gt;
&lt;span class="nx"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;red&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;at&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="c1"&gt;// works — TypeScript knows it's a tuple&lt;/span&gt;
&lt;span class="nx"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;green&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// works — TypeScript knows it's a string&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without &lt;code&gt;satisfies&lt;/code&gt; you would have to choose between type safety and useful inference. Now you get both.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-9.html" rel="noopener noreferrer"&gt;satisfies operator — typescriptlang.org&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Prefer &lt;code&gt;interface extends&lt;/code&gt; over &lt;code&gt;&amp;amp;&lt;/code&gt; for object composition
&lt;/h3&gt;

&lt;p&gt;When you are combining object types, &lt;code&gt;extends&lt;/code&gt; is more readable and more performant than &lt;code&gt;&amp;amp;&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="c1"&gt;// Less readable, less performant in large codebases&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;AdminUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;permissions&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="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;Timestamps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Better&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;AdminUser&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Timestamps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;permissions&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://github.com/microsoft/TypeScript/wiki/Performance#preferring-interfaces-over-intersections" rel="noopener noreferrer"&gt;TypeScript Performance — github.com/microsoft/TypeScript&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Use &lt;code&gt;const&lt;/code&gt; assertions for literal types
&lt;/h3&gt;

&lt;p&gt;When you want TypeScript to infer the narrowest possible type from a value, &lt;code&gt;as const&lt;/code&gt; is the move.&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;// Without as const — TypeScript infers string[]&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;directions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;north&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;south&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;east&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;west&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// With as const — TypeScript infers readonly ['north', 'south', 'east', 'west']&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;directions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;north&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;south&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;east&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;west&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="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Direction&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;directions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="c1"&gt;// Result: 'north' | 'south' | 'east' | 'west'&lt;/span&gt;

&lt;span class="c1"&gt;// Now adding an invalid direction is a compile error&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Direction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;up&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Error — not in the union&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions" rel="noopener noreferrer"&gt;const assertions — typescriptlang.org&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Use template literal types to keep string patterns safe
&lt;/h3&gt;

&lt;p&gt;If your codebase uses string patterns like route paths, event names, or CSS class names, template literal types let you enforce them at compile time.&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;Route&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`/api/&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="s2"&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;VersionedRoute&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`/api/v&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="s2"&gt;/&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;         &lt;span class="c1"&gt;// fine&lt;/span&gt;
&lt;span class="nf"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/v2/products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// fine&lt;/span&gt;
&lt;span class="nf"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;             &lt;span class="c1"&gt;// Error — does not start with /api/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html" rel="noopener noreferrer"&gt;Template Literal Types — typescriptlang.org&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Use &lt;code&gt;infer&lt;/code&gt; for type level parsing
&lt;/h3&gt;

&lt;p&gt;When you need to extract parts of a type programmatically, &lt;code&gt;infer&lt;/code&gt; is the right tool.&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;// Extract the return type of any async function&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Awaited&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="kd"&gt;extends&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="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;R&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;R&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="c1"&gt;// Extract the element type of any array&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ElementType&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="nf"&gt;extends &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;E&lt;/span&gt;&lt;span class="p"&gt;)[]&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;E&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Extract parameter types of a function&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;FirstParam&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="nf"&gt;extends &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;rest&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;F&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="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ElementType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// string&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;B&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;FirstParam&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// string&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#inferring-within-conditional-types" rel="noopener noreferrer"&gt;Inferring within Conditional Types — typescriptlang.org&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Decision Framework 🗂️
&lt;/h2&gt;

&lt;p&gt;Here is the clearest way I can give you to make the call.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use &lt;code&gt;interface&lt;/code&gt; when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You are defining a contract that classes will implement&lt;/li&gt;
&lt;li&gt;You are extending or augmenting third party library types&lt;/li&gt;
&lt;li&gt;You are writing a library and want consumers to be able to extend your types via declaration merging&lt;/li&gt;
&lt;li&gt;You are building deep object hierarchies and composition matters for compile performance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use &lt;code&gt;type&lt;/code&gt; when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need a union (&lt;code&gt;|&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;You need a conditional type&lt;/li&gt;
&lt;li&gt;You need a mapped type or template literal type&lt;/li&gt;
&lt;li&gt;You are defining tuples&lt;/li&gt;
&lt;li&gt;You are defining function signatures (the syntax is cleaner)&lt;/li&gt;
&lt;li&gt;You do not want accidental declaration merging to be possible&lt;/li&gt;
&lt;li&gt;You are using functional patterns where each thing is its own distinct type&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;In practice:&lt;/strong&gt; most experienced TypeScript teams land on one of two camps. Either "default to &lt;code&gt;interface&lt;/code&gt; for objects, use &lt;code&gt;type&lt;/code&gt; for everything else" or "default to &lt;code&gt;type&lt;/code&gt; for everything, use &lt;code&gt;interface&lt;/code&gt; only when declaration merging is needed." Both are defensible. The worst thing you can do is mix them randomly without a team decision.&lt;/p&gt;

&lt;p&gt;Whatever you choose, write it down and enforce it with ESLint.&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="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;.eslintrc&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;enforce&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;team's&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;choice&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;"rules"&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;"@typescript-eslint/consistent-type-definitions"&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;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"type"&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;or:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"interface"&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;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://typescript-eslint.io/rules/consistent-type-definitions/" rel="noopener noreferrer"&gt;consistent-type-definitions — typescript-eslint.io&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  TypeScript 7 and What Is Coming 🔭
&lt;/h2&gt;

&lt;p&gt;Two things worth knowing about the near future.&lt;/p&gt;

&lt;p&gt;TypeScript 6.0 is the last release on the old JavaScript compiler codebase. It is being framed as a bridge release to prepare the ecosystem for TypeScript 7. TypeScript 6.0 will deprecate features to align with 7.0, and will be highly compatible in terms of type checking behavior.&lt;/p&gt;

&lt;p&gt;TypeScript 7 features like &lt;code&gt;--incremental&lt;/code&gt;, project reference support, and &lt;code&gt;--build&lt;/code&gt; mode are all ported over and working in the native Go compiler. Most projects can now try the native preview with minimal changes.&lt;/p&gt;

&lt;p&gt;What this means for the &lt;code&gt;type&lt;/code&gt; vs &lt;code&gt;interface&lt;/code&gt; question: the syntax is not changing. Your existing code works. The big shift is that the performance argument in favor of &lt;code&gt;interface extends&lt;/code&gt; over &lt;code&gt;&amp;amp;&lt;/code&gt; intersections becomes less urgent as the Go compiler makes everything faster. But correctness and clarity remain the right reasons to pick one over the other regardless of the compiler speed.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://devblogs.microsoft.com/typescript/" rel="noopener noreferrer"&gt;TypeScript 6.0 Beta — devblogs.microsoft.com&lt;/a&gt;&lt;br&gt;
📖 &lt;a href="https://devblogs.microsoft.com/typescript/progress-on-typescript-7-december-2025/" rel="noopener noreferrer"&gt;Progress on TypeScript 7 — devblogs.microsoft.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Short Version 👋
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;interface&lt;/code&gt; gives you declaration merging, clean class contracts, and slightly better compile performance for object composition through &lt;code&gt;extends&lt;/code&gt;. Use it when you want extensibility to be explicit and intentional, or when you are writing library code that consumers will augment.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;type&lt;/code&gt; gives you unions, conditional types, mapped types, template literals, tuples, and protection against accidental merging. Use it for everything that goes beyond describing a simple object shape.&lt;/p&gt;

&lt;p&gt;For performance: prefer &lt;code&gt;interface extends&lt;/code&gt; over &lt;code&gt;type &amp;amp;&lt;/code&gt; intersections when composing many object types, especially in large codebases. The &lt;code&gt;satisfies&lt;/code&gt; operator is your friend when you need both type safety and narrow inference. And discriminated unions are the pattern you should be reaching for every time you see optional properties that only make sense in certain states.&lt;/p&gt;

&lt;p&gt;Pick a convention. Lint for it. Stop relitigating it every PR review.&lt;/p&gt;

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




&lt;p&gt;&lt;em&gt;Which camp are you in — team &lt;code&gt;type&lt;/code&gt; or team &lt;code&gt;interface&lt;/code&gt;? Drop it in the comments. I want to know if I will be starting a war.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>🎭 I Switched to Playwright and I Am Not Going Back</title>
      <dc:creator>Alvin Bellero</dc:creator>
      <pubDate>Sun, 08 Mar 2026 08:54:47 +0000</pubDate>
      <link>https://dev.to/shiftescape/i-switched-to-playwright-and-i-am-not-going-back-epa</link>
      <guid>https://dev.to/shiftescape/i-switched-to-playwright-and-i-am-not-going-back-epa</guid>
      <description>&lt;p&gt;Let me paint you a picture.&lt;/p&gt;

&lt;p&gt;It is 2019. You have a Selenium test suite that takes 45 minutes to run. Half the tests are flaky. There is a custom wrapper around WebDriver that one person wrote and only one person understands. That person left the company in 2020.&lt;/p&gt;

&lt;p&gt;You are that person now.&lt;/p&gt;

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

&lt;p&gt;Then Cypress came along and saved a lot of us. Fast, developer friendly, genuinely good developer experience. We migrated. We were happy. For a while.&lt;/p&gt;

&lt;p&gt;Then you hit the wall. Cross browser testing? Painful. Multiple tabs? Good luck. Iframe handling? Let us not talk about it. File downloads? I have a story about that one but my therapist says I should stop bringing it up.&lt;/p&gt;

&lt;p&gt;Enter &lt;strong&gt;Playwright&lt;/strong&gt;. Built by Microsoft. Quietly battle tested by teams at serious scale. Currently sitting at &lt;strong&gt;v1.58&lt;/strong&gt; as of early 2026. And honestly? It fixed almost everything I had quietly accepted as just the way testing works.&lt;/p&gt;

&lt;p&gt;This is not a beginner's guide. You already know what end to end testing is. You have opinions about &lt;code&gt;async/await&lt;/code&gt;. You have been burned before. So let me just show you the parts that made me switch and genuinely never look back.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 All code examples are tested against v1.58.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Auto Waiting That Actually Works 🧘
&lt;/h2&gt;

&lt;p&gt;Every action in Playwright, whether it is a &lt;code&gt;click&lt;/code&gt;, a &lt;code&gt;fill&lt;/code&gt;, or a &lt;code&gt;check&lt;/code&gt;, automatically waits for the element to be attached to the DOM, visible on screen, stable and not mid animation, enabled, and not obscured by something else sitting on top of it.&lt;/p&gt;

&lt;p&gt;All of that. Before it does anything. Without you asking.&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;// Goodbye forever:&lt;/span&gt;
&lt;span class="c1"&gt;// await page.waitForSelector('#submit-btn')&lt;/span&gt;
&lt;span class="c1"&gt;// await driver.wait(until.elementIsVisible(...))&lt;/span&gt;

&lt;span class="c1"&gt;// Hello:&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#submit-btn&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;That one change alone eliminated roughly 30% of our flaky tests overnight. Not because those tests were badly written. Because half of our so called flakiness was just timing issues we had been papering over with &lt;code&gt;sleep()&lt;/code&gt; calls like it was 2011. 🐒&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://playwright.dev/docs/actionability" rel="noopener noreferrer"&gt;Auto Waiting — playwright.dev&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  True Cross Browser Support That Does Not Make You Want to Cry 🌐
&lt;/h2&gt;

&lt;p&gt;Playwright ships with three browser engines built right in: &lt;strong&gt;Chromium, Firefox, and WebKit&lt;/strong&gt;. These are not thin wrappers or third party drivers. They are first class, actively maintained engines.&lt;/p&gt;

&lt;p&gt;And as of &lt;strong&gt;v1.57&lt;/strong&gt;, Playwright moved from generic Chromium builds over to &lt;strong&gt;Chrome for Testing&lt;/strong&gt; builds. Same test behavior, but now you are actually running on the same Chrome your users see. Headed mode uses &lt;code&gt;chrome&lt;/code&gt; and headless mode uses &lt;code&gt;chrome-headless-shell&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://playwright.dev/docs/release-notes#version-157" rel="noopener noreferrer"&gt;Chrome for Testing in v1.57 — playwright.dev release notes&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// playwright.config.ts&lt;/span&gt;
&lt;span class="nx"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chromium&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;use&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;devices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Desktop Chrome&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;firefox&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;use&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;devices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Desktop Firefox&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webkit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;use&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;devices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Desktop Safari&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mobile-chrome&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;use&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;devices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Pixel 7&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mobile-safari&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;use&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;devices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;iPhone 14&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One command. Five browsers. Done. No Sauce Labs subscription needed for basic cross browser coverage. No separate CI pipeline per browser to babysit.&lt;/p&gt;

&lt;p&gt;Cypress added Firefox support at some point. WebKit support there is still experimental. With Playwright, both were first class from the very beginning because the whole thing was designed that way from scratch.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://playwright.dev/docs/browsers" rel="noopener noreferrer"&gt;Cross Browser Testing — playwright.dev&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Multiple Tabs and Windows Like a Normal Person 🗂️
&lt;/h2&gt;

&lt;p&gt;OAuth flows. Payment redirects. Anything that pops open a new tab. All of it is handled natively in Playwright without needing workarounds or third party plugins.&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;// Just wait for the new tab and carry on&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;newPage&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitForEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;page&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a[target="_blank"]&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;await&lt;/span&gt; &lt;span class="nx"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitForLoadState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/Payment Confirmation/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Browser contexts are where Playwright really pulls ahead though. Each context is a fully isolated browser session with its own cookies, its own local storage, and its own auth state. You can spin up multiple contexts inside a single test and simulate two different users interacting with the same page simultaneously.&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;// Two users. One test. Zero drama.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userA&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;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newContext&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;userB&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;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newContext&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;pageA&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;userA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&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;pageB&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;userB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Try doing that cleanly in Selenium. I will be right here waiting. ⏳&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://playwright.dev/docs/browser-contexts" rel="noopener noreferrer"&gt;Browser Contexts — playwright.dev&lt;/a&gt;&lt;br&gt;
📖 &lt;a href="https://playwright.dev/docs/pages" rel="noopener noreferrer"&gt;Pages and Tabs — playwright.dev&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Network Interception That Does Not Feel Like a Hack 🔌
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Mock a specific API endpoint&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;**/api/users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;route&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;await&lt;/span&gt; &lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fulfill&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test User&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Silently kill analytics requests so they stop slowing down your tests&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;**/*analytics*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;route&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// Fetch the real response, change one thing, send it back&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;**/api/config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;route&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;response&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;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;json&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;featureFlags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newDashboard&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;await&lt;/span&gt; &lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fulfill&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;json&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;That third one is genuinely my favorite trick in Playwright. You get the real API response, flip one feature flag, and return it. You are testing the actual behavior of your app without mocking an entire endpoint. I reach for this all the time.&lt;/p&gt;

&lt;p&gt;And as of &lt;strong&gt;v1.57&lt;/strong&gt;, Playwright also reports and routes network requests from &lt;strong&gt;Service Workers&lt;/strong&gt; through &lt;code&gt;BrowserContext&lt;/code&gt; in Chromium. If you are building any kind of PWA, that is a big deal.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://playwright.dev/docs/mock" rel="noopener noreferrer"&gt;Network Mocking — playwright.dev&lt;/a&gt;&lt;br&gt;
📖 &lt;a href="https://playwright.dev/docs/release-notes#version-157" rel="noopener noreferrer"&gt;Service Worker Routing in v1.57 — playwright.dev release notes&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Clock API: Finally, Time Travel for Your Tests ⏰
&lt;/h2&gt;

&lt;p&gt;This feature gets criminally undermentioned. &lt;code&gt;page.clock&lt;/code&gt; gives you full control over time inside the browser: &lt;code&gt;Date&lt;/code&gt;, &lt;code&gt;setTimeout&lt;/code&gt;, &lt;code&gt;setInterval&lt;/code&gt;, all of it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Freeze time at a specific moment&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;install&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;time&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;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2025-01-01T08:00:00&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;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:3000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Skip ahead two hours to trigger session expiry&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fastForward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;02:00:00&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Session expired&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeVisible&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Or just park it at a specific time and assert&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pauseAt&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;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2025-01-01T10:00:00&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByTestId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;current-time&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toHaveText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;10:00 AM&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;No more sneaking &lt;code&gt;Date.now()&lt;/code&gt; mocks into your application code just to make a test pass. No more &lt;code&gt;jest.useFakeTimers()&lt;/code&gt; gymnastics that only sort of work. Playwright reaches directly into the browser clock and takes over. Countdown timers, token expiry, scheduled notifications, time sensitive UI states — all testable without touching your production code.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://playwright.dev/docs/clock" rel="noopener noreferrer"&gt;Clock API — playwright.dev&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Trace Viewer and the New Speedboard 🪄
&lt;/h2&gt;

&lt;p&gt;When a test fails in CI with Selenium, you get a stack trace and maybe a screenshot if someone remembered to configure it. Playwright's trace viewer is a completely different experience. It is a full recording of your test: DOM snapshots at every action, every network request, every console log, screenshots throughout. You can scrub through it like rewinding a video.&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;// playwright.config.ts&lt;/span&gt;
&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;retain-on-failure&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;screenshot&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;only-on-failure&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;video&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;retain-on-failure&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx playwright show-trace trace.zip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full interactive replay of your failed test, straight from a CI artifact, without ever needing to reproduce it locally. For any senior engineer who has wasted hours trying to reproduce a flaky test that only breaks on Tuesday in CI, this changes your life.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://playwright.dev/docs/trace-viewer" rel="noopener noreferrer"&gt;Trace Viewer — playwright.dev&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The newer addition I am equally excited about is the &lt;strong&gt;Speedboard&lt;/strong&gt;, which landed in &lt;strong&gt;v1.57&lt;/strong&gt; and got improved in &lt;strong&gt;v1.58&lt;/strong&gt;. It is a tab in the HTML reporter that lists all your tests sorted from slowest to fastest. At a glance you can see which tests are dragging the whole suite down and start asking useful questions about why.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://playwright.dev/docs/release-notes#version-157" rel="noopener noreferrer"&gt;Speedboard in v1.57 — playwright.dev release notes&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And with the &lt;strong&gt;Timeline view&lt;/strong&gt; added in &lt;strong&gt;v1.58&lt;/strong&gt;, when you are using merged reports across sharded runs, you get a full picture of what ran when across every machine. Sharding across ten workers and still having complete visibility? That is how a grown up CI setup looks.&lt;/p&gt;

&lt;p&gt;The UI Mode and Trace Viewer also got quality of life updates in &lt;strong&gt;v1.58&lt;/strong&gt;: native OS dark and light mode support, &lt;code&gt;Cmd/Ctrl+F&lt;/code&gt; search inside code editors, and JSON responses that format themselves automatically. Small things. The kind of small things that make you feel like the team actually uses their own tool every day.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://playwright.dev/docs/release-notes#version-158" rel="noopener noreferrer"&gt;Timeline and UI Mode updates in v1.58 — playwright.dev release notes&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Playwright Agents: AI That Writes and Heals Your Tests 🤖
&lt;/h2&gt;

&lt;p&gt;This is the newest addition and honestly the most interesting direction the project is heading. Starting in &lt;strong&gt;v1.56&lt;/strong&gt;, Playwright ships with three official AI agent definitions: &lt;strong&gt;planner&lt;/strong&gt; explores your app and produces a structured Markdown test plan, &lt;strong&gt;generator&lt;/strong&gt; takes that plan and turns it into actual Playwright test files, and &lt;strong&gt;healer&lt;/strong&gt; runs your suite and automatically fixes tests that broke because a selector changed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx playwright init-agents &lt;span class="nt"&gt;--loop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;claude
&lt;span class="c"&gt;# or if you prefer&lt;/span&gt;
npx playwright init-agents &lt;span class="nt"&gt;--loop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vscode
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The healer is where I spend most of my attention. Any sufficiently large test suite has tests that break not because the feature broke but because someone renamed a button or restructured a component. Instead of manually hunting down 40 selectors after a big UI refactor, the healer finds what broke and repairs the locators.&lt;/p&gt;

&lt;p&gt;Is it production perfect yet? Not entirely. But it ships as a first class part of Playwright itself, not some community plugin that might go unmaintained in six months. The trajectory here is very clear.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://playwright.dev/docs/release-notes#version-156" rel="noopener noreferrer"&gt;Playwright Test Agents in v1.56 — playwright.dev release notes&lt;/a&gt;&lt;br&gt;
📖 &lt;a href="https://playwright.dev/docs/test-agents" rel="noopener noreferrer"&gt;Playwright Agents Guide — playwright.dev&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Codegen Now Writes Assertions For You 🧠
&lt;/h2&gt;

&lt;p&gt;The test generator got a nice upgrade in &lt;strong&gt;v1.58&lt;/strong&gt;. When you use &lt;code&gt;npx playwright codegen&lt;/code&gt; to record your interactions, it now &lt;strong&gt;automatically generates &lt;code&gt;toBeVisible()&lt;/code&gt; assertions&lt;/strong&gt; alongside the actions. Before this, Codegen was great for capturing clicks and fills but you still had to go back and add assertions yourself. Now it scaffolds them as you go, and you can toggle the behavior off in the Codegen settings UI if you prefer the old way.&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;// What Codegen produces now after you submit a form:&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Submit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Success! Form submitted.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeVisible&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// generated for you ✨&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Less manual work. Faster test writing. Good change.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://playwright.dev/docs/codegen" rel="noopener noreferrer"&gt;Codegen — playwright.dev&lt;/a&gt;&lt;br&gt;
📖 &lt;a href="https://playwright.dev/docs/release-notes#version-158" rel="noopener noreferrer"&gt;Auto assertions in Codegen, v1.58 — playwright.dev release notes&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  webServer Finally Knows When Your App Is Actually Ready 🚦
&lt;/h2&gt;

&lt;p&gt;This one sounds small but it has saved me multiple CI headaches. In &lt;strong&gt;v1.57&lt;/strong&gt;, the &lt;code&gt;webServer&lt;/code&gt; config got a &lt;code&gt;wait&lt;/code&gt; field that accepts a regex. Playwright now holds off on running tests until your dev server logs something that matches.&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;// playwright.config.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;webServer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;npm run start&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/Listening on port &lt;/span&gt;&lt;span class="se"&gt;(?&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;my_server_port&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use a named capture group and Playwright automatically passes it through as an environment variable, so you can pick up the dynamic port in your test config.&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;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`http://localhost:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MY_SERVER_PORT&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;3000&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No more hardcoded ports. No more &lt;code&gt;waitForTimeout(5000)&lt;/code&gt; at the top of every file because someone got burned by a slow CI machine once and quietly added it three years ago and nobody knows why it is still there.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://playwright.dev/docs/release-notes#version-157" rel="noopener noreferrer"&gt;webServer wait field in v1.57 — playwright.dev release notes&lt;/a&gt;&lt;br&gt;
📖 &lt;a href="https://playwright.dev/docs/test-webserver" rel="noopener noreferrer"&gt;webServer config — playwright.dev&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  TypeScript Just Works 🟦
&lt;/h2&gt;

&lt;p&gt;Zero configuration. No plugins to install. No &lt;code&gt;@types/&lt;/code&gt; packages to track down. You write TypeScript and Playwright is fine with it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Page&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;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&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;loginAs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[name="email"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[name="password"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[type="submit"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin can access settings&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&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;await&lt;/span&gt; &lt;span class="nf"&gt;loginAs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;secret&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nav &amp;gt;&amp;gt; text=Settings&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toHaveText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Settings&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;Full type inference on the &lt;code&gt;page&lt;/code&gt; object. Autocomplete on locators. Typed fixture overrides. It just feels good to work in.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://playwright.dev/docs/test-typescript" rel="noopener noreferrer"&gt;TypeScript in Playwright — playwright.dev&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The One Thing I Still Miss From Cypress
&lt;/h2&gt;

&lt;p&gt;The time travel debugging in the Cypress test runner. That interactive, step through experience when you are writing a new test locally is still the most comfortable way to author tests I have ever used. Playwright's UI mode is genuinely closing the gap but Cypress still wins on raw first time test writing comfort.&lt;/p&gt;

&lt;p&gt;That is the only thing on my list. Just the one. Think about what that means.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://playwright.dev/docs/test-ui-mode" rel="noopener noreferrer"&gt;Playwright UI Mode — playwright.dev&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Should You Actually Switch?
&lt;/h2&gt;

&lt;p&gt;If you are on &lt;strong&gt;Selenium&lt;/strong&gt;, yes. Please. There is no honest argument for Selenium on a modern web app in 2026 unless you have legacy constraints you genuinely cannot escape yet.&lt;/p&gt;

&lt;p&gt;If you are on &lt;strong&gt;Cypress&lt;/strong&gt;, it depends. If you keep running into the walls I described, cross browser, multi tab flows, PWA testing, the Clock API, then yes the migration is worth it. If Cypress is genuinely working well for your team and productivity is good, the switching cost might not make sense today. But watch this space. The gap between the two tools is growing with every Playwright release.&lt;/p&gt;

&lt;p&gt;If you are &lt;strong&gt;starting from scratch&lt;/strong&gt;, Playwright is the default answer. Not because Cypress is bad but because Playwright was designed to handle everything from the beginning.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;a href="https://testdino.com/blog/cypress-to-playwright-migration/" rel="noopener noreferrer"&gt;Cypress to Playwright migration: A step-by-step guide — playwright.dev&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Short Version 👋
&lt;/h2&gt;

&lt;p&gt;Playwright v1.58 gives you auto waiting that actually eliminates flakiness (&lt;a href="https://playwright.dev/docs/actionability" rel="noopener noreferrer"&gt;docs&lt;/a&gt;), real cross browser support running on Chrome for Testing builds (&lt;a href="https://playwright.dev/docs/release-notes#version-157" rel="noopener noreferrer"&gt;v1.57 notes&lt;/a&gt;), native multi tab and multi user testing (&lt;a href="https://playwright.dev/docs/browser-contexts" rel="noopener noreferrer"&gt;docs&lt;/a&gt;), clean network interception including Service Worker routing (&lt;a href="https://playwright.dev/docs/mock" rel="noopener noreferrer"&gt;docs&lt;/a&gt;), a Clock API that controls browser time directly (&lt;a href="https://playwright.dev/docs/clock" rel="noopener noreferrer"&gt;docs&lt;/a&gt;), a Trace Viewer and Speedboard that make debugging CI failures actually manageable (&lt;a href="https://playwright.dev/docs/trace-viewer" rel="noopener noreferrer"&gt;docs&lt;/a&gt;), AI agents that write and repair your tests (&lt;a href="https://playwright.dev/docs/release-notes#version-156" rel="noopener noreferrer"&gt;v1.56 notes&lt;/a&gt;), smarter Codegen with automatic assertions (&lt;a href="https://playwright.dev/docs/release-notes#version-158" rel="noopener noreferrer"&gt;v1.58 notes&lt;/a&gt;), and TypeScript support with zero setup (&lt;a href="https://playwright.dev/docs/test-typescript" rel="noopener noreferrer"&gt;docs&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;It is what end to end testing should have been all along. And the team is still shipping.&lt;/p&gt;

&lt;p&gt;Go write some tests. Good ones this time. 🎭&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Already on Playwright? Drop your favorite underrated feature in the comments. I am always looking for the next trick I did not know existed.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>testing</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>🌟 Extensions Dump: UnitFlux and CodeMorph!</title>
      <dc:creator>Alvin Bellero</dc:creator>
      <pubDate>Sun, 10 Nov 2024 09:51:38 +0000</pubDate>
      <link>https://dev.to/shiftescape/extensions-dump-unitflux-and-codemorph-bhi</link>
      <guid>https://dev.to/shiftescape/extensions-dump-unitflux-and-codemorph-bhi</guid>
      <description>&lt;p&gt;Hey there, developers! 👋&lt;/p&gt;

&lt;p&gt;I'm excited to share two new VSCode extensions that I recently published: &lt;strong&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=AlvinJamesBellero.unit-flux" rel="noopener noreferrer"&gt;UnitFlux&lt;/a&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=AlvinJamesBellero.code-morph" rel="noopener noreferrer"&gt;CodeMorph&lt;/a&gt;&lt;/strong&gt;! These tools aim to streamline your workflow and make your life a little easier in the editor.&lt;/p&gt;

&lt;p&gt;Let’s dive into what they’re all about! 🚤&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExeGx5dTh5eHNvMmh6d2k4cWZqdnp2YjJnNnpvMzB1cHRsN2g1OWlsMCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/CjmvTCZf2U3p09Cn0h/giphy-downsized.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExeGx5dTh5eHNvMmh6d2k4cWZqdnp2YjJnNnpvMzB1cHRsN2g1OWlsMCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/CjmvTCZf2U3p09Cn0h/giphy-downsized.gif" width="384" height="360"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🎨 &lt;strong&gt;UnitFlux: Effortless Unit Conversion for Responsive CSS&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;CSS design can be tricky, especially when trying to achieve fluid layouts.&lt;/p&gt;

&lt;p&gt;That’s where &lt;strong&gt;UnitFlux&lt;/strong&gt; comes in! With this extension, you can seamlessly convert pixels (PX) to other responsive units like &lt;code&gt;em&lt;/code&gt; and &lt;code&gt;rem&lt;/code&gt; directly in your editor. 🎯&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Why Use UnitFlux?&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;📏 &lt;strong&gt;Boosts Responsive Design&lt;/strong&gt; – Instantly scale pixel-based values to flexible units for a design that adjusts to any screen size.&lt;/li&gt;
&lt;li&gt;⚡ &lt;strong&gt;Save Time&lt;/strong&gt; – Forget the manual calculations; &lt;strong&gt;UnitFlux&lt;/strong&gt; does the work for you!&lt;/li&gt;
&lt;li&gt;🖌️ &lt;strong&gt;Enhanced CSS Workflow&lt;/strong&gt; – Perfect for designers and developers aiming for fluid, responsive layouts without the hassle.&lt;/li&gt;
&lt;/ul&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%2Fxqji8t8qqywr21yy7b8h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxqji8t8qqywr21yy7b8h.png" alt="UnitFlux Code Lens" width="800" height="491"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🔄 &lt;strong&gt;CodeMorph: Unicode Compatibility Made Easy&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Ever had issues with non-ASCII characters in JSON files? 🐛&lt;/p&gt;

&lt;p&gt;That’s where &lt;strong&gt;CodeMorph&lt;/strong&gt; steps in. This extension allows you to quickly convert any non-ASCII characters in JSON files to Unicode escape sequences, ensuring compatibility across various environments and systems. ✅&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Why Use CodeMorph?&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🌍 &lt;strong&gt;Ensures JSON Compatibility&lt;/strong&gt; – Unicode-escape all non-ASCII characters to avoid encoding issues in JSON.&lt;/li&gt;
&lt;li&gt;💻 &lt;strong&gt;Effortless Transformation&lt;/strong&gt; – Convert characters with a click and make your JSON files universally readable.&lt;/li&gt;
&lt;li&gt;✨ &lt;strong&gt;Handy Commands&lt;/strong&gt; – Easy-to-trigger command from the Command Palette and Context Menu (File Explorer).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 With CodeMorph, say goodbye to encoding headaches and ensure your JSON files are ready for any system!&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 Ready to Try Them Out?
&lt;/h2&gt;

&lt;p&gt;Both &lt;strong&gt;UnitFlux&lt;/strong&gt; and &lt;strong&gt;CodeMorph&lt;/strong&gt; are available now on the &lt;a href="https://marketplace.visualstudio.com/publishers/AlvinJamesBellero" rel="noopener noreferrer"&gt;VSCode Marketplace&lt;/a&gt;! 🎉 &lt;/p&gt;

&lt;p&gt;Check them out, give them a spin, and let me know what you think. I’d love your feedback as I continue to improve these tools and add new features!&lt;/p&gt;

&lt;p&gt;📸 Cover Photo by &lt;a href="https://unsplash.com/@pankajpatel?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Pankaj Patel&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/turned-on-flat-screen-monitor-4oAFasAPftg?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExN2J0bzU5eDZuYjh4Nm1wMXdpYmNvcmtqbXZtbnVtaG10aGZpaXlyOCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/BoHCeLmEKytt7oFxyR/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExN2J0bzU5eDZuYjh4Nm1wMXdpYmNvcmtqbXZtbnVtaG10aGZpaXlyOCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/BoHCeLmEKytt7oFxyR/giphy.gif" width="480" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>extensions</category>
      <category>productivity</category>
      <category>developers</category>
    </item>
    <item>
      <title>💼 Marking One Year in Singapore: My Life as an Expat Software Engineer</title>
      <dc:creator>Alvin Bellero</dc:creator>
      <pubDate>Tue, 04 Jul 2023 16:56:48 +0000</pubDate>
      <link>https://dev.to/shiftescape/marking-one-year-in-singapore-my-life-as-an-expat-software-engineer-10h6</link>
      <guid>https://dev.to/shiftescape/marking-one-year-in-singapore-my-life-as-an-expat-software-engineer-10h6</guid>
      <description>&lt;p&gt;&lt;a href="https://i.giphy.com/media/l2JeikcLCAHdLdUac/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/l2JeikcLCAHdLdUac/giphy.gif" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  From Home to A New Horizon
&lt;/h2&gt;

&lt;p&gt;Just over a year ago, I packed my bags 🧳 and embarked on an adventure, trading the comfort of my home country, the &lt;strong&gt;Philippines&lt;/strong&gt; 🇵🇭 for the vibrant city of &lt;strong&gt;Singapore&lt;/strong&gt; 🇸🇬.&lt;/p&gt;

&lt;p&gt;I took on an exciting role at the global tech giant, &lt;a href="https://www.equinix.se/" rel="noopener noreferrer"&gt;Equinix&lt;/a&gt;, as a Software Engineer 👨‍💻. Now, I'm looking back and celebrating 🎉 the milestone of my first work-anniversary in this exhilarating role!&lt;/p&gt;




&lt;h2&gt;
  
  
  Building Resilience
&lt;/h2&gt;

&lt;p&gt;Navigating life 🧭 in an unfamiliar country as an expat, especially for the first time, has indeed been challenging. &lt;/p&gt;

&lt;p&gt;Living alone, away from my family, has at times been a lonely 😔 experience. Searching for a place 🏠 to call home in a city foreign to me was one of the first obstacles I faced. I sifted through numerous listings 🗂️, contacted lots of strangers and visited countless properties 🏢 before finally finding a place I could settle into 🛋️.&lt;/p&gt;

&lt;p&gt;Yet, amidst these challenges, I found &lt;strong&gt;resilience&lt;/strong&gt; and &lt;strong&gt;perseverance&lt;/strong&gt;. Every hurdle faced was a step closer to making Singapore feel like home. I was &lt;strong&gt;fuelled by my passion&lt;/strong&gt;, the drive to &lt;strong&gt;broaden my horizons&lt;/strong&gt;, and above all, the thought of &lt;strong&gt;providing a better life for my family&lt;/strong&gt; ❤️.&lt;/p&gt;




&lt;h2&gt;
  
  
  Embracing Cultural Diversity
&lt;/h2&gt;

&lt;p&gt;Living in Singapore as an expat has also been an enriching journey. Apart from honing my professional skills, I've been immersed in a &lt;strong&gt;melting pot&lt;/strong&gt; of diverse cultures, languages, and traditions 🎏.&lt;/p&gt;

&lt;p&gt;One of the highlights is discovering the love for Indian food 🥘. My taste buds have embarked on an adventure of their own, exploring the symphony of spices and flavours in dishes like &lt;strong&gt;mutton curry&lt;/strong&gt;, &lt;strong&gt;butter chicken&lt;/strong&gt;, &lt;strong&gt;biryani&lt;/strong&gt;, &lt;strong&gt;naan&lt;/strong&gt; and many more! 🍗 🫓 🤤.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;I fondly recall a friend, jokingly suggesting that I must have been Indian in my past life!&lt;/em&gt; 🤣 ❤️&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Moreover, I've also developed a special fondness for local delights such as &lt;strong&gt;&lt;a href="https://www.visitsingapore.com/dining-drinks-singapore/local-dishes/laksa/" rel="noopener noreferrer"&gt;Laksa&lt;/a&gt;&lt;/strong&gt;, &lt;em&gt;a spicy noodle soup&lt;/em&gt; 🍜, &lt;strong&gt;&lt;a href="https://www.visitsingapore.com/editorials/types-of-chicken-rice/" rel="noopener noreferrer"&gt;Hainanese Chicken Rice&lt;/a&gt;&lt;/strong&gt;, &lt;em&gt;Singapore's national dish&lt;/em&gt; 🍛 and my Perfect Pair, a cup of &lt;a href="https://www.visitsingapore.com/editorials/order-kopi-like-a-local/" rel="noopener noreferrer"&gt;&lt;strong&gt;Kopi&lt;/strong&gt;&lt;/a&gt; ☕ and &lt;a href="https://www.visitsingapore.com/dining-drinks-singapore/local-dishes/kaya-toast/" rel="noopener noreferrer"&gt;&lt;strong&gt;Kaya Toast&lt;/strong&gt;&lt;/a&gt; 🥪.&lt;/p&gt;

&lt;p&gt;These culinary journeys have not only &lt;strong&gt;tantalized&lt;/strong&gt; my palate but also provided a comfort away from home 😌.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Year of Extraordinary Experiences
&lt;/h2&gt;

&lt;p&gt;My journey 🗺️ in the past year has been nothing short of extraordinary. I've had the privilege to work and contribute alongside some of the best people here in &lt;strong&gt;Singapore&lt;/strong&gt; and &lt;strong&gt;Silicon Valley&lt;/strong&gt; 👩‍💻👨‍💻, an experience that has enriched my professional life immensely.&lt;/p&gt;

&lt;p&gt;Participating in an internal (global) &lt;a href="https://www.linkedin.com/posts/equinix_2022-equinix-hackathon-activity-6994010525122625536-VAJ1?utm_source=share&amp;amp;utm_medium=member_desktop" rel="noopener noreferrer"&gt;&lt;strong&gt;Hackathon&lt;/strong&gt;&lt;/a&gt; was a standout moment, where our team secured the &lt;strong&gt;People's Choice Award&lt;/strong&gt; 🏆 (&lt;em&gt;out of 41 teams participated&lt;/em&gt;), a testament to our combined efforts and ingenuity.&lt;/p&gt;

&lt;p&gt;I've met also an array of cool people, including fellow &lt;strong&gt;Filipinos&lt;/strong&gt; 🇵🇭, creating a sense of community in this foreign land.&lt;/p&gt;

&lt;p&gt;I've attended countless tech &lt;strong&gt;meetups&lt;/strong&gt; and &lt;strong&gt;events&lt;/strong&gt; 🎟️, each one a learning opportunity and a chance to network with like-minded individuals. &lt;/p&gt;




&lt;h2&gt;
  
  
  Giving Back to the Tech Community
&lt;/h2&gt;

&lt;p&gt;In addition to my work at Equinix, I've expanded my contributions to the &lt;strong&gt;open-source&lt;/strong&gt; community 👨‍💻. I've had the opportunity to create tools and solutions that can help fellow developers—which has been incredibly &lt;strong&gt;rewarding&lt;/strong&gt; 🎁.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;I regularly update my &lt;a href="https://github.com/shiftEscape" rel="noopener noreferrer"&gt;GitHub Profile&lt;/a&gt; with all of my latest works! 🌟&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This year, I also began my &lt;strong&gt;own blog&lt;/strong&gt; 📝 (yes, this blog!). It's always been a &lt;strong&gt;fun&lt;/strong&gt; way to &lt;strong&gt;share my ideas&lt;/strong&gt; and &lt;strong&gt;connect&lt;/strong&gt; with other &lt;strong&gt;tech enthusiasts&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Reflections and Future
&lt;/h2&gt;

&lt;p&gt;Celebrating one year as an Expat Software Engineer isn't just a work-anniversary, it's a &lt;strong&gt;nod&lt;/strong&gt; to my journey of &lt;strong&gt;growth&lt;/strong&gt; and a promise of the &lt;strong&gt;exciting adventures&lt;/strong&gt; that lie ahead.&lt;/p&gt;

&lt;p&gt;Here's to a fantastic year, the lessons learned 🎓, the friendships made, and the &lt;strong&gt;thrilling journey&lt;/strong&gt; that awaits! 🚀&lt;/p&gt;




&lt;p&gt;&lt;a href="https://i.giphy.com/media/8Iv5lqKwKsZ2g/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/8Iv5lqKwKsZ2g/giphy.gif" width="478" height="200"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Cover Photo by &lt;a href="https://unsplash.com/@nilsnedel?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Nils Nedel&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/ONpGBpns3cs?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>career</category>
      <category>yearinreview</category>
      <category>expatinsingapore</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>📦 Stay Organized and Efficient with Auto-Label-Pulls Github Action</title>
      <dc:creator>Alvin Bellero</dc:creator>
      <pubDate>Sat, 01 Jul 2023 03:34:12 +0000</pubDate>
      <link>https://dev.to/shiftescape/stay-organized-and-efficient-with-auto-label-pulls-github-action-29pi</link>
      <guid>https://dev.to/shiftescape/stay-organized-and-efficient-with-auto-label-pulls-github-action-29pi</guid>
      <description>&lt;h2&gt;
  
  
  ❓ What is &lt;code&gt;auto-label-pulls&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/marketplace/actions/auto-label-pulls" rel="noopener noreferrer"&gt;Auto Label Pulls&lt;/a&gt; is a robust &lt;a href="https://github.com/features/actions" rel="noopener noreferrer"&gt;GitHub Action&lt;/a&gt; that can significantly streamline the process of labeling Pull Requests. Its &lt;code&gt;branch-based&lt;/code&gt; mechanism offers the flexibility to cater to various project needs, making it an indispensable tool for any GitHub project maintainer.&lt;/p&gt;

&lt;p&gt;Whether you have a small project or manage a large repository with tons of pull requests, &lt;code&gt;auto-label-pulls&lt;/code&gt; can help you stay organized and save valuable time.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://i.giphy.com/media/c4Nc0v0g15g9G/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/c4Nc0v0g15g9G/giphy.gif" width="500" height="283"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  💡 Key Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Easy to Set Up:&lt;/strong&gt; Getting started with &lt;code&gt;auto-label-pulls&lt;/code&gt; is a breeze. All you need to do is include the action in your GitHub workflow file and configure the necessary parameters.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Automatic Label Assignment&lt;/strong&gt;: Labels are automatically assigned to new pull requests based on the target branch, removing the need for manual labelling.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Customizable&lt;/strong&gt;: You can configure which labels correspond to which branches, allowing you to use in a way that suits your project's needs.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;Using &lt;code&gt;auto-label-pulls&lt;/code&gt; is simple and straightforward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;First&lt;/strong&gt;, add the action to your GitHub workflow file (&lt;code&gt;.github/workflows/main.yml&lt;/code&gt;).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Workflow Name&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;opened&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Assign labels&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;shiftEscape/auto-label-pulls@v1.0.0&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;secrets.GITHUB_TOKEN&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;config-path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.github/config/auto-label-pulls.json"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;NOTE: Use this action only for &lt;code&gt;pull_request&lt;/code&gt; event of type &lt;code&gt;opened&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Second&lt;/strong&gt;, create a configuration file (&lt;code&gt;.github/config/auto-label-pulls.json&lt;/code&gt;) for the mapping of &lt;code&gt;labels&lt;/code&gt; to &lt;code&gt;branches&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&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;"master"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"🚀 Production Deployment"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"develop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"🧪 SIT Deployment"&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;blockquote&gt;
&lt;p&gt;In the above example, when a &lt;code&gt;Pull Request&lt;/code&gt; is made to &lt;code&gt;master&lt;/code&gt; branch, "🚀 Production Deployment" label will be assigned to that PR and so on and so forth.&lt;/p&gt;

&lt;p&gt;💡 Visit the Action's Dashboard for more info: &lt;a href="https://github.com/marketplace/actions/auto-label-pulls" rel="noopener noreferrer"&gt;https://github.com/marketplace/actions/auto-label-pulls&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  👏 And there you have it, folks!
&lt;/h2&gt;

&lt;p&gt;By incorporating &lt;code&gt;auto-label-pulls&lt;/code&gt; into your GitHub workflow, you're not just organizing your process – you're revolutionizing it.&lt;/p&gt;

&lt;p&gt;Explore the tool, share your thoughts, and let's transform the &lt;code&gt;open-source&lt;/code&gt; space together!&lt;/p&gt;

&lt;p&gt;Thanks for reading! 😉&lt;/p&gt;




&lt;p&gt;&lt;a href="https://i.giphy.com/media/UQaRUOLveyjNC/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/UQaRUOLveyjNC/giphy.gif" width="480" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>autolabelpulls</category>
      <category>opensource</category>
      <category>workflows</category>
      <category>githubactions</category>
    </item>
    <item>
      <title>🤖 ChatGPT meets Firestore: Create Intelligent Conversations in Your App</title>
      <dc:creator>Alvin Bellero</dc:creator>
      <pubDate>Fri, 30 Jun 2023 15:51:52 +0000</pubDate>
      <link>https://dev.to/shiftescape/chatgpt-meets-firestore-create-intelligent-conversations-in-your-app-11mj</link>
      <guid>https://dev.to/shiftescape/chatgpt-meets-firestore-create-intelligent-conversations-in-your-app-11mj</guid>
      <description>&lt;h3&gt;
  
  
  👋 🤝 Hello, everyone! It's been a long time!
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/l1J9urAfGd3grKV6E/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/l1J9urAfGd3grKV6E/giphy.gif" width="480" height="216"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚡️ Quick Story
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;I can still vividly recall the excitement I felt when Firebase announced &lt;a href="https://firebase.google.com/products/extensions" rel="noopener noreferrer"&gt;Extensions&lt;/a&gt; at the &lt;a href="https://youtu.be/DkXtV-XzYOQ" rel="noopener noreferrer"&gt;2019 Summit in Madrid, Spain&lt;/a&gt;. I immediately saw the immense value they could bring to us developers, especially for those of us heavily relying on Firebase products.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, as AI's popularity soars, I've seen a multitude of innovative applications powered by this technology, giving rise to increasingly beneficial products. Truly, the &lt;strong&gt;sky's the limit&lt;/strong&gt; when it comes to AI's potential!&lt;/p&gt;




&lt;h2&gt;
  
  
  👀 What's New?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;It's been a week now since the Firebase Team approved my first-ever Firebase Extension: &lt;a href="https://extensions.dev/extensions/shiftescape/firestore-chatgpt-bot" rel="noopener noreferrer"&gt;&lt;strong&gt;Chatbot with ChatGPT&lt;/strong&gt;&lt;/a&gt; ! 🥳 👏 🍾&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is an extension that integrates the &lt;a href="https://openai.com/chatgpt" rel="noopener noreferrer"&gt;ChatGPT Model from OpenAI&lt;/a&gt; and designed to facilitate AI-based conversations in real-time chat applications backed by &lt;a href="https://firebase.google.com/docs/firestore" rel="noopener noreferrer"&gt;Firestore&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;By making use of Firebase and ChatGPT's powerful language processing capabilities, this extension empowers developers to implement AI-based automated interactions within their apps, making them more engaging and interactive!&lt;/p&gt;




&lt;h2&gt;
  
  
  📌 How to Use
&lt;/h2&gt;

&lt;p&gt;Setting up and using &lt;code&gt;Chatbot with ChatGPT&lt;/code&gt; is easy.&lt;/p&gt;

&lt;p&gt;Go to the &lt;a href="https://extensions.dev/extensions/shiftescape/firestore-chatgpt-bot" rel="noopener noreferrer"&gt;extensions page&lt;/a&gt; and click &lt;code&gt;Install in Firebase console&lt;/code&gt; button. Follow the prompts, and you're good to go!&lt;/p&gt;




&lt;p&gt;&lt;a href="https://i.giphy.com/media/G5XoVH1Rh8tL7MbC5f/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/G5XoVH1Rh8tL7MbC5f/giphy.gif" width="480" height="262"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚙️ Configurable Parameters
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Collection Path&lt;/code&gt;&lt;/strong&gt;: Path to a Cloud Firestore collection which will represent a discussion with OpenAI's ChatGPT.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Prompt Field&lt;/code&gt;&lt;/strong&gt;: The field in the message document that contains the prompt. (Default: &lt;code&gt;prompt&lt;/code&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Response Field&lt;/code&gt;&lt;/strong&gt;: The field in the message document into which to put the response. (Default: &lt;code&gt;response&lt;/code&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Cloud Functions Location&lt;/code&gt;&lt;/strong&gt;: Where do you want to deploy the functions created for this extension? For help selecting a location, refer to the &lt;a href="https://firebase.google.com/docs/functions/locations" rel="noopener noreferrer"&gt;location selection guide&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Language Model&lt;/code&gt;&lt;/strong&gt;: Which language &lt;code&gt;model&lt;/code&gt; do you want to use? Refer to &lt;a href="https://platform.openai.com/docs/models/overview" rel="noopener noreferrer"&gt;OpenAI's Model Reference&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Temperature&lt;/code&gt;&lt;/strong&gt;: What sampling &lt;code&gt;temperature&lt;/code&gt; to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. &lt;a href="https://platform.openai.com/docs/api-reference/chat/create#chat/create-temperature" rel="noopener noreferrer"&gt;Learn more here&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Nucleus Sampling Probability&lt;/code&gt;&lt;/strong&gt;: An alternative to sampling with temperature, called &lt;code&gt;nucleus sampling&lt;/code&gt;, where the model considers the results of the tokens with &lt;code&gt;top_p&lt;/code&gt; probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. &lt;a href="https://platform.openai.com/docs/api-reference/chat/create#chat/create-top_p" rel="noopener noreferrer"&gt;Learn more here&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🎬 Demo
&lt;/h2&gt;

&lt;p&gt;For this demo, here's my configured params:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenAI API Key: &lt;code&gt;*******************&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Collection path: &lt;code&gt;users/{uid}/messages&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Prompt field: &lt;code&gt;(default - prompt)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Response field: &lt;code&gt;(default - response)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Language Model: &lt;code&gt;GPT-3.5&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&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%2Ftcnyls8je2t4o8y9fd41.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftcnyls8je2t4o8y9fd41.gif" alt="Chatbot with ChatGPT Demo" width="1284" height="788"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  That's all folks! 🤜🤛
&lt;/h2&gt;

&lt;p&gt;Give it a try, please share your experiences, thoughts, and suggestions. Also, please don't forget to share this to your friends! 😊&lt;/p&gt;

&lt;p&gt;Thank you so much! 🙇&lt;/p&gt;




&lt;p&gt;&lt;a href="https://i.giphy.com/media/12noFudALzfIynHuUp/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/12noFudALzfIynHuUp/giphy.gif" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>chatgpt</category>
      <category>firebase</category>
      <category>opensource</category>
      <category>chatbot</category>
    </item>
    <item>
      <title>🚀 Firebase Hosting: Free Hosting for your First Web Project</title>
      <dc:creator>Alvin Bellero</dc:creator>
      <pubDate>Sun, 14 May 2023 07:49:45 +0000</pubDate>
      <link>https://dev.to/shiftescape/firebase-hosting-free-hosting-for-your-first-web-project-28n8</link>
      <guid>https://dev.to/shiftescape/firebase-hosting-free-hosting-for-your-first-web-project-28n8</guid>
      <description>&lt;p&gt;Are you looking for a hosting solution for your first Web project? Look no further than Firebase Hosting! Firebase Hosting is a cloud-based hosting service provided by Google's Firebase platform, and it's completely free to use.&lt;/p&gt;

&lt;p&gt;What makes Firebase Hosting stand out is its powerful features and easy setup process. You can quickly deploy static web pages, single-page applications, and even dynamic server-side content. Plus, Firebase Hosting comes with automatic SSL, a global content delivery network, and easy integration with other Firebase services.&lt;/p&gt;

&lt;p&gt;So if you're a developer looking to launch your first web project, give Firebase Hosting a try. It's a powerful, flexible, and affordable hosting solution that can help you get up and running in no time!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 To learn more about Firebase Pricing, please check &lt;a href="https://firebase.google.com/pricing" rel="noopener noreferrer"&gt;this&lt;/a&gt; out.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;a href="https://i.giphy.com/media/BpGWitbFZflfSUYuZ9/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/BpGWitbFZflfSUYuZ9/giphy.gif" width="480" height="400"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🏗️ Setting up Firebase Hosting
&lt;/h2&gt;

&lt;p&gt;Setting up Firebase Hosting for your web project is a straightforward process. Follow these steps to get started:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create a Firebase account&lt;/strong&gt;&lt;br&gt;
If you don't already have a Firebase account, go to the &lt;strong&gt;&lt;a href="https://firebase.google.com/" rel="noopener noreferrer"&gt;Firebase Website&lt;/a&gt;&lt;/strong&gt; and sign up for a free account. Once you're logged in, you'll see the &lt;strong&gt;Console&lt;/strong&gt;, which is where you'll manage your Firebase projects.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create a Firebase project&lt;/strong&gt;&lt;br&gt;
In the Firebase console, click on the "&lt;strong&gt;Create a project&lt;/strong&gt;" button to create a new Firebase project. Follow the prompts to name your project and enable Google Analytics if you want to track user behaviour on your website (optional).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Install the Firebase CLI&lt;/strong&gt;&lt;br&gt;
The Firebase CLI (Command Line Interface) is a tool that you'll use to deploy your website to Firebase Hosting. To install the Firebase CLI (globally), open a terminal window and run the following command:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; firebase-tools
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Initialize your Firebase project&lt;/strong&gt;&lt;br&gt;
Once you have the Firebase CLI installed, navigate to your project directory in your terminal and run the following command:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;firebase init hosting
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 This will prompt you to the Firebase Hosting option. Follow the prompts to set up your project for hosting.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🚀 Deploying your website to Firebase Hosting
&lt;/h2&gt;

&lt;p&gt;With your project set up for hosting, you're now ready to deploy your website to Firebase Hosting. Follow these steps to deploy your website:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Build your website&lt;/strong&gt;&lt;br&gt;
Before you can deploy your website, you'll need to build it. This typically involves running a build command that compiles your source code into a production-ready format. The specific build command will depend on your project's build system. Once your website is built, you should have a directory containing your compiled files.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Deploy your website&lt;/strong&gt;&lt;br&gt;
In your terminal window, navigate to the directory containing your compiled files and run the following command:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;firebase deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 This will deploy your website to Firebase Hosting, and you'll be given a URL where you can access your website.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Congratulations! 🤝
&lt;/h2&gt;

&lt;p&gt;You've now set up Firebase Hosting for your web project and deployed your website. With its powerful features and easy deployment process, you'll be able to manage your website hosting needs with ease.&lt;/p&gt;

&lt;p&gt;😎💯👌&lt;/p&gt;




&lt;p&gt;&lt;a href="https://i.giphy.com/media/PlzGTGET28PVQwHJLL/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/PlzGTGET28PVQwHJLL/giphy.gif" width="480" height="269"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;P.S.&lt;/strong&gt; &lt;a href="https://youtu.be/rGqj6U7SawA" rel="noopener noreferrer"&gt;Catch up on the highlights from Firebase @ Google I/O ‘23&lt;/a&gt;! 🤯 ❤️&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>firebase</category>
    </item>
    <item>
      <title>🧑‍💻 Visual Studio Code Productivity Hacks: My Favourite 15 Extensions</title>
      <dc:creator>Alvin Bellero</dc:creator>
      <pubDate>Sat, 25 Feb 2023 18:34:17 +0000</pubDate>
      <link>https://dev.to/shiftescape/visual-studio-code-productivity-hacks-my-favourite-15-extensions-1gb7</link>
      <guid>https://dev.to/shiftescape/visual-studio-code-productivity-hacks-my-favourite-15-extensions-1gb7</guid>
      <description>&lt;p&gt;Do you use Visual Studio Code (VSCode) for your coding projects? If so, you know how important it is to have the right tools at your disposal.&lt;/p&gt;

&lt;p&gt;Luckily, there are a ton of extensions available that can help you streamline your workflow and boost your productivity.&lt;/p&gt;

&lt;p&gt;In this article, I'm going to share my top 15 favourite VSCode extensions that have become essential productivity hacks in my daily work.&lt;/p&gt;

&lt;p&gt;Whether you're a seasoned developer or just getting started with VSCode, these extensions are sure to help you get more done in less time.&lt;/p&gt;

&lt;p&gt;So, without further ado, let's dive in!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/UimdDmDNu5nf8fZKVM/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/UimdDmDNu5nf8fZKVM/giphy.gif" alt="Ready as I'll ever be! - Norman, Software Engineer, 25" width="400" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💁 — &lt;em&gt;John a.k.a "EverReady", &lt;strong&gt;Software Engineer&lt;/strong&gt;, 25&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  1. &lt;a href="https://marketplace.visualstudio.com/items?itemName=formulahendry.auto-rename-tag" rel="noopener noreferrer"&gt;Auto Rename Tag&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;⭐️ &lt;strong&gt;Jun Han&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💁 &lt;em&gt;Automatically rename paired HTML/XML tag.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;💡 This extension will save you time finding the other end of the existing tag during an update! Please do read their docs especially the &lt;a href="https://github.com/formulahendry/vscode-auto-rename-tag#note" rel="noopener noreferrer"&gt;Note&lt;/a&gt; section.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/l36kU80xPf0ojG0Erg/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/l36kU80xPf0ojG0Erg/giphy.gif" alt="Spiderman Pointing" width="498" height="278"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  2. &lt;a href="https://marketplace.visualstudio.com/items?itemName=aaron-bond.better-comments" rel="noopener noreferrer"&gt;Better Comments&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;⭐️ &lt;strong&gt;Aaron Bond&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💁 &lt;em&gt;Improve your code commenting by annotating with alert, informational, TODOs, and more!&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;💡 &lt;strong&gt;Commenting&lt;/strong&gt; is an essential part of programming that helps to improve code quality, readability, and maintainability. By taking the time to write clear and concise comments, you can make your code more accessible and easier to work with for yourself and other developers!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/VML2lNolrKpP5sQvUn/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/VML2lNolrKpP5sQvUn/giphy.gif" alt="Explain to me" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  3. &lt;a href="https://marketplace.visualstudio.com/items?itemName=oleg-shilo.codemap" rel="noopener noreferrer"&gt;CodeMap&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;⭐️ &lt;strong&gt;Oleg Shilo&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💁 &lt;em&gt;Interactive code map for quick visualization and navigation within code DOM objects (e.g. classes, members).&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;💡 I personally use this one to map custom patterns that I want including logging (debuggers | logs) &amp;amp; comments!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/myBz2xDdUTMmuACPD6/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/myBz2xDdUTMmuACPD6/giphy.gif" alt="Star Trek Captain" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  4. &lt;a href="https://marketplace.visualstudio.com/items?itemName=formulahendry.code-runner" rel="noopener noreferrer"&gt;Code Runner&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;⭐️ &lt;strong&gt;Jun Han&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💁 &lt;em&gt;Run code snippet or code file for multiple languages&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/yhRhIgnJIRD0I/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/yhRhIgnJIRD0I/giphy.gif" alt="Road Runner" width="491" height="300"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  5. &lt;a href="https://marketplace.visualstudio.com/items?itemName=anseki.vscode-color" rel="noopener noreferrer"&gt;Color Picker&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;⭐️ &lt;strong&gt;anseki&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💁 &lt;em&gt;Helper with GUI to generate color codes such as CSS color notations.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;💡 You can actually change your colors &lt;u&gt;IN PLACE&lt;/u&gt; by using the Color Picker. How cool is that? 🤯&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/l2SqdNw9wktsLTsn6/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/l2SqdNw9wktsLTsn6/giphy.gif" alt="Pick another color" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  6. &lt;a href="https://marketplace.visualstudio.com/items?itemName=AlvinJamesBellero.console-buddy" rel="noopener noreferrer"&gt;Console Buddy&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;⭐️ &lt;strong&gt;Yours Truly&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💁 (Shameless Plug) &lt;em&gt;A Snippet Extension that provides a collection of &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/console" rel="noopener noreferrer"&gt;Web Console API&lt;/a&gt; snippets that allows you to quickly generated logging preference!&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/5gw0VWGbgNm8w/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/5gw0VWGbgNm8w/giphy.gif" alt="I see what you did there" width="480" height="234"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  7. &lt;a href="https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint" rel="noopener noreferrer"&gt;ESLint&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;⭐️ &lt;strong&gt;Microsoft&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💁 &lt;em&gt;Integrates ESLint JavaScript into VS Code.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;💡 Thanks, Microsoft! 🙇🏻‍♂️&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/3o72FcJmLzIdYJdmDe/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/3o72FcJmLzIdYJdmDe/giphy.gif" alt="Happy Simon" width="500" height="281"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  8. &lt;a href="https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens" rel="noopener noreferrer"&gt;GitLens — Git supercharged&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;⭐️ &lt;strong&gt;GitKraken&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💁 &lt;em&gt;Visualize code authorship at a glance via Git blame annotations and CodeLens, seamlessly navigate and explore Git repositories, gain valuable insights via rich visualizations and powerful comparison commands, and so much more&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;💡 In case you wanna find out who wrote a crappy, garbage line of code(s).... But then it shows: "You · 3 months ago" 👀&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/COYGe9rZvfiaQ/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/COYGe9rZvfiaQ/giphy.gif" alt="Hiding" width="320" height="233"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  9. &lt;a href="https://marketplace.visualstudio.com/items?itemName=wix.vscode-import-cost" rel="noopener noreferrer"&gt;Import Cost&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;⭐️ &lt;strong&gt;Wix&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💁 &lt;em&gt;Display import/require package size in the editor&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;💡 Gives you more insights to all the packages you're installing. Something to think about.. 🤔💭&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/WRQBXSCnEFJIuxktnw/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/WRQBXSCnEFJIuxktnw/giphy.gif" alt="Woman thinking" width="504" height="322"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  10. &lt;a href="https://marketplace.visualstudio.com/items?itemName=moalamri.inline-fold" rel="noopener noreferrer"&gt;Inline fold&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;⭐️ &lt;strong&gt;Mohammed Alamri&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💁 &lt;em&gt;A custom decorator that "fold" matching content in single line&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;💡 The Tailwind CSS Framework is really great but it results in a lot of verbose classes. For that particular problem, this extension comes in handy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/12NUbkX6p4xOO4/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/12NUbkX6p4xOO4/giphy.gif" alt="Magic" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  11. &lt;a href="https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer" rel="noopener noreferrer"&gt;Live Server&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;⭐️ &lt;strong&gt;Ritwick Dey&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💁 &lt;em&gt;Launch a development local Server with live reload feature for static &amp;amp; dynamic pages&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;💡 This spins up a NodeJS server and serves your HTML file with all the assets from the given directory. On top of that, all of your changes will be reflected immediately upon saving! Pretty cool, huh? 😎&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/GwGXoeb0gm7sc/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/GwGXoeb0gm7sc/giphy.gif" alt="Cool Cats" width="400" height="225"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  12. &lt;a href="https://marketplace.visualstudio.com/items?itemName=oderwat.indent-rainbow" rel="noopener noreferrer"&gt;indent-rainbow&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;⭐️ &lt;strong&gt;oderwat&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💁 &lt;em&gt;A simple extension to make indentation more readable.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;💡 Love the RGB colours that matches with your fancy keyboard lighting? Check this out! 🌈&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/3ohhwERyIvr1VOVgQM/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/3ohhwERyIvr1VOVgQM/giphy.gif" alt="LOL keyboard" width="480" height="240"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  13. &lt;a href="https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode" rel="noopener noreferrer"&gt;Prettier - Code formatter&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;⭐️ &lt;strong&gt;Prettier&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💁 &lt;em&gt;Enforces a consistent style by parsing your code and re-printing it with its own rules that take the maximum line length into account, wrapping code when necessary.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;💡 &lt;em&gt;Speechless...&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/2yKXIJyTDdsTm/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/2yKXIJyTDdsTm/giphy.gif" alt="I have no words" width="431" height="244"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  14. &lt;a href="https://marketplace.visualstudio.com/items?itemName=SonarSource.sonarlint-vscode" rel="noopener noreferrer"&gt;SonarLint&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;⭐️ &lt;strong&gt;SonarSource&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💁 &lt;em&gt;Like a spell checker, SonarLint highlights Bugs and Security Vulnerabilities as you write code, with clear remediation guidance so you can fix them before the code is even committed&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;💡 We always learn from our &lt;strong&gt;misteaks&lt;/strong&gt; 🙃🥩&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/XjlNyeZp5lDri/giphy-downsized-large.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/XjlNyeZp5lDri/giphy-downsized-large.gif" alt="Fixing a bug in production" width="400" height="307"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  15. &lt;a href="https://marketplace.visualstudio.com/items?itemName=rangav.vscode-thunder-client" rel="noopener noreferrer"&gt;Thunder Client&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;⭐️ &lt;strong&gt;Ranga Vadhineni&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💁 &lt;em&gt;Lightweight Rest API Client for VS Code&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/pL6mkjytFkTlekNMtM/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/pL6mkjytFkTlekNMtM/giphy.gif" alt="Thor Marvel" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;That's all folks! 🤜🤛&lt;/p&gt;

&lt;p&gt;Are there any other extensions that you use? If so, be sure to let me know in the comments! 😉💯&lt;/p&gt;




&lt;p&gt;&lt;a href="https://i.giphy.com/media/lrPNVta5uLk9VvhHgd/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/lrPNVta5uLk9VvhHgd/giphy.gif" alt="Pointing down to comments" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>productivity</category>
      <category>extensions</category>
      <category>webdev</category>
    </item>
    <item>
      <title>🌎 Publishing Your Extensions to Visual Studio Marketplace</title>
      <dc:creator>Alvin Bellero</dc:creator>
      <pubDate>Sun, 19 Feb 2023 01:55:50 +0000</pubDate>
      <link>https://dev.to/shiftescape/publishing-your-extensions-to-visual-studio-marketplace-49ma</link>
      <guid>https://dev.to/shiftescape/publishing-your-extensions-to-visual-studio-marketplace-49ma</guid>
      <description>&lt;p&gt;In today's world, technology is evolving at a fast pace, and developers are constantly looking for ways to improve their productivity and streamline their workflow. One of the tools that has gained immense popularity in recent years is &lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;Visual Studio Code (VSCode)&lt;/a&gt;. With its robust set of features and extensive library of extensions, VSCode has become the go-to code editor for many developers.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 But did you know that you can also create your own extensions for VSCode and share it with the community? If you missed my last post about &lt;em&gt;"Creating Your First VSCode Extension Theme"&lt;/em&gt;, you can check that out &lt;strong&gt;&lt;a href="https://dev.to/shiftescape/creating-your-first-vscode-extension-9d4"&gt;here&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this article, we will explore the process of publishing your own extensions to the VSCode Marketplace. Whether you're looking to solve a specific problem, add new features, or just share your work with others, publishing extensions to the marketplace is an excellent way to contribute to the developer community and improve your own development experience. 🙌 😉&lt;/p&gt;




&lt;p&gt;&lt;a href="https://i.giphy.com/media/xjEmbSLychDd6JQFo0/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/xjEmbSLychDd6JQFo0/giphy.gif" alt="Seems like a winning combo to me" width="499" height="499"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  ✨ Setup You Microsoft Azure DevOps Account
&lt;/h2&gt;

&lt;p&gt;As you've probably know, VSCode is created and maintained by Microsoft. With that, we now have a single login for Azure Cloud Services and VSCode Extensions.&lt;/p&gt;

&lt;p&gt;Head on to &lt;a href="https://dev.azure.com/" rel="noopener noreferrer"&gt;https://dev.azure.com/&lt;/a&gt; website and create an account. You can opt to sign in with your Github account or just register with your personal email.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Once you've successfully registered, you will be redirected to your Azure DevOps Dashboard page under your created Organization name.&lt;/p&gt;
&lt;/blockquote&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%2Fiqvjd9fayb029pfbvhg2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiqvjd9fayb029pfbvhg2.png" alt="Azure DevOps Dashboard" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🪙 Create your Personal Access Token (PAT)
&lt;/h2&gt;

&lt;p&gt;This will be used as your authentication in publishing your extensions in the marketplace.&lt;/p&gt;

&lt;p&gt;Click on the &lt;strong&gt;User Profile&lt;/strong&gt; settings icon beside your avatar and choose &lt;strong&gt;Personal access tokens&lt;/strong&gt;.&lt;/p&gt;

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




&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;New Token&lt;/strong&gt; to create your Token.&lt;/li&gt;
&lt;li&gt;Add your Token Name&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;Organization&lt;/strong&gt;, select &lt;strong&gt;All accessible organizations&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;Expiration&lt;/strong&gt;, select &lt;strong&gt;Custom defined&lt;/strong&gt; which will default to 1 year from now as the maximum expiration time&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;Scopes&lt;/strong&gt;, make sure &lt;strong&gt;Custom defined&lt;/strong&gt; is selected.&lt;/li&gt;
&lt;li&gt;At the bottom, click &lt;strong&gt;Show all scopes&lt;/strong&gt; and scroll down to Marketplace section&lt;/li&gt;
&lt;li&gt;Select the &lt;strong&gt;Manage&lt;/strong&gt; option&lt;/li&gt;
&lt;li&gt;And lastly, click the blue &lt;strong&gt;Create&lt;/strong&gt; button&lt;/li&gt;
&lt;/ol&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%2Ff9fhnordknfsorr6gwj0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff9fhnordknfsorr6gwj0.png" alt="Create Personal Access Token Modal" width="800" height="1184"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🚨 &lt;strong&gt;WARNING:&lt;/strong&gt; Make sure you &lt;u&gt;&lt;strong&gt;SAVE A COPY&lt;/strong&gt;&lt;/u&gt; of your newly created access token (PAT) as Azure will not store it and you will not be able to see it again. And most importantly, &lt;strong&gt;don't share it to anyone&lt;/strong&gt;! 👀 🔐&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/l0KQbLPpeHjKPnv6o/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/l0KQbLPpeHjKPnv6o/giphy.gif" alt="Are you still with me?" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  📝 Create a Publisher
&lt;/h2&gt;

&lt;p&gt;This will be your &lt;strong&gt;Publisher Name&lt;/strong&gt; attached to your extension when it is available to the marketplace.&lt;/p&gt;

&lt;p&gt;Navigate to: &lt;a href="https://marketplace.visualstudio.com/manage" rel="noopener noreferrer"&gt;https://marketplace.visualstudio.com/manage&lt;/a&gt; and you should see a &lt;strong&gt;Create Publisher&lt;/strong&gt; page. The only required fields here are the &lt;strong&gt;Name&lt;/strong&gt; and &lt;strong&gt;ID&lt;/strong&gt; of the publisher. The rest is optional.&lt;/p&gt;

&lt;p&gt;If all goes well, you will be redirected to &lt;strong&gt;Manage Publishers &amp;amp; Extensions&lt;/strong&gt; page. Keep that page open as we will be returning to it later.&lt;/p&gt;




&lt;h2&gt;
  
  
  📦 Prepare Your Extension
&lt;/h2&gt;

&lt;p&gt;Here are some steps to ensure your extension's smooth transition to marketplace.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open your extension's folder in VSCode and go to your &lt;strong&gt;&lt;code&gt;package.json&lt;/code&gt;&lt;/strong&gt; file and make sure these properties are being set.
&lt;/li&gt;
&lt;/ul&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;"publisher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;Your Publisher ID&amp;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;"repository"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"git"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"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;"&amp;lt;Your repository URL&amp;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;"keywords"&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;"&amp;lt;Keyword 1&amp;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;"&amp;lt;Keyword 2&amp;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;blockquote&gt;
&lt;p&gt;💡 NOTE: Make sure your &lt;strong&gt;&lt;code&gt;LICENSE&lt;/code&gt;&lt;/strong&gt; file is existing on your ROOT repository as VSCode will check this during packaging / publishing.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🚀 Package and Publish it!
&lt;/h2&gt;

&lt;p&gt;Once your extension is ready to go, you can package and publish it by using the terminal only.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open the integrated terminal in VSCode and install their extension plugin by running:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; vsce
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Login to your publisher's account using &lt;code&gt;vsce&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vsce login &amp;lt;You Publisher ID&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🔐 You will then be prompted to input your &lt;code&gt;Personal Access Token (PAT)&lt;/code&gt;. Copy and paste it there and tap &lt;code&gt;Enter&lt;/code&gt; key.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Next, you can build a package of your theme by running:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vsce package
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 The plugin will automatically create a &lt;code&gt;.vsix&lt;/code&gt; file in your extension's root directory indicating the package version on the filename. Make sure also &lt;strong&gt;NOT&lt;/strong&gt; to check in this file to your repository.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;To publish it to marketplace, run:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vsce publish
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;To verify the status of your deployment, navigate to your &lt;strong&gt;Manage Publishers &amp;amp; Extensions&lt;/strong&gt; page and there you can see your extension's in &lt;strong&gt;"Verifying"&lt;/strong&gt; status.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 This may take a while and as soon as this process is complete, you can now see your extension's version and a green check mark before it!&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🚧 Updating your Extension
&lt;/h2&gt;

&lt;p&gt;For version updates, make sure you have a clean GIT working directory before publishing a &lt;strong&gt;&lt;code&gt;PATCH&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;MINOR&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;MAJOR&lt;/code&gt;&lt;/strong&gt; update.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vsce publish &amp;lt;patch|minor|major&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The plugin will automatically handle the semantic versioning so you won't have to manually do it by yourself.&lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;TIP:&lt;/strong&gt; Run &lt;code&gt;vsce package&lt;/code&gt; every time you publish a new update 😉&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🎉 Congratulations!
&lt;/h2&gt;

&lt;p&gt;And there you have it! Your extension is now available to the community for installation! Share it to your social media and friends! ❤️&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/Qs0QEnugOy0xIsFkpD/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/Qs0QEnugOy0xIsFkpD/giphy.gif" alt="Ricky and Morty - Good Job!" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>productivity</category>
      <category>blockchain</category>
      <category>ai</category>
    </item>
    <item>
      <title>🎨 Creating Your First VSCode Extension Theme</title>
      <dc:creator>Alvin Bellero</dc:creator>
      <pubDate>Wed, 15 Feb 2023 14:05:31 +0000</pubDate>
      <link>https://dev.to/shiftescape/creating-your-first-vscode-extension-9d4</link>
      <guid>https://dev.to/shiftescape/creating-your-first-vscode-extension-9d4</guid>
      <description>&lt;p&gt;&lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;Visual Studio Code (VSCode)&lt;/a&gt; is a popular open-source code editor developed by &lt;a href="https://www.microsoft.com/" rel="noopener noreferrer"&gt;Microsoft&lt;/a&gt;. With its flexible and extensible architecture, developers can customise and enhance their editing experience by creating their own extensions.&lt;/p&gt;

&lt;p&gt;Recently, I decided to publish my own, preferred theme to &lt;a href="https://marketplace.visualstudio.com/search?term=themenemo&amp;amp;target=VSCode&amp;amp;category=All%20categories&amp;amp;sortBy=Relevance" rel="noopener noreferrer"&gt;Visual Studio Marketplace&lt;/a&gt;, &lt;strong&gt;ThemeNemo&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a title="Click to try it out!" href="https://marketplace.visualstudio.com/items?itemName=AlvinJamesBellero.theme-nemo" rel="noopener noreferrer"&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%2Fft46dd43tr4u01pczdqj.png" alt="ThemeNemo in VSCode Marketplace" width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you're looking for other themes, please check this out! 💯 🤜🤛&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;In this article, I will guide you through the process of creating your first theme (based on my own experience).&lt;/p&gt;

&lt;p&gt;Whether you're a seasoned developer or just getting started, this will help you get up and running.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/l4EoNaDgYsHa6vL9K/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/l4EoNaDgYsHa6vL9K/giphy.gif" alt="This is your lucky day!" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚡ Set up your Development Environment
&lt;/h2&gt;

&lt;p&gt;To start creating your VSCode extension, you'll need to set up a development environment on your machine. This typically involves installing Visual Studio Code and Node.js.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For &lt;strong&gt;VSCode&lt;/strong&gt; Installation, refer &lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;Node&lt;/strong&gt; and &lt;strong&gt;NPM&lt;/strong&gt; installation, you can check this &lt;a href="https://dev.to/whotarusharora/how-to-install-npm-and-node-js-on-mac-and-windows-35he"&gt;article&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ✨ Create a New Extension Project
&lt;/h2&gt;

&lt;p&gt;Once your development environment is set up, you can create a new extension project using the VSCode Extension Development Kit (VSCode-EDK) or the Yeoman generator for VSCode extensions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g yo generator-code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🏃 Run "&lt;code&gt;Yo Code&lt;/code&gt;"
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://yeoman.io/" rel="noopener noreferrer"&gt;Yeoman Generator&lt;/a&gt; will walk you through the steps required to create your customisation or extension prompting for the required information.&lt;/p&gt;

&lt;p&gt;To launch the generator simply type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yo code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://en.wikipedia.org/wiki/Command-line_interface" rel="noopener noreferrer"&gt;CLI&lt;/a&gt; will walk you through the generation process and asks you about your extension's preference. Please refer to the actual generation below.&lt;/p&gt;

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




&lt;h3&gt;
  
  
  AND ... VOILA! 🎉
&lt;/h3&gt;

&lt;p&gt;You have now successfully generated your theme files! Open it in VSCode and here's the file list looks like.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://i.giphy.com/media/9G52mAJTInGVy/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/9G52mAJTInGVy/giphy.gif" alt="Fist Bump Baymax" width="700" height="289"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;AWESOME!&lt;/strong&gt; ... But wait! There's more! 👀&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🔧 Debug Your Extension
&lt;/h2&gt;

&lt;p&gt;You may be wondering how the default theme looks like. To view it, go to your &lt;strong&gt;Menu&lt;/strong&gt; → &lt;strong&gt;Run&lt;/strong&gt; and choose &lt;strong&gt;Start Debugging&lt;/strong&gt; (F5).&lt;/p&gt;

&lt;p&gt;This will open a new instance of VSCode Editor and provides you your debugger tools from your main editor.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;This is the bare, default theme of VSCode 😕&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🎨 Customise Your Theme
&lt;/h2&gt;

&lt;p&gt;Start changing the colour values inside the JSON file. It will automatically be reflected to the Debugging Window after you save it.&lt;/p&gt;

&lt;p&gt;Isn't it nice? 🤯&lt;/p&gt;




&lt;h2&gt;
  
  
  💥 Bonus Tip!
&lt;/h2&gt;

&lt;p&gt;During the process of my theme creation, it is really hard for me tell which part of VSCode is changing whenever I update a particular set of colours from the JSON file.&lt;/p&gt;

&lt;p&gt;So I searched the internet and found this gem! 💎&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://themes.vscode.one/" rel="noopener noreferrer"&gt;https://themes.vscode.one/&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;With &lt;strong&gt;Theme Studio&lt;/strong&gt;, you can easily customise your own theme using their visual tools, change it the way you like and even get inspiration from existing themes in the community by forking their themes! 💯&lt;/p&gt;
&lt;/blockquote&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%2Ffjcn04qegx9efr4tunzm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffjcn04qegx9efr4tunzm.png" alt="Theme Studio - Editing ThemeNemo" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 Publish Your Theme Extension
&lt;/h2&gt;

&lt;p&gt;Finally, once your extension is complete and tested, you can publish it to the VSCode Marketplace so that others can use it.&lt;/p&gt;

&lt;p&gt;This involves creating a publisher account, packaging your extension, and submitting it for review by the VSCode Team.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚡ UPDATE: I created a separate post for that &lt;a href="https://dev.to/shiftescape/publishing-your-extensions-to-visual-studio-marketplace-49ma"&gt;here&lt;/a&gt; if you want to know more.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Just follow the steps and see your theme in the marketplace in all of its glory!&lt;/p&gt;

&lt;p&gt;That's all folks! You can also share your experience in the comments section below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. Don't forget about your theme logo!&lt;/strong&gt; 😆&lt;/p&gt;




&lt;p&gt;&lt;a href="https://i.giphy.com/media/nTqBNQXlEixLa/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/nTqBNQXlEixLa/giphy.gif" alt="SpongeBob, we did it!" width="499" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>opensource</category>
      <category>theme</category>
      <category>themenemo</category>
    </item>
  </channel>
</rss>
