Exploring the world of typed props in Vue.js 3 using TypeScript

Currently, I am attempting to add type hints to my props within a Vue 3 component using the composition API.

This is my approach:

<script lang="ts">
import FlashInterface from '@/interfaces/FlashInterface';
import { ref } from 'vue';
import { useStore } from 'vuex';

export default {
    props: {
        message: {
            type: FlashInterface,
            required: true
        }
    },
    setup(props): Record<string, unknown> {
        // Stuff
    }
};

The structure of my FlashInterface is as follows:

export default interface FlashInterface {
    level: string,
    message: string,
    id?: string
}

Despite working smoothly in other scenarios, I encountered an issue where I received this error:

ERROR in src/components/Flash.vue:20:10
TS2693: 'FlashInterface' only refers to a type, but is being used as a value here.
    18 |    props: {
    19 |        message: {
  > 20 |            type: FlashInterface,
       |                  ^^^^^^^^^^^^^^
    21 |            required: true
    22 |        }
    23 |    },

I'm puzzled as to why TypeScript perceives this as a value rather than a type. Any ideas on what I might be overlooking?

Answer №1

When utilizing it, make sure to use PropType imported from vue like

Object as PropType<FlashInterface>
:

import FlashInterface from '@/interfaces/FlashInterface';
import { ref,PropType, defineComponent } from 'vue';
import { useStore } from 'vuex';

export default defineComponent({
    props: {
        message: {
            type: Object as PropType<FlashInterface>,
            required: true
        }
    },
    setup(props) {
            // Operaions go here
        }
});

Please note: ensure your component is created using defineComponent for proper types inference.

script setup

<Vue3.3 :

You can define the props by using the defineProps function in two ways, but remember to declare the types/interfaces within the script setup as detailed here :

<script setup lang="ts">

import { ref,PropType, defineComponent , defineProps} from 'vue';
interface FlashInterface {
    level: string,
    message: string,
    id?: string
}
interface IProps{
   message : FlashInterface
}
const props = defineProps<IProps>()

or

<script setup lang="ts">
...
interface FlashInterface {
    level: string,
    message: string,
    id?: string
}
const props = defineProps({
        message: {
            type: Object as PropType<FlashInterface>,
            required: true
        }
    })

Update Vue 3.3+

Add this line in your package.json under dependencies

"vue": "^3.3.0"

Now, the defineProps<T>() macro can receive an imported interface making it possible to do this:

import FlashInterface from '@/interfaces/FlashInterface';
import { defineProps } from 'vue';

export default defineProps<FlashInterface>();

If you need to define default values, you can do so like this:

const { 
  message = "No message :(",
  level,
  id,
} = defineProps<FlashInterface>();

This feature was officially included in Vue 3.3.0 through PR 8083

Answer №2

Two line script setup

<script type="text/javascript">
import FlashInterface from '@/interfaces/FlashInterface';
defineProps<{flash: FlashInterface;}>();
</script>

Answer №3

The compiler is raising an issue regarding the absence of a reference for a (custom) constructor during type checking (the link may point to legacy documentation but behaves similarly with the latest Vue version).

When working with Typescript, interfaces can be viewed as a set of rules that an entity must adhere to; they do not act as constructors. Therefore, it's necessary to provide an implementation of these interfaces.

If you intend to maintain the interface while using Typescript, consider utilizing the class equivalent:

// Choose any name for the constructor,
// although prefacing interfaces with 'I' helps differentiate them from constructors

class Flash implements FlashInterface {
  level: string;
  message: string;
  id?: string;

  constructor() {
    // Initialize values for non-nullable properties
    this.level = '';
    this.message = '';
  }
}

export default {
  name: 'Home',

  props: {
    message: Flash
  }
}

A segment from the documentation states:

Furthermore, type can also refer to a custom constructor function, with the assertion performed using an instanceof check. For example, assuming the following constructor function is in place:

props: {
  message: {
    type: function Person(firstName, lastName) {
      this.firstName = firstName;
      this.lastName = lastName;
    }
  }
}

Alternatively, as mentioned in another post, you could explore the use of PropType. Both options are valid—it ultimately comes down to personal preference.

Answer №4

To easily define your props, utilize the setup script.

<script setup lang="ts">
import { ref } from 'vue';
import { useStore } from 'vuex';
interface FlashInterface {
    level: string,
    message: string,
    id?: string
}
interface Props{
   message:FlashInterface
}
const props = defineProps<Props>()

For further details and guidance, check out the Vue documentation

Answer №5

Utilizing PropType from 'vue' is recommended when dealing with manually created interfaces and setting default values!

import FlashInterface from '@/interfaces/FlashInterface';
import { ref, PropType, defineComponent } from 'vue';
import { useStore } from 'vuex';

export default defineComponent({
   props: {
       message: {
           type: Object as PropType<FlashInterface>,
           default: {}
       }
   },
// additional code
});

Answer №6

I have come up with a solution to address this issue by introducing a generic props type along with a function to ensure the correct implementation of the props.

type TypedProps<T> = {
  [key in keyof T]: {
     type: PropType<T[key]>
     required?: boolean
  } | PropType<T[key]>
}

function validateProps<T extends object>(props: TypedProps<T>): TypedProps<T> {
   return props
}

I plan to incorporate it into my defineComponents method like this

interface MyProps {
  prop1: string
  prop2: CustomInterface
}
export default defineComponent({
  props: validateProps<MyProps>({
    prop1: String,
    prop2: Object as PropType<CustomInterface>
  })
})

However, there seems to be an issue with the use of the required attribute. I am seeking clarification on why this is happening or suggestions for improvement.

Answer №7

