<?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: Jakub Freisler FRS</title>
    <description>The latest articles on DEV Community by Jakub Freisler FRS (@jakfrs).</description>
    <link>https://dev.to/jakfrs</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%2F3950276%2Fc8315aaa-b2da-43f1-bb26-36a86cf35e4c.png</url>
      <title>DEV Community: Jakub Freisler FRS</title>
      <link>https://dev.to/jakfrs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jakfrs"/>
    <language>en</language>
    <item>
      <title>`skills-npm`: a Stable Way to Distribute and Maintain Agent Skills</title>
      <dc:creator>Jakub Freisler FRS</dc:creator>
      <pubDate>Wed, 27 May 2026 11:11:37 +0000</pubDate>
      <link>https://dev.to/jakfrs/skills-npm-a-stable-way-to-distribute-and-maintain-agent-skills-3dfb</link>
      <guid>https://dev.to/jakfrs/skills-npm-a-stable-way-to-distribute-and-maintain-agent-skills-3dfb</guid>
      <description>&lt;h1&gt;
  
  
  &lt;code&gt;skills-npm&lt;/code&gt;: a Stable Way to Distribute and Maintain Agent Skills
&lt;/h1&gt;

&lt;p&gt;Let’s be honest, the pace of AI development is exhausting. It feels like there’s a new paradigm every week! Yet, our actual workflow for delivering "skills" has barely evolved for months.&lt;/p&gt;

&lt;p&gt;While the &lt;a href="https://www.npmjs.com/package/skills?activeTab=readme" rel="noopener noreferrer"&gt;&lt;code&gt;skills&lt;/code&gt; package&lt;/a&gt; from Vercel gave us a nice boost, this methods starts to fall apart once you’re managing more than a handful of projects. We’re building sophisticated agents, yet we’re still treating their "brains" like a collection of loose files. We’ve collectively spent years perfecting how we share code via npm, but when it comes to sharing agent capabilities, we’ve mostly been winging it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: That "Wild West" Maintenance Cycle
&lt;/h2&gt;

&lt;p&gt;Think about how you currently handle updates for your agent’s capabilities. You’ve got &lt;code&gt;skills&lt;/code&gt; package which created folders, maybe some symlinks, or worse: you're copy-pasting skills from a GitHub repo every time a prompt gets tweaked.&lt;/p&gt;

&lt;p&gt;Current approaches often treat skills as side-effects. Just bits of text floating in a directory without any real structure or version. If you’re building anything serious, this "Wild West" approach to maintenance is a ticking time bomb. You want the predictability of a lockfile. You want the sanity of &lt;code&gt;npm update&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;skills-npm&lt;/code&gt;: Treating Skills as First-Class Citizens
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/skills-npm?activeTab=readme" rel="noopener noreferrer"&gt;&lt;code&gt;skills-npm&lt;/code&gt;&lt;/a&gt; cuts through the clutter by treating a skill exactly like a standard piece of production-grade code. By wrapping in an npm-compliant structure, it turns skills into proper, versioned dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why it Wins on Delivery and Versioning
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Registry-Based Distribution:&lt;/strong&gt; Why reinvent the wheel? By leveraging the npm registry, you get most of your problems handled for free. You can publish a skill with its own &lt;code&gt;package.json&lt;/code&gt;, complete with its own dependency graph and documentation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Versioning:&lt;/strong&gt; This is where it really shines. When you treat a skill as a package, you can finally pin versions. If a new update drops and breaks your agent’s delicate prompt logic, you don't panic. You just stay on the version that works.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Power of Graphs:&lt;/strong&gt; Since these are standard packages, you can start nesting them. Need a &lt;code&gt;code-review&lt;/code&gt; skill that relies on a specific &lt;code&gt;linting&lt;/code&gt; skill? It’s just &lt;code&gt;npm install&lt;/code&gt;. No more spaghetti code or manual path-hunting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual Management (like in &lt;code&gt;skills&lt;/code&gt; package):&lt;/strong&gt; Cloning skills from Git repos adds unnecessary steps to each project.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No sharing Overhead:&lt;/strong&gt; Teams don't have to commit cloned files or repeat the setup on every machine.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The &lt;code&gt;skills-npm&lt;/code&gt; Way
&lt;/h2&gt;

