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

Exploring a JSON Object in TypeScript: A Step-by-Step Guide

I am currently utilizing Angular 7 and have a query that returns JSON data with a specific format: [ { "text": "test 1", "value": "1", "nbr": "1", "children": [ { "text": "test 1_1", ...

The custom class-validator decorator in NestJS fails to retrieve the value from the parameter

In my Nestjs project, I have created a Custom ValidatorConstraint using class-validator. The purpose is to create my own decorator and apply it later on DTO classes for validations. Let's consider this route: foo/:client After a request is made, I w ...

The export from chart.js does not include a member named 'ChartDataSets'. Perhaps you were referring to 'ChartDataset'? ts(2724)

Encountered an error message when trying to import ChartDataSets in line-chart.component.ts 'chart.js' does not have a member named 'ChartDataSets'. Perhaps you meant 'ChartDataset'? Uncertain about the source of the issue. C ...

The issue of invalid prop in Vue Utils

Having an issue with Vue Util Error: console.error [Vue warn]: Invalid prop: type check failed for prop "icon". Expected Object, Array, String, got Undefined In my parent component 'myComp', there is a global component Font Awesome n ...

Using regular expressions in Javascript to extract decimal numbers from a string for mathematical operations

I'm currently working on a Vue method where I extract information from a WordPress database. The data retrieved sometimes contains unnecessary text that I want to filter out. Using the prodInfo variable, the input data looks something like this: 2,5k ...

When using the transition mode "out-in" in Vue, the returned ref element may be undefined

Encountering an issue where when mode="out-in" is used in a <transition mode="out-in">, a ref'd element returns undefined during the `updated` lifecycle. Strangely, it works fine without the `mode="out-in"` attribute. Any suggestions on how to ...

Issue with VueJS and Jest: Module 'babel-core' not found

While attempting to integrate Jest with VueJS, I encountered an issue: Cannot find module 'babel-core' at Object.<anonymous> (node_modules/vue-jest/lib/compilers/babel-compiler.js:1:15). To resolve this, I had to include "@babel/core": " ...

Using Vuejs to pass the SAVE function into a CRUD component

I am facing a challenge in finding a suitable solution that involves advanced parent-child communication in Vue.js. The scenario is such that there are multiple parent components, each with its own logic on how to save data. On the other hand, there is onl ...

It seems like my Vue 3 project's `npm run serve` command is broken and showing a missing require stack error

Whenever I attempt to execute npm run serve, the following error message is displayed... INFO Starting development server... ERROR Error: Cannot find module 'caniuse-lite/data/features/css-unicode-bidi' Require stack: - D:\VueJs Acad&bso ...

Vue.js - Trouble with Rendering Elements

I'm facing an issue with displaying data fetched from an API using axios in Vue.js. Despite writing the code, the elements are not being rendered and don't appear in the elements console. See below for the code snippets: <div class=" ...

Expanding the properties of an object dynamically and 'directly' by utilizing `this` in JavaScript/TypeScript

Is it possible to directly add properties from an object "directly" to this of a class in JavaScript/TypeScript, bypassing the need to loop through the object properties and create them manually? I have attempted something like this but it doesn't se ...

Angular is able to successfully retrieve the current route when it is defined, but

Here's the code snippet I am working with: import { Router } from '@angular/router'; Following that, in my constructor: constructor(router: Router) { console.log(this.router.url); } Upon loading the page, it initially shows the URL a ...

Embracing the "export ... from" feature in the TypeScript compiler

Can the tsc compiler handle this particular export statement? export {PromiseWrapper, Promise, PromiseCompleter} from 'angular2/src/facade/promise'; Your assistance is greatly appreciated! ...

Parent Route Abstractions in Vue-Router

I am currently in the process of transitioning my existing website to vuejs. The navigation structure I aim for is as follows: /login /signup /password-reset /browse /search ... numerous other paths Since some of these paths have similar functionalities, ...

Trigger a Tabulator event when a checkbox is selected in Vue 3

Currently, I am utilizing Vue3 along with Tabulator (5.2.7) to create a data table using the Composition API. In the following code snippets, I have excluded irrelevant parts of the code. //DataTable.vue <script setup> import { TabulatorFull as Tabu ...

Arranging an array of integers followed by sorting by the decimal part of each value in a particular sequence using JavaScript

Below is an example of sorting an array: let arr = ['100.12', '100.8', '100.11', '100.9']; When sorted traditionally, the output is: '100.11', '100.12', '100.8', '100.9' Ho ...

Using T and null for useRef in React is now supported through method overloading

The React type definition for useRef includes function overloading for both T|null and T: function useRef<T>(initialValue: T): MutableRefObject<T>; // convenience overload for refs given as a ref prop as they typically start with a null ...

Creating mutual reactivity between two inputs in Vue.js

I am in the process of creating a tool that calculates the cost of purchasing specific materials. One challenge I'm facing is that users sometimes buy by mass and other times by volume. My goal is to have two active input fields (one for mass and one ...

Using Typescript to typecast in D3.js

Utilizing the D3 graph example available here. I've defined my data object as shown below: interface ID3Data { age: string, population: number } const data: ID3Data[] = [ { age: "<5", population: 2704659 }, { age: "5-13", population: 4499 ...

I'm having trouble understanding why I can't access the properties of a class within a function that has been passed to an Angular

Currently, I have integrated HTML 5 geolocation into an Angular component: ... export class AngularComponent { ... constructor(private db: DatabaseService) {} // this function is linked to an HTML button logCoords(message, ...