Vue 3 introduced a new, better way to create components, the Composition API. The Composition API’s features make it possible to better maintain code as components grow, reuse code easily, and write reliable code, all while providing TypeScript support.
However, Vue components rely heavily on the JavaScript object’s this
keyword, making it confusing to create a TypeScript object. To solve this issue, the Vue Class Component plugin uses ECMAScript decorators to pass statically typed values directly to components in Vue, making the compiler understand what is happening.
In this article, you’ll learn how to define properties like data, methods, computed properties, props, and watchers directly on the class in Vue components by supporting TypeScript in Vue class-based components. We’ll use Vue Class Component and Vue Property Decorator, and we’ll also cover the various JavaScript equivalents.
To follow along with this tutorial, you’ll need knowledge of TypeScript. You’ll also need npm and Node.js ≥v12.22.0 installed on your local machine. To confirm that Node.js is installed, run the following command in your terminal:
node -v
If you don’t have Node.js installed, you can follow the instructions on the official Node.js website. Lastly, you’ll need familiarity with setting up a Vue project. Let’s get started!
Open your terminal and cd
into the directory where you want to save the project. Run the following command. Note that you can name your project whatever you like:
vue create vue-props-example
While setting up your Vue project, you’ll be asked a series of questions. For the purpose of this tutorial, you should choose the following configurations:
Prompt | Option |
---|---|
Please pick a preset | Manually select features |
Check the features needed for your project | TypeScript |
Choose a version of Vue that you want to start the project with | 3.x |
Use class-style component syntax? | Y |
Use Babel alongside TypeScript? | Y |
If you look at the package.json
file, you’ll see that by installing our project with TypeScript, we also install the vue-class-component
plugin, which is used to define class-style components and is installed by default when a TypeScript and Vue 3 project is created.
The App.vue
file uses Vue Class Component, which uses the Options
decorator to define a Vue component. At this point, you have a Vue 3 project that includes the power of TypeScript.
Some of the most important parts of Vue are missing in Vue Class Component, for example, ECMAScript decorators like props
, inject
, model
, ref
, emit
, and provide
. As a result, the Vue community introduced a library called Vue Property Decorator, which fully depends on the Vue Class Component and is now fully endorsed by the Vue core team.
We’ll install Vue Property Decorator in our project by running the following command in the project directory:
npm i vue-property-decorator
Now, we can fully use class-based components in TypeScript.
With the class-style component syntax, we’ll build components by extending the Vue object. In App.vue
, copy and paste the following code to create a simple single-file component with TypeScript:
<template> <div> <label>Update inputData <input :value="inputData" @input="updateInputData($event)"/> </label> <div>{{ inputData }}</div> </div> </template> <script lang="ts"> import { Component, Vue } from 'vue-property-decorator' @Component export default class App extends Vue { // Data property inputData: string = 'My Input Data' // Component method updateInputData ($event: { target: { value: string } }) { this.inputData = $event.target.value } } </script>
The code above defines properties directly on the class and methods, which is possible with the help of the @Component(componentConfig)
decorator. During the compilation, the @Component(componentConfig)
decorator transforms the class into a format Vue can understand.
Now, when the app is compiled, if you look at your browser at http://localhost:8080/
, you’ll see an input field and some text reading myInputData
. myInputData
will interact with the input field, updating accordingly to reflect the changes made to the user’s input texts on the browser.
With the @Prop
decorator from the Vue Property Decorator library, we can define props on the class. We can also provide additional details for props, like required
, type
, and default
to allow reusability of data objects.
We first import the Prop
decorator from the Vue Property Decorator library and write the following code:
<script lang="ts"> import { Component, Prop, Vue } from 'vue-property-decorator' @Component export default class App extends Vue { @Prop() readonly text!: string @Prop({default: 'John doe'}) readonly name: string @Prop({required: true}) readonly age: number @Prop(String) readonly phone: string @Prop({required: true, type: String, default: 'Student'}) readonly job: string } </script>
The code above, written in TypeScript, defines various props called text
, name
, age
, phone
, and job
. To avoid modifying the prop, the readonly
attribute is assigned.
In JavaScript, the code above is equivalent to the following:
<script> export default { props: { text, name: { default: 'John doe' }, age: { required: true, }, phone: { type: String }, job: { required: true, type: String, default: 'Student' } } } </script>
Computed properties are written as getters
and setters
and defined on the class. To demonstrate, the example below contains computed properties in TypeScript that get a computed property called computedProp
and return a random number, displaying it on the browser:
<template> <div>{{ computedProp }}</div> </template> <script lang="ts"> import { Component, Vue } from 'vue-property-decorator' @Component export default class App extends Vue { get computedProp() { return Math.random() } } </script>
The JavaScript equivalent to the code above is:
<style> export default { computed: { computedProp() { return Math.random() } } } </style>
We can define watchers on the class by importing the @Watch(propertyString, config)
decorator from the Vue Property Decorator library. You’ll notice this is quite different from how it’s normally written in JavaScript.
Below is an example in TypeScript that watches for when myWatchedData
triggers onDataChanged
:
<template> <div> <label>Update myWatchedData <input :value="myWatchedData" @input="updateMyData($event)"/> </label> <div>{{ myWatchedDataStatus }}</div> </div> </template> <script lang="ts"> import { Component, Watch, Vue } from 'vue-property-decorator' @Component export default class App extends Vue { myWatchedData: string = 'Watched Data' myWatchedDataStatus: string = '' @Watch('myWatchedData') onDataChanged(value: string, oldValue: string) { this.myWatchedDataStatus = 'Watched Data Changed' } updateMyData ($event: { target: { value: string } }) { this.myWatchedPData = $event.target.value } } </script>
The JavaScript equivalent to the code above is:
<script> export default { data() { return { myWatchedProperty: null } } methods: { onPropertyChanged(value, oldValue) { // ... } } watch: { myWatchedProperty: { handler: 'onPropertyChanged', immediate: false, deep: true } } } </script>
With the code above, when you visit the browser at http://localhost:8080/
, you’ll see an input field. Now, changing the input value will display the message Watched Data Changed
.
In this article, we used Vue Class Component and Vue Property Decorator to define properties directly on the class in Vue components, allowing us to support TypeScript in Vue class-based components.
This article introduced various decorators likecomponent
, prop
, watch
, get
. You can learn more about decorators and class-based declarations in the documentation for Vue Class Component and Vue Property Decorator linked above.
Creating Vue components with TypeScript may be a bit confusing, for starters, but when you get used to it, you get to enjoy all the benefits that it brings, making your application more reliable. I hope you enjoyed this article, happy coding!
Debugging Vue.js applications can be difficult, especially when there are dozens, if not hundreds of mutations during a user session. If you’re interested in monitoring and tracking Vue mutations for all of your users in production, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording literally everything that happens in your Vue apps, including network requests, JavaScript errors, performance problems, and much more. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.
The LogRocket Vuex plugin logs Vuex mutations to the LogRocket console, giving you context around what led to an error and what state the application was in when an issue occurred.
Modernize how you debug your Vue apps — start monitoring for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to implement one-way and two-way data binding in Vue.js, using v-model and advanced techniques like defineModel for better apps.
Compare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
3 Replies to "Define properties with Vue Property Decorator and TypeScript"
I only got as far as “Creating a TypeScript Vue component” before it all blew up!
ERROR in src/App.vue:14:2
TS1238: Unable to resolve signature of class decorator when called as an expression.
This expression is not callable.
Type ‘typeof import(“/home/mark/repos/vue-props-example/node_modules/vue-class-component/dist/vue-class-component”)’ has no call signatures.
12 | import { Component, Vue } from “vue-property-decorator”;
13 |
> 14 | @Component
is there a solution to this not working? I just can’t get vue-property decorator to work at all
vue-property decorator is not compatable with vue 3