What is vue-property-decorator?
The vue-property-decorator package provides TypeScript decorators for Vue components, making it easier to write Vue components with TypeScript. It simplifies the syntax and enhances code readability by allowing the use of decorators for properties, methods, and lifecycle hooks.
What are vue-property-decorator's main functionalities?
Component Decorator
The @Component decorator is used to define a Vue component. It allows you to use class-style syntax to define your component, making the code more organized and readable.
import { Vue, Component } from 'vue-property-decorator';
@Component
export default class MyComponent extends Vue {
// Component logic here
}
Prop Decorator
The @Prop decorator is used to define props in a Vue component. It simplifies the syntax for declaring props and their types, making the code more concise.
import { Vue, Component, Prop } from 'vue-property-decorator';
@Component
export default class MyComponent extends Vue {
@Prop(String) readonly myProp!: string;
}
Watch Decorator
The @Watch decorator is used to watch for changes in a component's data properties. It simplifies the syntax for setting up watchers, making the code more readable.
import { Vue, Component, Watch } from 'vue-property-decorator';
@Component
export default class MyComponent extends Vue {
myData = '';
@Watch('myData')
onMyDataChanged(newVal: string, oldVal: string) {
console.log(`myData changed from ${oldVal} to ${newVal}`);
}
}
Emit Decorator
The @Emit decorator is used to emit custom events from a component. It simplifies the syntax for emitting events, making the code more concise and readable.
import { Vue, Component, Emit } from 'vue-property-decorator';
@Component
export default class MyComponent extends Vue {
@Emit('myEvent')
myMethod() {
return 'data';
}
}
Model Decorator
The @Model decorator is used to create a two-way binding for a prop. It simplifies the syntax for defining v-model bindings, making the code more concise.
import { Vue, Component, Model } from 'vue-property-decorator';
@Component
export default class MyComponent extends Vue {
@Model('change', { type: String }) readonly value!: string;
}
Other packages similar to vue-property-decorator
vue-class-component
The vue-class-component package allows you to define Vue components using class-style syntax in TypeScript. It provides a base class for Vue components and supports decorators for properties and methods. Compared to vue-property-decorator, it is more lightweight but requires additional packages for full decorator support.
vuex-class
The vuex-class package provides TypeScript decorators and helpers for Vuex. It allows you to use class-style syntax to define Vuex modules, actions, getters, and mutations. While it focuses on Vuex integration, it complements vue-property-decorator by providing similar decorator-based syntax for Vuex.
Vue Property Decorator

