TypeScript decorators for Vue.js class-based components with property binding, state management, and lifecycle capabilities
—
Decorators for handling component lifecycle events and custom event emission. These decorators provide reactive data observation and automatic event emission patterns for Vue class-based components.
Creates reactive watchers for data properties, enabling components to respond to changes in data, props, or computed properties.
/**
* Creates reactive watchers for data properties
* @param path - Property path or expression to observe (required)
* @param options - WatchOptions object (optional, defaults to {})
* @returns Method decorator function
*/
function Watch(path: string, options: WatchOptions = {}): MethodDecorator;
interface WatchOptions {
deep?: boolean;
immediate?: boolean;
}Usage Examples:
import { Vue, Component, Watch, Prop } from "vue-property-decorator";
@Component
export default class MyComponent extends Vue {
@Prop()
count!: number;
localData = 0;
user = { name: "", age: 0 };
// Basic watcher
@Watch("count")
onCountChanged(newVal: number, oldVal: number) {
console.log(`count changed from ${oldVal} to ${newVal}`);
}
// Deep watcher for objects
@Watch("user", { deep: true })
onUserChanged(newUser: any, oldUser: any) {
console.log("User object changed:", newUser);
}
// Immediate watcher (executes on component creation)
@Watch("localData", { immediate: true })
onLocalDataChanged(newVal: number, oldVal: number) {
if (newVal > 10) {
this.$emit("threshold-exceeded", newVal);
}
}
// Watch nested properties
@Watch("user.name")
onUserNameChanged(newName: string, oldName: string) {
console.log(`User name changed from ${oldName} to ${newName}`);
}
// Watch computed properties
get fullName() {
return `${this.user.name} (${this.user.age})`;
}
@Watch("fullName")
onFullNameChanged(newFullName: string) {
console.log("Full name updated:", newFullName);
}
}Automatically emits events with method return values and arguments, providing a declarative way to handle component event emission.
/**
* Automatically emits events with method return values and arguments
* @param event - Event name (optional, defaults to kebab-case method name)
* @returns Method decorator function
*/
function Emit(event?: string): MethodDecorator;Usage Examples:
import { Vue, Component, Emit } from "vue-property-decorator";
@Component
export default class MyComponent extends Vue {
count = 0;
// Basic emit - event name defaults to kebab-case method name
@Emit()
increment() {
this.count++;
return this.count; // Emitted as first argument
}
// Custom event name
@Emit("custom-event")
handleCustomAction() {
return { timestamp: Date.now(), action: "custom" };
}
// Emit with method arguments
@Emit("user-updated")
updateUser(name: string, age: number) {
// Method arguments are emitted after return value
return { success: true }; // Emitted as [{ success: true }, "name", age]
}
// Emit without return value
@Emit("button-clicked")
handleClick() {
// No return value, only method arguments are emitted
console.log("Button was clicked");
}
// Emit with Promise return value
@Emit("async-complete")
async performAsyncAction() {
const result = await this.fetchData();
return result; // Promise result is awaited and then emitted
}
// Complex emit with multiple arguments
@Emit("data-processed")
processData(input: any[], options: any) {
const processed = input.map(item => ({ ...item, processed: true }));
return {
processed,
originalCount: input.length,
processedCount: processed.length
};
// Emits: [returnValue, input, options]
}
private async fetchData() {
// Simulate async operation
return new Promise(resolve => {
setTimeout(() => resolve({ data: "fetched" }), 1000);
});
}
}import { Vue, Component, Watch, Prop } from "vue-property-decorator";
@Component
export default class ValidationForm extends Vue {
@Prop({ required: true })
initialData!: any;
formData = {
email: "",
password: "",
confirmPassword: ""
};
errors: any = {};
@Watch("formData.email")
validateEmail(email: string) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
this.errors.email = emailRegex.test(email) ? null : "Invalid email format";
}
@Watch("formData.password")
validatePassword(password: string) {
this.errors.password = password.length >= 8 ? null : "Password must be at least 8 characters";
// Re-validate confirm password when password changes
this.validateConfirmPassword(this.formData.confirmPassword);
}
@Watch("formData.confirmPassword")
validateConfirmPassword(confirmPassword: string) {
this.errors.confirmPassword =
confirmPassword === this.formData.password ? null : "Passwords do not match";
}
@Watch("initialData", { immediate: true, deep: true })
loadInitialData(data: any) {
if (data) {
this.formData = { ...data };
}
}
}import { Vue, Component, Emit, Watch } from "vue-property-decorator";
@Component
export default class DataProcessor extends Vue {
rawData: any[] = [];
processing = false;
@Watch("rawData")
onDataChanged(newData: any[]) {
if (newData.length > 0) {
this.startProcessing();
}
}
@Emit("processing-started")
startProcessing() {
this.processing = true;
return { timestamp: Date.now(), itemCount: this.rawData.length };
}
@Emit("item-processed")
processItem(item: any, index: number) {
// Simulate processing
const processed = { ...item, processed: true, index };
return processed;
}
@Emit("processing-complete")
completeProcessing() {
this.processing = false;
return {
timestamp: Date.now(),
totalProcessed: this.rawData.length,
success: true
};
}
@Emit("processing-error")
handleProcessingError(error: Error) {
this.processing = false;
return { error: error.message, timestamp: Date.now() };
}
}import { Vue, Component, Watch, Emit } from "vue-property-decorator";
@Component
export default class RealTimeSync extends Vue {
localData: any = {};
syncEnabled = true;
lastSyncTime = 0;
@Watch("localData", { deep: true })
onLocalDataChanged(newData: any, oldData: any) {
if (this.syncEnabled && this.hasSignificantChanges(newData, oldData)) {
this.scheduleSync();
}
}
@Watch("syncEnabled")
onSyncToggled(enabled: boolean) {
if (enabled) {
this.performSync();
}
}
@Emit("sync-requested")
scheduleSync() {
// Debounce sync requests
clearTimeout(this.syncTimer);
this.syncTimer = setTimeout(() => {
this.performSync();
}, 1000);
return { data: this.localData, scheduled: true };
}
@Emit("sync-complete")
async performSync() {
try {
await this.syncToServer(this.localData);
this.lastSyncTime = Date.now();
return { success: true, timestamp: this.lastSyncTime };
} catch (error) {
this.handleSyncError(error);
}
}
@Emit("sync-error")
handleSyncError(error: any) {
return { error: error.message, timestamp: Date.now() };
}
private syncTimer: any;
private hasSignificantChanges(newData: any, oldData: any): boolean {
// Implementation for determining significant changes
return JSON.stringify(newData) !== JSON.stringify(oldData);
}
private async syncToServer(data: any): Promise<void> {
// Implementation for server synchronization
return new Promise((resolve) => {
setTimeout(resolve, 500); // Simulate network delay
});
}
}interface WatchOptions {
deep?: boolean;
immediate?: boolean;
}
type MethodDecorator = (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) => void;Install with Tessl CLI
npx tessl i tessl/npm-vue-property-decorator