In TypeScript, decorators are a powerful feature that allows you to modify or enhance classes and their members (such as methods, properties, and accessors) in a declarative manner. One of the common use cases for decorators is to work with class properties. This tutorial will guide you through understanding and using property decorators in TypeScript.
A property decorator is a function that can be applied to a class property declaration. It receives two arguments:
The primary purpose of a property decorator is to observe, modify, or replace the behavior of a class property.
Let's dive into some practical examples to understand how property decorators work.
Suppose you want to log every time a property is accessed. You can create a property decorator for this purpose.
1function logProperty(target: any, key: string) {2let value = target[key];34const getter = function () {5console.log(`Getting ${key} with value ${value}`);6return value;7};89const setter = function (newVal: any) {10console.log(`Setting ${key} to ${newVal}`);11value = newVal;12};1314Object.defineProperty(target, key, {15get: getter,16set: setter,17enumerable: true,18configurable: true19});20}2122class Person {23@logProperty24public name: string;2526constructor(name: string) {27this.name = name;28}29}3031const person = new Person('John');32console.log(person.name); // Getting name with value John33person.name = 'Jane'; // Setting name to Jane
In this example, the logProperty decorator logs every time the name property is accessed or modified. The Object.defineProperty method is used to redefine the getter and setter of the property.
Another common use case for decorators is to validate property values. Let's create a decorator that ensures a property value meets certain criteria.
1function validate(target: any, key: string) {2let value = target[key];34const getter = function () {5return value;6};78const setter = function (newVal: any) {9if (typeof newVal !== 'string' || newVal.length < 3) {10throw new Error(`Invalid value for ${key}`);11}12value = newVal;13};1415Object.defineProperty(target, key, {16get: getter,17set: setter,18enumerable: true,19configurable: true20});21}2223class User {24@validate25public username: string;2627constructor(username: string) {28this.username = username;29}30}3132const user = new User('john_doe');33console.log(user.username); // john_doe3435try {36user.username = 'jo'; // Throws an error37} catch (e) {38console.error(e.message); // Invalid value for username39}
In this example, the validate decorator ensures that the username property is a string with at least three characters. If the validation fails, it throws an error.
Now that you've learned about property decorators, you might want to explore accessor decorators next. Accessor decorators allow you to modify or enhance class accessors (getters and setters) in a similar way to property decorators.
Stay tuned for more tutorials on TypeScript decorators!