Using Object as PropType seems a bit odd and hard to remember. A more elegant approach for typing properties is by specifying a generic type argument directly in the defineProps function, like this example using

<script setup lang="ts">
...

const props = defineProps<{ message: FlashInterface }>()

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

Transferring Files from Bower to Library Directory in ASP.Net Core Web Application

Exploring ASP.Net Core + NPM for the first time, I have been trying out different online tutorials. However, most of them don't seem to work completely as expected, including the current one that I am working on. I'm facing an issue where Bower ...

Guide to Implementing Vuetify within a Laravel 9 Vue SPA

My original intention was to integrate vuetify into my app. However, after installing the necessary packages and configuring resources\js\app.js, I encountered a runtime error: Uncaught TypeError: vue__WEBPACK_IMPORTED_MODULE_0___default.a is und ...

Retrieve a targeted data value from a JSON object based on an array value

Looking at the JSON array and another array provided below. JSON OBJECT { id: 1, name: 'abc', email: '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="dfbebdbc9fb8b2beb6b3f1bcb0b2">[emai ...

The cart total variable in Vuejs is coming back as NaN

I'm currently in the process of creating a cart system using vuejs and I've encountered an issue where my total variable is displaying NaN instead of calculating the total price of all products. Below is the function responsible for calculating ...

How can I deploy a react-express application to Azure cloud platform?

Struggling to deploy my react-express application on Azure. The code is divided into client and server directories. Attempted deployment using Azure Static Web application but encountered failure. https://i.stack.imgur.com/ailA0.png https://i.stack.imgur.c ...

What is the best way to single out a specific item from a list to display in a component upon clicking in Vue.js?

To view the data of a specific item in the child component "comandasInd", I just need to click on the icon. <template> <v-data-table :items="comandas"> <template v-slot:items="props"> <td>{{ props.item.nombre }}</td& ...

Using Vuex as a global event bus ensures that all subscribers will always receive notifications for

For a while now, I have relied on a global event bus in Vue - creating it as const bus = new Vue(). It works well, but managing subscriptions can get tedious at times. Imagine subscribing to an event in a component: mounted() { bus.$on('some.event ...

Guide to TypeScript, RxJS, web development, and NodeJS: A comprehensive manual

Looking for recommendations on advanced web development books that focus on modern techniques. Digital resources are great, but there's something special about reading from a physical book. I don't need basic intros or overviews - consider me an ...

Retrieve information from a URL to transmit to a different page in NextJS using Typescript and AppRouter

I'm struggling with transitioning from the Home page to the Detail page. I've successfully passed data to the URL from the Home screen, but I'm having trouble accessing it in the Detail screen. I'm working with NextJS ver 13, using Type ...

Is it feasible to utilize an array of properties with the "item-text" attribute in Vuetify?

<v-autocomplete v-model="friends" :disabled="isUpdating" :items="people" label="Select" item-text="name" <!-- Array ['name', 'id', 'value'] can be used her ...

What is the procedure for accessing a namespace when declaring it globally?

Website Project Background Currently, I am working on a simple website where users can update their pictures. To achieve this functionality, I am utilizing the Multer library along with Express in Typescript. Encountered Issue I am facing a challenge re ...

Can you tell me the significance of the constant variable isType = <T>(type: string) => (obj: unknown): obj is T => toString.call(obj) === `[object ${type}]`?

const isType = <T>(type: string) => (obj: unknown): obj is T => toString.call(obj) === `[object ${type}]` This function is all about determining types, but the arrows used in the syntax are confusing to me. I'm not sure what each arrow s ...

Using Angular 4 to monitor changes in two-way binding properties

Recently, I developed a unique toggle-sorting component that examines if the current sorting parameters align with its sorting slug and manages clicks to reflect any changes. // toggle-sorting.component.ts @Input() sortingSlug: string; @Input() currSorti ...

Utilizing React TypeScript: Leveraging useRef for Linking purposes

Implementing useRef to Handle Link Clicks import {Link} from 'react-router-dom'; const myLinkRef = useRef<HTMLAnchorElement>(null); ... myLinkRef.current.click() ... <Link to={{pathname: '/terms'}} id='myLink' ref= ...

Setting default property values in a React component using Typescript

   Is there a way to define default property values in React components when using Typescript? I came across a post on SE suggesting the use of a static class variable called defaultProps with key-value pairs for properties. However, this method didn&a ...

When modifying an array, the v-for directive causes all styles to be re-rendered

I am facing an issue with my v-for list that displays items from an array. The main problem is that when I make changes to the array, all the components get re-rendered entirely instead of just appending or prepending new elements. This results in a signif ...

What is GraphQl indicating when it informs me that I neglected to send arguments for the mutation

My GraphQL schema consists of various mutations and queries. I believe that validation of mutations should only occur when I send mutation { ... } to the graphql server. However, currently, the server is informing me that I have not sent arguments for all ...

In JavaScript, loop through an array of arrays and combine them using the concat

If I have an array like [["a", "b"], ["c", "d"]], is there a way to iterate, reduce, map, or join this array in order to get the desired output of ["ac", "ad", "bc", "bd"]? What if the array is structured as [["a", "b"], ["c", "d"], ["e", "f"]]; how can we ...

Manipulate values within an array when a checkbox is selected or deselected

I am developing an Angular application where I have created a checkbox that captures the change event and adds the checked value to an array. The issue I am facing is that even if the checkbox is unchecked, the object is still being added to the array. D ...

Modifying routes within the beforeEach function causes issues when the callback incorrectly passes in erroneous routes to and from

I'm currently in the process of developing a mobile frontend using Vue. My goal is to have route transitions dynamically change to slide either left or right based on the current tab index. To accomplish this, I've set up a transition component ...