Retrieving Data from Vuetify Component within vue 3

Currently, I am in the process of creating my own wrapper for Vuetify components to eliminate the need to repeatedly define the same props in each component. For example, I aim to develop a custom TextField with defaultProps while still being able to accept all props typically used with VTextField.

The approach I took in defining the script setup section was as follows:

<script lang="ts" setup>
import type { ExtractPropTypes } from 'vue';
import { computed } from 'vue';
import { VTextField } from 'vuetify/components';

type VTextProps = InstanceType<typeof VTextField>['$props'];
// type VTextProps = ExtractPropTypes<typeof VTextField>;

const props = defineProps<VTextProps>();
const emit = defineEmits(['update:modelValue']);

const value = computed({
  get() {
    return props.modelValue;
  },
  set(value) {
    emit('update:modelValue', value);
  },
});
</script>

I experimented with two different ways to extract the props of VTextField:

type VTextProps = InstanceType<typeof VTextField>['$props'];
type VTextProps = ExtractPropTypes<typeof VTextField>;

Unfortunately, I encountered an issue where neither method seemed to work. While my IDE showed functioning behavior and autocomplete features, I received the following error from vite:

[vite] Internal server error: [@vue/compiler-sfc] Unresolvable type reference or unsupported built-in utility type

It appears that the defineProps function of Vue may have trouble parsing this utility type correctly. Do you have any recommendations for a workaround so that I don't have to create my own types? It seems that Vuetify does not expose its types...

I would greatly appreciate any suggestions or assistance!

Answer №1

Dealing with a similar issue, I encountered a problem while using Vue 3.3.1 and Vuetify 3.4.7!

It seems that Vue does not handle very complex types well, such as the $props of Vuetify components.

For instance, when defining $slots for VTextField, you can make it work by following this approach:

<script setup lang="ts">
import { VTextField } from 'vuetify/components/VTextField'

type TextFieldSlots = InstanceType<typeof VTextField>['$slots']

defineSlots<TextFieldSlots>()

</script>

We can also experiment with $props:

...

type TextFieldProps = InstanceType<typeof VTextField>['$props']

interface Props {
  icon?: string
  externalLabel?: string
}

withDefaults(defineProps<Props & TextFieldProps>(), {
  // externalLabel: 'Default External Label'
})
</script>

I received an error message stating:

[@vue/compiler-sfc] Unresolvable type reference or unsupported built-in utility type...

To "fix" this issue, I created an interface, extended the type, and used /* @vue-ignore */

It may seem like a strange way to fix it, but if none of the other methods work, you can give this one a try. Here's the full example code:

TEMPLATE

<template>
  <div>
    <div v-if="externalLabel">{{ externalLabel }}</div>
    <v-text-field v-bind="{ ...$attrs, ...$slots }" variant="outlined">
      <!-- Default Slot for Prepend-Inner -> feature: small icon and right margin -->
      <template v-if="icon" v-slot:prepend-inner>
        <v-icon size="small" class="mr-1">{{ icon }}</v-icon>
      </template>

      <!-- Slots  -->        
      <template v-for="(slotScopedName, _, index) of (Object.keys($slots) as [])" #[slotScopedName]="slotData" :key="index">
        <slot :name="slotScopedName" v-bind="(slotData as {})"></slot>
      </template>

    </v-text-field>
  </div>
</template>

SCRIPT

<script setup lang="ts">
import { VTextField } from 'vuetify/components/VTextField'

type TextFieldProps = InstanceType<typeof VTextField>['$props']
type TextFieldSlots = InstanceType<typeof VTextField>['$slots']

interface ITextFieldProps extends /* @vue-ignore */ TextFieldProps {}
interface Props {
  icon?: string
  externalLabel?: string
}

defineOptions({ inheritAttrs: false })
defineSlots<TextFieldSlots>()
withDefaults(defineProps<Props & ITextFieldProps>(), {
  // externalLabel: 'Default External Label'
})
</script>