&lt;p&gt;Let's see how would you could work with &lt;code&gt;skills-npm&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuration (one-time setup)
&lt;/h3&gt;

&lt;p&gt;Install &lt;code&gt;skills-npm&lt;/code&gt; in your project and add it to your &lt;code&gt;prepare&lt;/code&gt; script in &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&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;package.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&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;"prepare"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"skills-npm"&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;If you need extra configuration, create a &lt;code&gt;skills-npm.config.ts&lt;/code&gt; file to target specific agents, etc.:&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;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;skills-npm&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="c1"&gt;// Target specific agents (defaults to all detected agents)&lt;/span&gt;
    &lt;span class="na"&gt;agents&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;cursor&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;windsurf&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;&lt;a href="https://www.npmjs.com/package/skills-npm?activeTab=readme#Configuration" rel="noopener noreferrer"&gt;More details in the readme&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Usage
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Add the desired skill as a devDependency in your package.json&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; @my-org/skill-database-optimizer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you’ve installed your skills, &lt;code&gt;skills-npm&lt;/code&gt; automatically symlinks them for your agent. Your agent simply points to the &lt;code&gt;node_modules&lt;/code&gt; folder, reads the versioned package, and runs. It’s clean, standardized, and just works.&lt;/p&gt;

&lt;h2&gt;
  
  
  More Information
&lt;/h2&gt;

&lt;p&gt;For detailed information about publishing and using the package, I highly recommend checking out the &lt;a href="https://www.npmjs.com/package/skills-npm?activeTab=readme" rel="noopener noreferrer"&gt;&lt;code&gt;skills-npm&lt;/code&gt; readme&lt;/a&gt;. It’s concise and gets right to the point!&lt;/p&gt;

&lt;p&gt;If you’re interested in even more background, be sure to take a look at the &lt;a href="https://github.com/antfu/skills-npm/blob/HEAD/PROPOSAL.md" rel="noopener noreferrer"&gt;PROPOSAL&lt;/a&gt; written by the one and only &lt;a href="https://antfu.me/" rel="noopener noreferrer"&gt;Anthony Fu&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This article has been originally published on &lt;a href="https://www.frsource.org/blog/post/skills-npm-a-stable-way-to-manage-agent-skills" rel="noopener noreferrer"&gt;FRSPACE blog&lt;/a&gt;.&lt;br&gt;
Take a look there to find more of my articles 🎉&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>aiagents</category>
      <category>npm</category>
      <category>skillsnpm</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Welcome generics in Vue</title>
      <dc:creator>Jakub Freisler FRS</dc:creator>
      <pubDate>Mon, 25 May 2026 13:57:47 +0000</pubDate>
      <link>https://dev.to/jakfrs/welcome-generics-in-vue-4bgh</link>
      <guid>https://dev.to/jakfrs/welcome-generics-in-vue-4bgh</guid>
      <description>&lt;h1&gt;
  
  
  Welcome generics in Vue
&lt;/h1&gt;

&lt;p&gt;Vue 3.3 has been just released 💚 And alongside other features and improvements, it includes a new compiler feature that I have been eagerly waiting on - &lt;a href="https://vuejs.org/api/sfc-script-setup.html#generics" rel="noopener noreferrer"&gt;&lt;strong&gt;generics support&lt;/strong&gt;&lt;/a&gt;! This feature brings a new level of flexibility and reusability to Vue.js components as it allows us to create &lt;strong&gt;dynamically typed component slots or emits&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generics in TypeScript
&lt;/h2&gt;

&lt;p&gt;TypeScript's (TS) static typing can help detect errors early in the development process. Developers can use TypeScript's various tools to express the APIs of their components, composables, services, and other code elements. Generics are a crucial TypeScript feature and allow defining types or functions that can work with various data types without compromising type safety.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generics in action - array function filterEven
&lt;/h3&gt;

