TypeScript dekoratorlar
Dekoratorlar kuchli TypeScript xususiyati bo‘lib, u sizga metama’lumotlarni qo‘shish va dizayn vaqtida classlar va ularning a’zolarini o‘zgartirish imkonini beradi.
Ular Angular va NestJS kabi frameworklarda qaramlikni in’ektsiya qilish, marshrutlash va boshqalar uchun keng qo‘llaniladi.
Dekoratorlarni yoqish
TypeScript-da dekorativlardan foydalanish uchun ularni tsconfig.json da yoqishingiz kerak:
tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"strictPropertyInitialization": false
},
"include": ["src/**/*.ts"]
}
Eslatma: emitDecoratorMetadata opsiyasi TypeORM va class-validator kabi kutubxonalar tomonidan ishlatiladigan dekorativlar uchun turdagi metama’lumotlarni chiqarish uchun eksperimental yordamni ta’minlaydi.
Dekoratorlarning turlari
TypeScript turli xil deklaratsiyalarga qo‘llanilishi mumkin bo‘lgan bir necha turdagi dekorativlarni qo‘llab-quvvatlaydi:
| Decorator Type | Nimaga qo‘llaniladi | Signature |
|---|---|---|
| Class Decorator | Class declarations | (constructor: Function) => void |
| Method Decorator | Class methods | (target: any, propertyKey: string, descriptor: PropertyDescriptor) => void |
| Property Decorator | Class properties | (target: any, propertyKey: string) => void |
| Parameter Decorator | Method parameters | (target: any, propertyKey: string, parameterIndex: number) => void |
Class dekoratorlari
Class dekoratorlari class konstruktoriga qo‘llaniladi va class ta’rifini kuzatish, o‘zgartirish yoki almashtirish uchun ishlatilishi mumkin.
Ular misollar yaratilganda emas, balki class e’lon qilinganda chaqiriladi.
Asosiy class dekoratori
Ushbu oddiy dekorator class aniqlanganda qayd qiladi:
// A simple class decorator that logs class definition
function logClass(constructor: Function) {
console.log(`Class ${constructor.name} was defined at ${new Date().toISOString()}`);
}
// Applying the decorator
@logClass
class UserService {
getUsers() {
return ['Alice', 'Bob', 'Charlie'];
}
}
// Output when the file is loaded: "Class UserService was defined at [timestamp]"
O‘zingiz sinab ko‘ring »
Konstruktor modifikatsiyasi bilan class dekoratori
Ushbu misol xususiyatlar va usullarni qo‘shish orqali sinfni qanday o‘zgartirishni ko‘rsatadi:
// A decorator that adds a version property and logs instantiation
function versioned(version: string) {
return function (constructor: Function) {
// Add a static property
constructor.prototype.version = version;
// Store the original constructor
const original = constructor;
// Create a new constructor that wraps the original
const newConstructor: any = function (...args: any[]) {
console.log(`Creating instance of ${original.name} v${version}`);
return new original(...args);
};
// Copy prototype so instanceof works
newConstructor.prototype = original.prototype;
return newConstructor;
};
}
// Applying the decorator with a version
@versioned('1.0.0')
class ApiClient {
fetchData() {
console.log('Fetching data...');
}
}
const client = new ApiClient();
console.log((client as any).version); // Outputs: 1.0.0
client.fetchData();
O‘zingiz sinab ko‘ring »
Muhrlangan class dekoratori
Ushbu dekorator yangi xususiyatlarni sinfga qo‘shishni oldini oladi va barcha mavjud xususiyatlarni sozlanmaydigan qilib belgilaydi:
function sealed(constructor: Function) {
console.log(`Sealing ${constructor.name}...`);
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return `Hello, ${this.greeting}`;
}
}
// This will throw an error in strict mode
// Greeter.prototype.newMethod = function() {}; // Error: Cannot add property newMethod
O‘zingiz sinab ko‘ring »
Class dekoratorlari haqida asosiy fikrlar
- Class dekoratorlari namunalar yaratilganda emas, balki class e’lon qilinganda chaqiriladi
- Ular class konstruktorini yagona parametr sifatida qabul qilishadi
- Ular asl sinfni almashtirish uchun yangi konstruktor funksiyasini qaytarishi mumkin
- Ular pastdan yuqoriga qarab bajariladi (eng ichki dekorator birinchi ishlaydi)
- Ular jurnalga yozish, muhrlash, muzlatish yoki metama’lumotlarni qo‘shish uchun ishlatilishi mumkin
Dekorativ uslublar
Metod dekoratorlari metod ta’riflariga qo‘llaniladi va usul ta’riflarini kuzatish, o‘zgartirish yoki almashtirish uchun ishlatilishi mumkin.
Ular uchta parametrni oladi:
target: Class prototipi (masalan, usullar) yoki konstruktor funksiyasi (statik usullar uchun)propertyKey: Usul nomidescriptor: Usul uchun xossa identifikatori
Vaqtni belgilash usuli
Ushbu dekorator usulning bajarilish vaqtini o‘lchaydi va qayd qiladi:
// Method decorator to measure execution time
function measureTime(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
const start = performance.now();
const result = originalMethod.apply(this, args);
const end = performance.now();
console.log(`${propertyKey} executed in ${(end - start).toFixed(2)}ms`);
return result;
};
return descriptor;
}
// Using the decorator
class DataProcessor {
@measureTime
processData(data: number[]): number[] {
// Simulate processing time
for (let i = 0; i < 100000000; i++) { /* processing */ }
return data.map(x => x * 2);
}
}
// When called, it will log the execution time
const processor = new DataProcessor();
processor.processData([1, 2, 3, 4, 5]);
O‘zingiz sinab ko‘ring »
Usul avtorizatsiya dekoratori
Ushbu misol uslublar dekoratorlari yordamida rolga asoslangan kirishni boshqarishni qanday amalga oshirishni ko‘rsatadi:
// User roles
type UserRole = 'admin' | 'editor' | 'viewer';
// Current user context (simplified)
const currentUser = {
id: 1,
name: 'John Doe',
roles: ['viewer'] as UserRole[]
};
// Decorator factory for role-based access control
function AllowedRoles(...allowedRoles: UserRole[]) {
return function (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
const hasPermission = allowedRoles.some(role =>
currentUser.roles.includes(role)
);
if (!hasPermission) {
throw new Error(
`User ${currentUser.name} is not authorized to call ${propertyKey}`
);
}
return originalMethod.apply(this, args);
};
return descriptor;
};
}
// Using the decorator
class DocumentService {
@AllowedRoles('admin', 'editor')
deleteDocument(id: string) {
console.log(`Document ${id} deleted`);
}
@AllowedRoles('admin', 'editor', 'viewer')
viewDocument(id: string) {
console.log(`Viewing document ${id}`);
}
}
// Usage
const docService = new DocumentService();
try {
docService.viewDocument('doc123'); // Works - viewer role is allowed
docService.deleteDocument('doc123'); // Throws error - viewer cannot delete
} catch (error) {
console.error(error.message);
}
// Change user role to admin
currentUser.roles = ['admin'];
docService.deleteDocument('doc123'); // Now works - admin can delete
O‘zingiz sinab ko‘ring »
Eskirish haqida ogohlantirish dekoratori
Ushbu dekorator kelajakdagi versiyada olib tashlanadigan usulga eskirish haqida ogohlantirish qo‘shadi:
function deprecated(message: string) {
return function (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.warn(`Warning: ${propertyKey} is deprecated. ${message}`);
return originalMethod.apply(this, args);
};
return descriptor;
};
}
class PaymentService {
@deprecated('Use processPaymentV2 instead')
processPayment(amount: number, currency: string) {
console.log(`Processing payment of ${amount} ${currency}`);
}
processPaymentV2(amount: number, currency: string) {
console.log(`Processing payment v2 of ${amount} ${currency}`);
}
}
const payment = new PaymentService();
payment.processPayment(100, 'USD'); // Shows deprecation warning
payment.processPaymentV2(100, 'USD'); // No warning
O‘zingiz sinab ko‘ring »
Usul dekorativlari haqida asosiy fikrlar
- Metod dekoratorlari usul chaqirilganda emas, balki aniqlanganda chaqiriladi
- Ular usulning harakatini qo‘shimcha mantiq bilan o‘rash orqali o‘zgartirishi mumkin
- Ular ro‘yxatga olish, tekshirish va avtorizatsiya kabi o‘zaro bog‘liq muammolar uchun ishlatilishi mumkin
- Ular usulning xulq-atvorini o‘zgartirishga imkon beruvchi xususiyat deskriptorini oladi
- Ular xususiyat deskriptorini (property descriptor) yoki undefined qaytarishi kerak (agar deskriptor o‘zgartirilmasa)
Ko‘chmas xususiyat dekorativlari
Xususiyat dekorativlari xususiyat deklaratsiyasiga qo‘llaniladi va xususiyat ta’riflarini kuzatish, o‘zgartirish yoki almashtirish uchun ishlatilishi mumkin.
Ular ikkita parametrni oladi:
target: Class prototipi (masalan, xususiyatlar) yoki konstruktor funksiyasi (statik xususiyatlar uchun)propertyKey: Xususiyat nomi
Formatlangan xususiyat dekoratori
Ushbu dekorator o‘rnatilganda xususiyatni avtomatik ravishda formatlaydi:
// Property decorator to format a string property
function format(formatString: string) {
return function (target: any, propertyKey: string) {
let value: string;
const getter = () => value;
const setter = (newVal: string) => {
value = formatString.replace('{}', newVal);
};
Object.defineProperty(target, propertyKey, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
};
}
class Greeter {
@format('Hello, {}!')
greeting: string;
}
const greeter = new Greeter();
greeter.greeting = 'World';
console.log(greeter.greeting); // Outputs: Hello, World!
O‘zingiz sinab ko‘ring »
Jurnal xususiyat dekoratori
Ushbu dekorator mulkka kirish va o‘zgarishlarni qayd qiladi:
function logProperty(target: any, propertyKey: string) {
let value: any;
const getter = function() {
console.log(`Getting ${propertyKey}: ${value}`);
return value;
};
const setter = function(newVal: any) {
console.log(`Setting ${propertyKey} from ${value} to ${newVal}`);
value = newVal;
};
Object.defineProperty(target, propertyKey, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
}
class Product {
@logProperty
name: string;
@logProperty
price: number;
constructor(name: string, price: number) {
this.name = name;
this.price = price;
}
}
const product = new Product('Laptop', 999.99);
product.price = 899.99; // Logs: Setting price from 999.99 to 899.99
console.log(product.name); // Logs: Getting name: Laptop
O‘zingiz sinab ko‘ring »
Kerakli xususiyat dekoratori
Ushbu dekorator xususiyat o‘rnatilishi kerakligini ta’minlaydi:
function required(target: any, propertyKey: string) {
let value: any;
const getter = function() {
if (value === undefined) {
throw new Error(`Property ${propertyKey} is required`);
}
return value;
};
const setter = function(newVal: any) {
value = newVal;
};
Object.defineProperty(target, propertyKey, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
}
class User {
@required
username: string;
@required
email: string;
age?: number;
constructor(username: string, email: string) {
this.username = username;
this.email = email;
}
}
const user1 = new User('johndoe', 'john@example.com'); // Works
// const user2 = new User(undefined, 'test@example.com'); // Throws error: Property username is required
// const user2 = new User('johndoe', undefined); // Throws error: Property email is required
O‘zingiz sinab ko‘ring »
Xususiyat dekorativlari haqida asosiy fikrlar
- Xususiyat dekoratorlari mulkka kirishda emas, balki aniqlanganda chaqiriladi
- Ular uslub dekorativlari kabi xususiyat tavsifini olmaydilar
- Xususiyat xatti-harakatlarini o‘zgartirish uchun siz
Object.definePropertydan foydalanishingiz kerak - Ular ko‘pincha metama’lumotlarni aks ettirish yoki mulkka kirishni o‘zgartirish uchun ishlatiladi
- Ular murakkabroq xatti-harakatlar uchun boshqa dekorativlar bilan birlashtirilishi mumkin
Parametr dekorativlari
Parametr dekoratorlari konstruktor yoki usulda parametr e’lonlariga qo‘llaniladi.
Ular uchta parametrni oladi:
target: Class prototipi (masalan, usullar) yoki konstruktor funksiyasi (statik usullar uchun)propertyKey: Usul nomi (yoki konstruktor parametrlari uchun aniqlanmagan)parameterIndex: Funksiya parametrlari ro‘yxatidagi parametrning tartib indeksi
Parametrlarni tekshirish dekoratori
Ushbu dekorator usul parametrlarini tasdiqlaydi:
function validateParam(type: 'string' | 'number' | 'boolean') {
return function (target: any, propertyKey: string | symbol, parameterIndex: number) {
const existingValidations: any[] = Reflect.getOwnMetadata('validations', target, propertyKey) || [];
existingValidations.push({ index: parameterIndex, type });
Reflect.defineMetadata('validations', existingValidations, target, propertyKey);
};
}
function validate(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
const validations: Array<{index: number, type: string}> =
Reflect.getOwnMetadata('validations', target, propertyKey) || [];
for (const validation of validations) {
const { index, type } = validation;
const param = args[index];
let isValid = false;
switch (type) {
case 'string':
isValid = typeof param === 'string' && param.length > 0;
break;
case 'number':
isValid = typeof param === 'number' && !isNaN(param);
break;
case 'boolean':
isValid = typeof param === 'boolean';
}
if (!isValid) {
throw new Error(`Parameter at index ${index} failed ${type} validation`);
}
}
return originalMethod.apply(this, args);
};
return descriptor;
}
class UserService {
@validate
createUser(
@validateParam('string') name: string,
@validateParam('number') age: number,
@validateParam('boolean') isActive: boolean
) {
console.log(`Creating user: ${name}, ${age}, ${isActive}`);
}
}
const service = new UserService();
service.createUser('John', 30, true); // Works
// service.createUser('', 30, true); // Throws error: Parameter at index 0 failed string validation
// service.createUser('John', 'thirty', true); // Throws error: Parameter at index 1 failed number validation
O‘zingiz sinab ko‘ring »
Parametr dekorativlari haqida asosiy fikrlar
- Parametr dekoratorlari chaqirilganda emas, balki usul aniqlanganda chaqiriladi
- Ular ko‘pincha kesishgan tashvishlarni amalga oshirish uchun uslublar dekoratorlari bilan ishlatiladi
- Ulardan
reflect-metadatakutubxonasi bilan metama’lumotlarni saqlash va olish uchun foydalanish mumkin - Ular odatda qaramlik in’ektsiya ramkalarida qo‘llaniladi
- Ular parametr indeksini oladilar, undan ish vaqtida parametr qiymatiga kirish uchun foydalanish mumkin
Dekorativ zavodlar
Dekorator zavodlari dekorativ funksiyani qaytaruvchi funksiyalardir.
Ular sizga parametrlarni o‘tkazish orqali dekorativlarni sozlash imkonini beradi.
Bu sizning dekorativlaringizni yanada moslashuvchan va qayta foydalanishga imkon beradi.
Sozlanishi mumkin bo‘lgan logging dekoratori
Ushbu misol turli jurnal darajalari bilan moslashtirilishi mumkin bo‘lgan sozlanishi mumkin bo‘lgan logging dekoratorini qanday yaratishni ko‘rsatadi:
// Decorator factory that accepts configuration
function logWithConfig(config: { level: 'log' | 'warn' | 'error', message?: string }) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
const { level = 'log', message = 'Executing method' } = config;
console[level](`${message}: ${propertyKey}`, { arguments: args });
const result = originalMethod.apply(this, args);
console[level](`${propertyKey} completed`);
return result;
};
return descriptor;
};
}
class PaymentService {
@logWithConfig({ level: 'log', message: 'Processing payment' })
processPayment(amount: number) {
console.log(`Processing payment of $${amount}`);
}
}
O‘zingiz sinab ko‘ring »
Baholash tartibi
Ushbu misolda bir nechta dekorativlar uchun baholash tartibi ko‘rsatilgan:
Agar deklaratsiyaga bir nechta dekoratorlar qo‘llanilsa, ular quyidagi tartibda baholanadi:
- Har bir misol a’zosi uchun Parametr dekoratorlari, so‘ngra Metod, Aksessuar yoki Xususiyat dekoratorlari qo‘llaniladi.
- Har bir statik element uchun Parametr dekoratorlari, undan keyin Metod, Aksessuar yoki Xususiyat dekoratorlari qo‘llaniladi.
- Konstruktor uchun Parametr Dekoratorlari qo‘llaniladi.
- Class uchun dekoratorlar qo‘llaniladi.
function first() {
console.log('first(): factory evaluated');
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
console.log('first(): called');
};
}
function second() {
console.log('second(): factory evaluated');
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
console.log('second(): called');
};
}
class ExampleClass {
@first()
@second()
method() {}
}
// Output:
// second(): factory evaluated
// first(): factory evaluated
// first(): called
// second(): called
O‘zingiz sinab ko‘ring »
Haqiqiy dunyo misollari
Dekoratorlar bilan API tekshiruvi
Bu misol NestJS yoki Express-dagi kabi oddiy API kontroller yaratish uchun dekorativlardan qanday foydalanish mumkinligini ko‘rsatadi:
// Simple decorator implementations (simplified for example)
const ROUTES: any[] = [];
function Controller(prefix: string = '') {
return function (constructor: Function) {
constructor.prototype.prefix = prefix;
};
}
function Get(path: string = '') {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
ROUTES.push({
method: 'get',
path,
handler: descriptor.value,
target: target.constructor
});
};
}
// Using the decorators
@Controller('/users')
class UserController {
@Get('/')
getAllUsers() {
return { users: [{ id: 1, name: 'John' }] };
}
@Get('/:id')
getUserById(id: string) {
return { id, name: 'John' };
}
}
// Simulate route registration
function registerRoutes() {
ROUTES.forEach(route => {
const prefix = route.target.prototype.prefix || '';
console.log(`Registered ${route.method.toUpperCase()} ${prefix}${route.path}`);
});
}
registerRoutes();
// Output:
// Registered GET /users
// Registered GET /users/:id
O‘zingiz sinab ko‘ring »
Eng yaxshi amaliyotlar
Dekoratorning eng yaxshi amaliyotlari
Dekoratorlar bilan ishlashda quyidagi eng yaxshi amaliyotlarga amal qiling:
- Keep decorators focused : Har bir dekorator bitta mas’uliyatga ega bo‘lishi kerak.
- Document behavior : Dekoratorlaringiz nima qilayotganini va ularda yuzaga kelishi mumkin bo‘lgan har qanday nojo‘ya ta’sirlarni aniq hujjatlashtiring.
- Use decorator factories : Yaxshiroq qayta foydalanish uchun dekorativlarni zavodlar yordamida sozlanishi mumkin.
- Consider performance : Ishlash vaqtiga qo‘shimcha xarajatlar qo‘shadigan dekoratorlar bilan ishlashga ta’sir qilishiga e’tibor bering.
- Type safety : Iloji bo‘lsa, dekorativlar turini xavfsiz qilish uchun TypeScript turi tizimidan foydalaning.
- Error handling : Dekoratorlarda xatoliklarni to‘g‘ri hal qilish.
- Testing : Dekoratorlaringiz kutilganidek ishlashiga ishonch hosil qilish uchun birlik testlarini yozing.
- Metadata : Ish vaqti turi ma’lumotlarini talab qiladigan kengaytirilgan stsenariylar uchun
reflect-metadatadan foydalaning.
Umumiy tuzoqlar
Oddiy dekorativ tuzoqlar
Dekoratorlar bilan ishlashda quyidagi keng tarqalgan xatolardan qoching:
- Forgetting to enable decorators :
tsconfig.jsondaexperimentalDecoratorsyoqilganligiga ishonch hosil qiling. - Incorrect decorator signature : Har bir dekorator turi o‘ziga xos imzoga ega. Noto‘g‘ri parametrlardan foydalanish ish vaqti xatolariga olib kelishi mumkin.
- Decorator evaluation order : Dekoratorlar har bir deklaratsiya uchun pastdan yuqoriga qarab baholanadi.
- Property initialization : Xususiyat dekoratorlari namuna xususiyatlari ishga tushirilgunga qadar ishlaydi.
- Metadata reflection : Dekorator metama’lumotlaridan foydalanishda
reflect-metadataimport qilishni unutish. - Performance overhead : Ishlash uchun muhim kodda ishlash vaqtiga katta yuk qo‘shadigan dekorativlar bilan ehtiyot bo‘ling.
- Browser compatibility : Dekoratorlar 3-bosqich taklifidir va eski brauzerlar uchun transpilyatsiyani talab qilishi mumkin.
Misol: xususiyat dekoratori
function readonly(target: any, propertyKey: string) {
Object.defineProperty(target, propertyKey, {
writable: false
});
}
class Person {
@readonly
name = "John";
}
Misol: Parametr dekoratori
function logParameter(target: any, propertyKey: string, parameterIndex: number) {
console.log(`Parameter in ${propertyKey} at index ${parameterIndex}`);
}
class Demo {
greet(@logParameter message: string) {
return message;
}
}
Buni tsconfig.json ga qo‘shish orqali dekorativlarni yoqing:
Misol
{
"compilerOptions": {
"experimentalDecorators": true
}
}
Dekoratorlar qayerda ishlatiladi?
- Angular: Komponentlar, xizmatlar, modullar va boshqalar uchun.
- NestJS: Kontrollerlar, provayderlar, marshrutlar va boshqalar uchun.
W3Schools Pathfinder
Yutuqlaringizni kuzating – bu bepul!
