Can Vue props accept a generic type argument?

Currently, I have a basic component that is receiving the following props:

  props: {
    options: {
      type: Array as PropType<unknown[]>,
      default: () => []
    },
    labelKey: {
      type: String,
      default: "label"
    }
  }

Within the template, I am using the following syntax:

<SomeComponent
  v-for="(item, index) in options"
  v-text="item[labelKey]"
  ...
/>

Ideally, instead of using unknown, I would like to pass the specific type of an option item (e.g: T) and inform TypeScript that labelKey should be of type keyof T. Is this achievable in Vue?


At present, my only recourse is to acknowledge the error:

<SomeComponent
  v-for="(item, index) in options"
  /* @ts-expect-error: for TS item is unknown */
  v-text="item[labelKey]"
  ...
/>

Answer №1

After delving into this topic further, here are my discoveries 1, 2:

Within Vue2, it was possible to craft a universal function that would yield a component definition:

const generateUniversalSearch<T>() {
  return defineComponent({
    props: {
      options: { 
        type: Array as PropType<T[]>,
        default: () => []
      },
      labelKey: {
        type: String as PropType<keyof T>,
        default: 'label'
      }
    },
    // ...
  })
}

...and then utilize it in the parent component like so:

import { generateUniversalSearch } './path/to/it'

export default defineComponent({
  components: {
    MyCustomSearch: createGenericSearch<MyCustomItemType>()
  }
})

However, while effective, this approach has a significant downside: it deviates from using Single File Components for <GenericSearch />. Consequently, you must pass the template as a prop of defineComponent and relocate all <style /> content to the parent component.

The main drawback of abandoning the SFC pattern is that Vue must now compile the template at runtime, necessitating the use of the compiler-inclusive Vue version – resulting in an increase of over 200k. While I advocate for Typescript, enlarging your app size significantly outweighs TS's lack of awareness regarding labelKey being keyof OptionType in my generalized typed search component.

Note that employing generic types through the Class API is still feasible, but due to its deprecated status, I won't delve into it here.


In Vue3, the incorporation of generic types has been simplified with

<script setup lang="ts" generic="T">
defineProps<{
  options: T[]
  labelKey: keyof T
}>()
</scipt>

, introduced in version 3.3.
For more information, refer to the official documentation and this answer.


1 - Although labeled under vue3.js, the project currently operates on

<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="d0a6a5b590e2fee7">[email protected]</a>
, gearing up for migration to vue@3. The ongoing transition involves shifting components from Options API to Composition and implementing minor typescript enhancements. At the time of posting this query, I was unaware of Vue's support for generics and the distinct syntax between versions ^2 and ^3.
2 - During my exploration on creating a generically-typed component, I discovered this informative article.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Adding dynamic parameters to each route in Nuxt's router as a suffix will allow for more

I am currently working with Nuxt2 and I am trying to set up dynamic routing with parameters for each route. These parameters can vary depending on the route. For example: /users/london /posts/london /products/berlin /products/apple-watch/berlin /products/s ...

Does adding .catch resolve a promise?

Being new to typescript / javascript, I have limited knowledge about promises. My current scenario involves creating three distinct promises within a cloud-function and subsequently returning them using Promise.all([promise1, promise2, promise3]). Each of ...

What is the reason behind Node.js tutorials instructing to install Node.js on the host when dockerizing Node.js?

Is it necessary to have Node.js and Vue.js installed on my host before getting a Node/Vue image for Docker? Many Vue.js tutorials advise installing Node and Vue on the host first and then obtaining the Docker image, but is this redundant? Examples: I am ...

Attempting to assign a File object as a property to a component but receiving an empty object in return. I'm curious about the security implications of this as well

I created a modal for previewing avatars with a generic design: <avatar-update :img-file="avatarFile" :show="avatarModalShow" :img-url="url" @close="avatarModalShow = !avatarModalShow" :change-avatar="updateCrop" @destroyUrl="imgUrl = null"> </av ...

Where can I locate htmlWebpackPlugin.options.title in a Vue CLI 3 project or how can I configure it?