&lt;p&gt;Let's consider a simple example of a &lt;code&gt;filterEven&lt;/code&gt; function that takes an array as input and returns a new array with odd-indexed items filtered 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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filterEven&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;array&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;index&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although this function works in simple cases, it has a significant flaw: the returned array loses its original type and is outputted with the type &lt;code&gt;unknown[]&lt;/code&gt; instead:&lt;/p&gt;

&lt;p&gt;&lt;a href="/post/welcome-generics-in-vue/example1-issue.webp" class="article-body-image-wrapper"&gt;&lt;img src="/post/welcome-generics-in-vue/example1-issue.webp" alt="The array returned by "&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.typescriptlang.org/play?#code/MYewdgzgLgBAZgSwDZQKYCcCiA3VYYC8MAFAIbrqkCeAXDAK5gDWYIA7mANoC6AlIQD4Y5SlQB0iFBmLEA+gBoYCMABNUAD34EhytepgBSGACZeAbgBQF0JFgBbKgEEK1OgG8YNtGCh1o6ZQBzGABfHkIYTg8vPF8YAHIIEDtUKAALIPjQxWjwbzj40jAqdMzQ7ksrG2h4ZDR0VBVnUQjJepw8Ygdm6nMLIA" rel="noopener noreferrer"&gt;See it in the playground&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;From TypeScript's perspective, this behavior is expected because the input is described as type &lt;code&gt;unknown[]&lt;/code&gt; in the &lt;code&gt;filterEven&lt;/code&gt; function declaration. Since the function's purpose is to take in an array and return a filtered version of it, TS assumes that the array type won't change in the process.&lt;/p&gt;

&lt;p&gt;However, this is not exactly how we wanted our function to behave. We don't want it to always return &lt;code&gt;unknown[]&lt;/code&gt; - it should return the same type of array that was passed in. To achieve this, we can use generics.&lt;/p&gt;

&lt;p&gt;Instead of explicitly specifying the input array type as &lt;code&gt;unknown[]&lt;/code&gt;, we can declare it to be of any type that extends (is built upon) &lt;code&gt;unknown[]&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filterEven&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;array&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;index&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach allows TypeScript to infer the type of the input array into the generic &lt;code&gt;Array&lt;/code&gt; and then apply it to the output array:&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%2Ffqjyrqid2owvv8enjqqv.webp" 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%2Ffqjyrqid2owvv8enjqqv.webp" alt="The array returned by " width="800" height="170"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.typescriptlang.org/play?#code/MYewdgzgLgBAZgSwDZQKYCcCiA3VYYC8MAPAILroCGAnjKgB5pgAmEMArmANZggDuYANoBdAHwAKShRoAuGOSrUAlIVEwpigHSIUGceID6AGhgIWDFQTVnmDGAFIYAJiUBuAFDvQkWAFtqCrIwAN4w3kxQctDoZgDmMAC+IoQwgqHheJEwAOQQIL6oUAAWcdmJJungEXLZlGDUxaWJwh6ent7Q8Mho6KjMgbREOj04eOL+A27uQA" rel="noopener noreferrer"&gt;See it in the playground&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Voila, now the array returned by &lt;code&gt;filterEven&lt;/code&gt; function has the correct type! 🎉&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Still don't understand how generics work in TypeScript? Don't worry! You might want to read further about them in &lt;a href="https://www.typescriptlang.org/docs/handbook/2/generics.html" rel="noopener noreferrer"&gt;the official handbook&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Generics syntax in Vue Single-File Components (SFCs)
&lt;/h2&gt;

&lt;p&gt;Now you understand how generics might be useful in TypeScript (or any other statically typed language), but what about Vue? Let's write a component example where generics might come in handy.&lt;/p&gt;