IMG - Autocomplete working for $props
IMG - Autocomplete working for $slots

Answer №2

Vue (version 3.4) currently does not support using an extracted type to generate props. While the IDE may appear to be functioning correctly due to full type analysis, the component will not compile.

The documentation on composition API with TypeScript mentions limitations in syntax, stating:

Due to the AST-based conversion of types to runtime, complex types like conditional types are not supported. Conditional types can be used for a single prop's type, but not for the entire props object.


On the other hand, Vuetify 3 components construct their props using a factory function (refer to code). You can import this function and utilize it to create a props object for defineProps(). Interestingly, the factory function lacks typing, so utilizing the extracted type is still necessary for autocomplete and type hints in your IDE:

Import { ComponentObjectPropsOptions, ExtractPropTypes } from 'vue';
import { makeVTextFieldProps } from 'vuetify/lib/components/VTextField/VTextField.mjs'
import type { VTextField } from 'vuetify/lib/components/index.mjs'

type VTextFieldProps = InstanceType<typeof VTextField>['$props']
type MyTextFieldProps = ComponentObjectPropsOptions<VTextFieldProps>;
const props = defineProps(makeVTextFieldProps() as MyTextFieldProps)

If you wish to extend the props object, you can directly use the props factory function from Vuetify. However, since local variables cannot be used in defineProps() and TS types must be provided manually, it is recommended to wrap it within a function that includes types. For example:

// Place this in its own .ts file or in a regular script block next to the script setup block

import type { ComponentObjectPropsOptions, ExtractPropTypes } from 'vue';
import { propsFactory } from 'vuetify/lib/util/propsFactory.mjs'

function buildExtendingProps<B, E>(baseProps: B, extendedProps: E, source: any)
{
  const combinedProps = {...baseProps, ...extendedProps}
  const builder = propsFactory(combinedProps, source)
  return builder() as ComponentObjectPropsOptions<B & ExtractPropTypes<E>>
}

You can then implement it as follows:

Import { makeVTextFieldProps } from 'vuetify/lib/components/VTextField/VTextField.mjs'
import type { VTextField } from 'vuetify/lib/components/index.mjs'

type VTextFieldProps = InstanceType<typeof VTextField>['$props']

const props = defineProps(buildExtendingProps(
  makeVTextFieldProps() as VTextFieldProps,
  {
    foo: {type: String, default: 'foo'}, 
    bar: {type: Number}
  },
  'MyTextField'
))

This approach may have its challenges, but it provides a solution nonetheless.

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

``Incorporating Vuetify components into VueJs components dynamically: A step-by-step guide

I have some data that includes HTML elements: data(){ text: 'Go to <v-btn> to="profile">Profile</v-btn>'} Now I want to render this data with the embedded HTML in my component. I attempted to accomplish this using th ...

Is there a way to create a self-contained installation package for my Vue application?

Is it possible for my application to be downloaded and installed without requiring internet access after the download is complete? I am looking to create a standalone installer for this purpose. Are there any suggestions on how to go about implementing t ...

In order to successfully run this generator XXX, you need to have a minimum of version 4.0.0-rc.0 of yeoman-environment. However, the current version available

When attempting to run a generator using the latest version of yeoman-generator (7.1.0), yo discord An error message pops up saying: This generator (discord:app) requires yeoman-environment version 4.0.0-rc.0 or higher, but the current version is 3.19.3. ...

Looping through various object attributes and accessing an array of objects within it

Within my application, I am currently receiving an object structured as follows: { "data1":[ {},{}{} ], "data2":[ {},{},{}....], "data3":[ {},{},{}.....] } I am looking for guidance on how to utilize v-f ...

Prevent event bubbling in Vue.js when using the data-toggle attribute from Bootstrap

How can I stop event propagation from the anchor href while using v-bind:data-toggle in Vue.js? <template> <ul class="nav navbar-nav"> <li v-for="(item, i) in items" :class="classes(item)"> ...

Seeking assistance with TypeScript promises