After creating my webpage using vue cli 3, I decided to add a title. Upon examining the public/index.html file, I discovered the code snippet <title><%= htmlWebpackPlugin.options.title %></title>. Can you guide me on how to change and cu ...

Display nested array objects of varying levels within VUE

How do I display nested elements on the UI when dynamically generated based on a parent element's selected option (e.g. List)? For example, in the code below, when creating a field and selecting the List option, another nested should appear, and so o ...

What steps are needed to configure ESLint to exclusively analyze .ts files?

I need ESLint to exclusively focus on processing .ts files and not .js files. In order to achieve that, I made a .eslintignore file and included the following lines: *.js **/*.js Nevertheless, it appears that ESLint is disregarding this file. Is there so ...

Utilizing Vuex data dynamically within a v-for loop

Can Vuex data be accessed dynamically in a v-for loop within a template? I attempted to access it using the $data variable in the template but without success: <template> <div v-for="(item, index) in items"> {{item.id}}: {{$data[i ...

Delete an entry in a singular mapping in a one-to-one connection [TypeORM]

Is there a way to remove an index from a one-to-one relationship in TypeORM? @OneToOne(() => Customer, { cascade: true }) @JoinColumn({ name: 'customer', referencedColumnName: 'uid' }) customer: Customer I searched the d ...

Changing the global type in TypeScript

Currently, I am incorporating two third-party TypeScript libraries into my project. Interestingly, both of these libraries expose a global variable with the same name through the Window interface. However, they offer different methods for interacting with ...

What is the best way to broaden the capabilities of function objects through the use of

Below is a code snippet that raises the question of how one should define certain types. In this scenario, it is required that Bar extends Foo and the return type of FooBar should be 'a'. interface Foo { (...args: any):any b: string } i ...

Angular 2: Emptying input field value on click event

I am experiencing an issue with resetting my input values. I have a search bar with filter functions. When I enter a value, it displays a list below and I want to add a function to these links. When I click on one of them, it takes me to another component ...

What is the method for utilizing Tuple elements as keys in a Mapped Type or template literal within typescript?

Is there a specific way to correctly type the following function in TypeScript? Assuming we have a function createMap() that requires: a prefix (e.g. foo) and a tuple of suffixes (e.g. ['a', 'b', 'c']) If we call createMap(& ...

convert a JSON object into an array field

I am looking to convert a list of objects with the following structure: { "idActivite": 1, "nomActivite": "Accueil des participants autour d’un café viennoiseries", "descriptionActivite": "", "lieuActivite": "", "typeActivite": "", ...

The elixir-typescript compilation process encountered an error and was unable to complete

I am currently working on integrating Angular2 with Laravel 5.2 and facing an issue with configuring gulp to compile typescript files. Below is a snippet from my package.json file: { "private": true, "scripts": { "prod": "gulp --production", ...

Utilizing React to pass parent state to a child component becomes more complex when the parent state is derived from external classes and is subsequently modified. In this scenario,

I'm struggling to find the right way to articulate my issue in the title because it's quite specific to my current situation. Basically, I have two external classes structured like this: class Config { public level: number = 1; //this is a s ...

Linting: Add "`··` eslint(prettier/prettier)" to a project using React with TypeScript and Styled Components

I'm facing a challenge with conflicting rules between Eslint and Prettier in my React project that uses TypeScript and Styled Components. When working in VSCode, I keep getting this error message: "Insert ·· eslint(prettier/prettier)" T ...

Choosing between Vue.use and Vue.component for the main Vue component

When using Vue.use, the functionality becomes global, similar to when calling Vue.component in the root Vue component like app.vue. I recently came across a sample app that utilized both methods, with multiple calls of Vue.component and Vue.use within the ...

"Converting to Typescript resulted in the absence of a default export in the module

I converted the JavaScript code to TypeScript and encountered an issue: The module has no default export I tried importing using curly braces and exporting with module.exports, but neither solution worked. contactController.ts const contacts: String[ ...

Unable to find module 'child_process'

Here is the content of my main.ts file: import { enableProdMode } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { environment } from './environments/environment'; if ...