&lt;p&gt;Imagine a &lt;code&gt;Tabs&lt;/code&gt; component that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;will handle the tab navigation by itself,&lt;/li&gt;
&lt;li&gt;will support complete customization of the rendered content for each tab via slots.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;// Tabs.vue
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt; &lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;computed&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;vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;defineProps&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;tabs&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="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&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;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;activeIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;activeTab&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;activeIndex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt;
            &lt;span class="na"&gt;v-for=&lt;/span&gt;&lt;span class="s"&gt;"(&lt;/span&gt;{ heading, id }, i) in tabs"
            :key="id"
            type="button"
            @click="activeIndex = i"
        &amp;gt;
            &lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;heading&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"content"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;slot&lt;/span&gt; &lt;span class="na"&gt;:tab=&lt;/span&gt;&lt;span class="s"&gt;"activeTab"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This component can be consumed as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;// App.vue
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt; &lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Tabs&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;./Tabs.vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productTabs&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="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;bestsellers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bestsellers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;The best-selling articles in our store!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;products&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;Gray sweater&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="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;offers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Offers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Here are some best offers tailored just to your liking 🧵&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;products&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;Green T-shirt&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="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Tabs&lt;/span&gt; &lt;span class="na"&gt;:tabs=&lt;/span&gt;&lt;span class="s"&gt;"productTabs"&lt;/span&gt; &lt;span class="na"&gt;v-slot=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;{ tab }"&amp;gt;
        &lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Tabs&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://play.vuejs.org/#eNqNVEtu20AMvQqrTRzAloIsFSdIu2m7ahfZWVnIEhVPLM0MZiglhqCzdNkT9F49Qkl9HAkFghiwoeGQj4+PT26Dz9aGTY1BHGx95pQlKFP9dJsE5JMAPFJt7xINoCprHMFDuvdQOFNBEoSRnKQ6CW4kR76Z0Z7AOpPXGfXZt7CTC4AWVB5z3R49eSxLdNxiDQdMc6Wf5ObL8oaxCDXJzcMBQeo2cs3ZkDpSWYkelAZTO/BkHH6SqrG3j2HXgk4rlPqvLj2Bf8GU0PFc3SN06yUrUxT/E/pxDs64fEOH3B/Bm2pgBUMxUKpKppHDc81BMnASaqU6CuO/v37/eYcfooaHjT8oR3OCj6zsNhpWw4vgA2FlS56jX8uWxTiCw5I35unEghwQBeDgsODYgcj6OIqyXIfPPsdSNS7USJG2VfQiaoSZ9/fXkanp7cz10QBfpUr3T/zcbzMm/mXg2YY5u9n40hCHW9ZgD10SjEWsbx8KR/2g60a03jxDk2jsso1mswXrYLLXR83ZshCFrKqyNfEWusGpFwxxwTIu7GnFmDkWSuNPOW3bgZeMF4+m8OR4cTdvhpgCZzNMgW4newLo7laXi05pRqrB7zrHV+7H9FZXkrC85jn5cqK9Wl3C7d3AMRQ6uxlI2KRljY+C8Z4rctVMW9vXRKZnJJ9mUxjHAq7aaao1j8peA3Upr5L0S4IpOz7iiZNV/haik0UOjajn8H1WquzIF8uB1ZQxt8PYmK0w/GWIAwa8ibNHRjEasjL1YrZR75mrtuK33oznnqzi2bcCOYKMFhsUWTiMj/0rw49ne7ILEqpS96T0hoyN4frKvrLY7FpGHLOD7h9KGb8T" rel="noopener noreferrer"&gt;Click here to see a working demo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this example, we pass an array &lt;code&gt;productTabs&lt;/code&gt; into the &lt;code&gt;Tabs&lt;/code&gt; component as a prop. The component then displays tab navigation buttons and allows the consumer to provide a tab template through a slot. &lt;a href="https://vuejs.org/guide/components/slots.html#scoped-slots" rel="noopener noreferrer"&gt;This slot is scoped&lt;/a&gt; as the data &lt;code&gt;tab&lt;/code&gt; (containing information about the currently active tab) is passed into it.&lt;/p&gt;