Just starting out with typescript and nodejs, but I've got to tackle some issues in the typescript code. I'm looking to execute an ECS one-off task using Pulumi. I have the documentation on how to run the task from the taskDefinition, which can ...

Why am I encountering issues accessing a child property in TypeScript?

Here is some TypeScript code that I am working with: export interface SelectQuery_thing { __typename: "ThingQueryPayload"; content: (SelectQuery_thing_content | null)[]; pageInfo: SelectQuery_thing_pageInfo; } export interface SelectQuery_thing_con ...

Efficiently organizing dates in the Vuetify date range picker

I am working with a vuetify date range picker component and have the following code: <v-menu ref="effectiveDateMenu" v-model="effectiveDateMenu" :close-on-content-cl ...

Learn the process of submitting tag values using JohMun/vue-tags-input feature

I am a beginner in Vue.js and I am looking to implement an input field with multiple tags (user skills) using a Vue.js component. Although I have managed to make it work, I am struggling to figure out how to submit the tags in a form. Below is my code: T ...

Combining types with additional features

Is it possible to configure the TypeScript compiler to generate an error when a function is called with an argument that can belong to both cases in a union type? For example: interface Name { name: string } interface Email { email: string } type ...

Storing a JWT refreshToken cookie in the response: Best practices

I'm currently in the process of using GraphQL to authenticate a user with JWT. After logging in, I receive a JSON response containing a token and an httponly cookie that stores the refresh token (Saleor-core is used on the server-side). After referri ...

"What could be causing my React Native app to build without any issues even though the TypeScript compiler is throwing

Recently, I decided to explore TypeScript in combination with Expo. I took the time to set up linter and formatter integrations like typescript-eslint to help me catch errors while coding. Periodically, I run npx tsc to ensure my code compiles correctly an ...

Adding numerous pieces of data into the Shopware 6 database through the use of the Administration for handling Many

I recently developed a plugin within the administration of Shopware 6 and I am encountering an issue while trying to insert multiple products associated with vehicles into the database. The specific problem arises when attempting to add '92961afbc50e4 ...

Populate two b-form-select components with the same object by using the v-model directive in VueJS with BootstrapVue

I am trying to populate multiple select elements with the values from my "objectSelected" object, but for some reason it is not working within the bootstrap-vue form. There are no error messages displayed, it just simply does not load the values. //My sel ...

Tips for assigning a JSON object as the resolve value and enabling autosuggestion when utilizing the promise function

Is there a way to make my promise function auto-suggest the resolved value if it's a JSON object, similar to how the axios NPM module does? Here is an example of how axios accomplishes this: axios.get("url.com") .then((res) => { Here, axios will ...

How can I pass additional props that are not specified in the interface while working with a React TS component?

I am working with a TS React component called MyButton.tsx: import React from 'react' interface MyButtonProps { children: JSX.Element | JSX.Element[], className?: string, variant?: 'big-button' | 'medium-button' | &apos ...

What is the best way to wait for the state to be set before mapping the array

I have some data stored in an array (shown in the screenshot below) that I am trying to map, but I am facing issues accessing it as it is loaded asynchronously. How can I await the data? Is there a way to achieve this within the render function? render() ...

Tips for conducting a worldwide search in Angular 2?

I'm currently navigating my way through angular2 development and I am aiming to conduct a comprehensive search within an array of JSON objects. To illustrate, consider this sample array: invoiceList = [ { invoiceNumber: 1234, invo ...

Utilizing the props value for emission within the emits array: A guide

While attempting to list a custom event in the component's emits option, I encountered a console error. The code looked like this: PARENT <Btn event-name="toggleSideMenu" @toggle-side-menu="toggleHandler"> togg ...

Fixing 500 (Internal Server Error) in Vue.js and Laravel: The Ultimate Guide

Working on a university project using Vue.js and Laravel, I need assistance with sending information to the API. I'm trying to use axios for the POST request, but it's giving me an error: 500 (Internal Server Error). I can't seem to identif ...