This library fully depends on vue-class-component, so please read its README before using this library.
License
MIT License
Install
npm i -S vue-property-decorator
Usage
There are several decorators and 1 function (Mixin):
See also
vuex-class
@Prop(options: (PropOptions | Constructor[] | Constructor) = {})
decorator
import { Vue, Component, Prop } from 'vue-property-decorator'
@Component
export default class YourComponent extends Vue {
@Prop(Number) readonly propA: number | undefined
@Prop({ default: 'default value' }) readonly propB!: string
@Prop([String, Boolean]) readonly propC: string | boolean | undefined
}
is equivalent to
export default {
props: {
propA: {
type: Number,
},
propB: {
default: 'default value',
},
propC: {
type: [String, Boolean],
},
},
}
Note that:
If you'd like to set type
property of each prop value from its type definition, you can use reflect-metadata.
- Set
emitDecoratorMetadata
to true
.
- Import
reflect-metadata
before importing vue-property-decorator
(importing reflect-metadata
is needed just once.)
import 'reflect-metadata'
import { Vue, Component, Prop } from 'vue-property-decorator'
@Component
export default class MyComponent extends Vue {
@Prop() age!: number
}
Each prop's default value need to be defined as same as the example code shown in above.
It's not supported to define each default
property like @Prop() prop = 'default value'
.
@PropSync(propName: string, options: (PropOptions | Constructor[] | Constructor) = {})
decorator
import { Vue, Component, PropSync } from 'vue-property-decorator'
@Component
export default class YourComponent extends Vue {
@PropSync('name', { type: String }) syncedName!: string
}
is equivalent to
export default {
props: {
name: {
type: String,
},
},
computed: {
syncedName: {
get() {
return this.name
},
set(value) {
this.$emit('update:name', value)
},
},
},
}
@PropSync
works like @Prop
besides the fact that it takes the propName as an argument of the decorator, and also creates a computed getter and setter behind the scenes. This way you can interface with the property as if it was a regular data property whilst making it as easy as appending the .sync
modifier in the parent component.
@Model(event?: string, options: (PropOptions | Constructor[] | Constructor) = {})
decorator
import { Vue, Component, Model } from 'vue-property-decorator'
@Component
export default class YourComponent extends Vue {
@Model('change', { type: Boolean }) readonly checked!: boolean
}
is equivalent to
export default {
model: {
prop: 'checked',
event: 'change',
},
props: {
checked: {
type: Boolean,
},
},
}
@Model
property can also set type
property from its type definition via reflect-metadata
.
@ModelSync(propName: string, event?: string, options: (PropOptions | Constructor[] | Constructor) = {})
decorator
import { Vue, Component, ModelSync } from 'vue-property-decorator'
@Component
export default class YourComponent extends Vue {
@ModelSync('checked', 'change', { type: Boolean })
readonly checkedValue!: boolean
}
is equivalent to
export default {
model: {
prop: 'checked',
event: 'change',
},
props: {
checked: {
type: Boolean,
},
},
computed: {
checkedValue: {
get() {
return this.checked
},
set(value) {
this.$emit('change', value)
},
},
},
}
@ModelSync
property can also set type
property from its type definition via reflect-metadata
.
@Watch(path: string, options: WatchOptions = {})
decorator
import { Vue, Component, Watch } from 'vue-property-decorator'
@Component
export default class YourComponent extends Vue {
@Watch('child')
onChildChanged(val: string, oldVal: string) {}
@Watch('person', { immediate: true, deep: true })
onPersonChanged1(val: Person, oldVal: Person) {}
@Watch('person')
onPersonChanged2(val: Person, oldVal: Person) {}
}
is equivalent to
export default {
watch: {
child: [
{
handler: 'onChildChanged',
immediate: false,
deep: false,
},
],
person: [
{
handler: 'onPersonChanged1',
immediate: true,
deep: true,
},
{
handler: 'onPersonChanged2',
immediate: false,
deep: false,
},
],
},
methods: {
onChildChanged(val, oldVal) {},
onPersonChanged1(val, oldVal) {},
onPersonChanged2(val, oldVal) {},
},
}
@Provide(key?: string | symbol)
/ @Inject(options?: { from?: InjectKey, default?: any } | InjectKey)
decorator
import { Component, Inject, Provide, Vue } from 'vue-property-decorator'
const symbol = Symbol('baz')
@Component
export class MyComponent extends Vue {
@Inject() readonly foo!: string
@Inject('bar') readonly bar!: string
@Inject({ from: 'optional', default: 'default' }) readonly optional!: string
@Inject(symbol) readonly baz!: string
@Provide() foo = 'foo'
@Provide('bar') baz = 'bar'
}
is equivalent to
const symbol = Symbol('baz')
export const MyComponent = Vue.extend({
inject: {
foo: 'foo',
bar: 'bar',
optional: { from: 'optional', default: 'default' },
baz: symbol,
},
data() {
return {
foo: 'foo',
baz: 'bar',
}
},
provide() {
return {
foo: this.foo,
bar: this.baz,
}
},
})
@ProvideReactive(key?: string | symbol)
/ @InjectReactive(options?: { from?: InjectKey, default?: any } | InjectKey)
decorator
These decorators are reactive version of @Provide
and @Inject
. If a provided value is modified by parent component, then the child component can catch this modification.
const key = Symbol()
@Component
class ParentComponent extends Vue {
@ProvideReactive() one = 'value'
@ProvideReactive(key) two = 'value'
}
@Component
class ChildComponent extends Vue {
@InjectReactive() one!: string
@InjectReactive(key) two!: string
}
@Emit(event?: string)
decorator
The functions decorated by @Emit
$emit
their return value followed by their original arguments. If the return value is a promise, it is resolved before being emitted.
If the name of the event is not supplied via the event
argument, the function name is used instead. In that case, the camelCase name will be converted to kebab-case.
import { Vue, Component, Emit } from 'vue-property-decorator'
@Component
export default class YourComponent extends Vue {
count = 0
@Emit()
addToCount(n: number) {
this.count += n
}
@Emit('reset')
resetCount() {
this.count = 0
}
@Emit()
returnValue() {
return 10
}
@Emit()
onInputChange(e) {
return e.target.value
}
@Emit()
promise() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(20)
}, 0)
})
}
}
is equivalent to
export default {
data() {
return {
count: 0,
}
},
methods: {
addToCount(n) {
this.count += n
this.$emit('add-to-count', n)
},
resetCount() {
this.count = 0
this.$emit('reset')
},
returnValue() {
this.$emit('return-value', 10)
},
onInputChange(e) {
this.$emit('on-input-change', e.target.value, e)
},
promise() {
const promise = new Promise((resolve) => {
setTimeout(() => {
resolve(20)
}, 0)
})
promise.then((value) => {
this.$emit('promise', value)
})
},
},
}
@Ref(refKey?: string)
decorator
import { Vue, Component, Ref } from 'vue-property-decorator'
import AnotherComponent from '@/path/to/another-component.vue'
@Component
export default class YourComponent extends Vue {
@Ref() readonly anotherComponent!: AnotherComponent
@Ref('aButton') readonly button!: HTMLButtonElement
}
is equivalent to
export default {
computed() {
anotherComponent: {
cache: false,
get() {
return this.$refs.anotherComponent as AnotherComponent
}
},
button: {
cache: false,
get() {
return this.$refs.aButton as HTMLButtonElement
}
}
}
}
@VModel(propsArgs?: PropOptions)
decorator
import { Vue, Component, VModel } from 'vue-property-decorator'
@Component
export default class YourComponent extends Vue {
@VModel({ type: String }) name!: string
}
is equivalent to
export default {
props: {
value: {
type: String,
},
},
computed: {
name: {
get() {
return this.value
},
set(value) {
this.$emit('input', value)
},
},
},
}