&lt;p&gt;It seems like everything has worked on the first try, but has it really? Let's take a closer look at the type of the &lt;code&gt;tab&lt;/code&gt; property that's passed into a slot template:&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%2F99sw9f3ljov1xkol2ckw.webp" 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%2F99sw9f3ljov1xkol2ckw.webp" alt="Tab property available in slot has a type of " width="504" height="216"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Did you notice anything missing? The issue here is similar to the one previously encountered in this article. The type of &lt;code&gt;tab&lt;/code&gt; property has been narrowed down to the type of the &lt;code&gt;tabs&lt;/code&gt; prop. However, in our case, the input type is broader than that as the &lt;code&gt;products&lt;/code&gt; field is being stripped down.&lt;/p&gt;

&lt;p&gt;Okay, so how to fix it? The solution, of course, involves the use of generics! Instead of typing &lt;code&gt;tabs&lt;/code&gt; prop strictly like we did in previous example we should allow TypeScript to accept and infer specific type that fulfills the type &lt;code&gt;{ id: string; heading: string; content: string; }&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;// Tabs.vue
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;
    &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;
    &lt;span class="na"&gt;setup&lt;/span&gt;
    &lt;span class="na"&gt;generic=&lt;/span&gt;&lt;span class="s"&gt;"Tab extends { id: string; heading: string; content: string }"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;computed&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;vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;defineProps&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Tab&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;activeIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;activeTab&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;activeIndex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt;
            &lt;span class="na"&gt;v-for=&lt;/span&gt;&lt;span class="s"&gt;"(&lt;/span&gt;{ heading, id }, i) in tabs"
            :key="id"
            type="button"
            @click="activeIndex = i"
        &amp;gt;
            &lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;heading&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"content"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;slot&lt;/span&gt; &lt;span class="na"&gt;:tab=&lt;/span&gt;&lt;span class="s"&gt;"activeTab"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's examine the implementation. First, we created a generic by adding &lt;code&gt;generic="Tab extends { id: string; heading: string; content: string; }"&lt;/code&gt; to the component &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; section. Second, we used the &lt;code&gt;Tab&lt;/code&gt; generic to declare the type of &lt;code&gt;tabs&lt;/code&gt; prop as &lt;code&gt;Tab[]&lt;/code&gt;. By making these two changes, we informed TypeScript that the &lt;code&gt;tabs&lt;/code&gt; prop can be filled with any type, as long as it's an array, and each item is built on top of type &lt;code&gt;{ id: string; heading: string; content: string; }&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And what about the consumer component? Is the slot type inferred correctly now? Let's take a look:&lt;/p&gt;

&lt;p&gt;&lt;a href="/post/welcome-generics-in-vue/vue-example2-output.webp" class="article-body-image-wrapper"&gt;&lt;img src="/post/welcome-generics-in-vue/vue-example2-output.webp" alt="Tab property available in slot has a type of "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Excellent! The tab data is passed directly into the scoped slot with the correct, initial type! 🎉 This enhances the flexibility of our component, as the slot content can now easily use custom tab properties without sacrificing the benefits of static type checking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing words
&lt;/h2&gt;

&lt;p&gt;Generics support was a huge addition to Vue.js and its arrival is a significant milestone. The introduction of this feature simplified the process of representing dynamic types that are essential for achieving the required flexibility in specific scenarios. Also, the &lt;code&gt;generic=""&lt;/code&gt; attribute is a seamless and natural addition to Vue SFC syntax, which enhances the language's expressiveness. I hope to see more and more libraries (particularly UI ones) taking advantage of generics and improving their type safety!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This article has been originally published on &lt;a href="https://www.frsource.org/blog/post/welcome-generics-in-vue" rel="noopener noreferrer"&gt;FRSPACE blog&lt;/a&gt;.&lt;br&gt;
Take a look there to find more of my articles 🎉&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>tutorial</category>
      <category>vue</category>
      <category>generics</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
