العربية

اكتشف مزينات TypeScript: ميزة برمجة وصفية قوية لتعزيز بنية الكود وقابلية إعادة الاستخدام والصيانة. تعلم كيفية الاستفادة منها بفعالية مع أمثلة عملية.

مزينات TypeScript: إطلاق العنان لقوة البرمجة الوصفية

توفر مزينات TypeScript طريقة قوية وأنيقة لتعزيز الكود الخاص بك بقدرات البرمجة الوصفية. إنها توفر آلية لتعديل وتوسيع الفئات، والتوابع، والخصائص، والمعلمات في وقت التصميم، مما يسمح لك بحقن سلوكيات وتعليقات توضيحية دون تغيير المنطق الأساسي للكود. ستتعمق هذه المقالة في تعقيدات مزينات TypeScript، وتقدم دليلاً شاملاً للمطورين من جميع المستويات. سنستكشف ما هي المزينات، وكيف تعمل، والأنواع المختلفة المتاحة، وأمثلة عملية، وأفضل الممارسات لاستخدامها الفعال. سواء كنت جديدًا في TypeScript أو مطورًا ذا خبرة، سيزودك هذا الدليل بالمعرفة اللازمة للاستفادة من المزينات للحصول على كود أنظف وأكثر قابلية للصيانة وأكثر تعبيرًا.

ما هي مزينات TypeScript؟

في جوهرها، تعد مزينات TypeScript شكلاً من أشكال البرمجة الوصفية. إنها في الأساس دوال تأخذ وسيطًا واحدًا أو أكثر (عادةً الشيء الذي يتم تزيينه، مثل فئة أو تابع أو خاصية أو معلمة) ويمكنها تعديله أو إضافة وظائف جديدة. فكر فيها كتعليقات توضيحية أو سمات تلحقها بالكود الخاص بك. يمكن بعد ذلك استخدام هذه التعليقات التوضيحية لتوفير بيانات وصفية حول الكود، أو لتغيير سلوكه.

يتم تعريف المزينات باستخدام الرمز `@` متبوعًا باستدعاء دالة (على سبيل المثال، `@decoratorName()`). سيتم بعد ذلك تنفيذ دالة المزخرف أثناء مرحلة وقت التصميم لتطبيقك.

المزينات مستوحاة من ميزات مماثلة في لغات مثل Java و C# و Python. إنها توفر طريقة لفصل الاهتمامات وتعزيز قابلية إعادة استخدام الكود عن طريق الحفاظ على نظافة منطقك الأساسي وتركيز جوانب البيانات الوصفية أو التعديل في مكان مخصص.

كيف تعمل المزينات

يقوم مترجم TypeScript بتحويل المزينات إلى دوال يتم استدعاؤها في وقت التصميم. تعتمد الوسائط الدقيقة التي يتم تمريرها إلى دالة المزخرف على نوع المزخرف المستخدم (فئة، تابع، خاصية، أو معلمة). دعنا نحلل الأنواع المختلفة من المزينات ووسائط كل منها:

فهم توقيعات هذه الوسائط أمر بالغ الأهمية لكتابة مزينات فعالة.

أنواع المزينات

تدعم TypeScript عدة أنواع من المزينات، كل منها يخدم غرضًا محددًا:

أمثلة عملية

دعنا نستكشف بعض الأمثلة العملية لتوضيح كيفية استخدام المزينات في TypeScript.

مثال على مزين الفئة: إضافة طابع زمني

تخيل أنك تريد إضافة طابع زمني لكل كائن من فئة ما. يمكنك استخدام مزين فئة لتحقيق ذلك:


function addTimestamp<T extends { new(...args: any[]): {} }>(constructor: T) {
  return class extends constructor {
    timestamp = Date.now();
  };
}

@addTimestamp
class MyClass {
  constructor() {
    console.log('MyClass created');
  }
}

const instance = new MyClass();
console.log(instance.timestamp); // الإخراج: طابع زمني

في هذا المثال، يضيف مزين `addTimestamp` خاصية `timestamp` إلى كائن الفئة. يوفر هذا معلومات قيمة لتصحيح الأخطاء أو سجل التدقيق دون تعديل تعريف الفئة الأصلي مباشرة.

مثال على مزين التابع: تسجيل استدعاءات التوابع

يمكنك استخدام مزين تابع لتسجيل استدعاءات التوابع ووسائطها:


function logMethod(target: any, key: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;

  descriptor.value = function (...args: any[]) {
    console.log(`[LOG] Method ${key} called with arguments:`, args);
    const result = originalMethod.apply(this, args);
    console.log(`[LOG] Method ${key} returned:`, result);
    return result;
  };

  return descriptor;
}

class Greeter {
  @logMethod
  greet(message: string): string {
    return `Hello, ${message}!`;
  }
}

const greeter = new Greeter();
greeter.greet('World');
// الإخراج:
// [LOG] Method greet called with arguments: [ 'World' ]
// [LOG] Method greet returned: Hello, World!

يسجل هذا المثال كل مرة يتم فيها استدعاء التابع `greet`، بالإضافة إلى وسائطه وقيمته المرجعة. هذا مفيد جدًا لتصحيح الأخطاء والمراقبة في التطبيقات الأكثر تعقيدًا.

مثال على مزين الخاصية: إضافة التحقق من الصحة

إليك مثال على مزين خاصية يضيف تحققًا أساسيًا من الصحة:


function validate(target: any, key: string) {
  let value: any;

  const getter = function () {
    return value;
  };

  const setter = function (newValue: any) {
    if (typeof newValue !== 'number') {
      console.warn(`[WARN] Invalid property value: ${key}. Expected a number.`);
      return;
    }
    value = newValue;
  };

  Object.defineProperty(target, key, {
    get: getter,
    set: setter,
    enumerable: true,
    configurable: true,
  });
}

class Person {
  @validate
  age: number; // <- خاصية مع التحقق من الصحة
}

const person = new Person();
person.age = 'abc'; // يسجل تحذيرًا
person.age = 30;   // يضبط القيمة
console.log(person.age); // الإخراج: 30

في مزين `validate` هذا، نتحقق مما إذا كانت القيمة المعينة رقمًا. إذا لم تكن كذلك، فإننا نسجل تحذيرًا. هذا مثال بسيط ولكنه يوضح كيف يمكن استخدام المزينات لفرض سلامة البيانات.

مثال على مزين المعلمة: حقن التبعية (مبسط)

بينما تستخدم أطر عمل حقن التبعية الكاملة غالبًا آليات أكثر تعقيدًا، يمكن أيضًا استخدام المزينات لوضع علامة على المعلمات للحقن. هذا المثال هو توضيح مبسط:


// هذا تبسيط ولا يعالج الحقن الفعلي. حقن التبعية الحقيقي أكثر تعقيدًا.
function Inject(service: any) {
  return function (target: any, propertyKey: string | symbol, parameterIndex: number) {
    // قم بتخزين الخدمة في مكان ما (على سبيل المثال، في خاصية ثابتة أو خريطة)
    if (!target.injectedServices) {
      target.injectedServices = {};
    }
    target.injectedServices[parameterIndex] = service;
  };
}

class MyService {
  doSomething() { /* ... */ }
}

class MyComponent {
  constructor(@Inject(MyService) private myService: MyService) {
    // في نظام حقيقي، ستقوم حاوية حقن التبعية بحل 'myService' هنا.
    console.log('MyComponent constructed with:', myService.constructor.name); //مثال
  }
}

const component = new MyComponent(new MyService());  // حقن الخدمة (مبسط).

يميز مزين `Inject` معلمة على أنها تتطلب خدمة. يوضح هذا المثال كيف يمكن للمزين تحديد المعلمات التي تتطلب حقن التبعية (ولكن إطار العمل الحقيقي يحتاج إلى إدارة حل الخدمة).

فوائد استخدام المزينات

أفضل الممارسات لاستخدام المزينات

مفاهيم متقدمة

مصانع المزينات (Decorator Factories)

مصانع المزينات هي دوال تُرجع دوال المزينات. هذا يسمح لك بتمرير وسائط إلى مزيناتك، مما يجعلها أكثر مرونة وقابلية للتكوين. على سبيل المثال، يمكنك إنشاء مصنع مزين للتحقق من الصحة يسمح لك بتحديد قواعد التحقق:


function validate(minLength: number) {
  return function (target: any, key: string) {
    let value: string;

    const getter = function () {
      return value;
    };

    const setter = function (newValue: string) {
      if (typeof newValue !== 'string') {
        console.warn(`[WARN] Invalid property value: ${key}. Expected a string.`);
        return;
      }
      if (newValue.length < minLength) {
        console.warn(`[WARN] ${key} must be at least ${minLength} characters long.`);
        return;
      }
      value = newValue;
    };

    Object.defineProperty(target, key, {
      get: getter,
      set: setter,
      enumerable: true,
      configurable: true,
    });
  };
}

class Person {
  @validate(3) // تحقق من الصحة بطول أدنى 3
  name: string;
}

const person = new Person();
person.name = 'Jo';
console.log(person.name); // يسجل تحذيرًا، ويضبط القيمة.
person.name = 'John';
console.log(person.name); // الإخراج: John

تجعل مصانع المزينات المزينات أكثر قابلية للتكيف.

تركيب المزينات

يمكنك تطبيق مزينات متعددة على نفس العنصر. يمكن أن يكون الترتيب الذي يتم تطبيقها به مهمًا في بعض الأحيان. الترتيب من الأسفل إلى الأعلى (كما هو مكتوب). على سبيل المثال:


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() {}
}

// الإخراج:
// second(): factory evaluated
// first(): factory evaluated
// second(): called
// first(): called

لاحظ أنه يتم تقييم دوال المصنع بالترتيب الذي تظهر به، ولكن يتم استدعاء دوال المزينات بالترتيب العكسي. افهم هذا الترتيب إذا كانت مزيناتك تعتمد على بعضها البعض.

المزينات وانعكاس البيانات الوصفية (Metadata Reflection)

يمكن للمزينات أن تعمل جنبًا إلى جنب مع انعكاس البيانات الوصفية (على سبيل المثال، باستخدام مكتبات مثل `reflect-metadata`) لاكتساب سلوك أكثر ديناميكية. يتيح لك هذا، على سبيل المثال، تخزين واسترداد المعلومات حول العناصر المزينة أثناء وقت التشغيل. هذا مفيد بشكل خاص في أطر العمل وأنظمة حقن التبعية. يمكن للمزينات إضافة تعليقات توضيحية للفئات أو التوابع ببيانات وصفية، ومن ثم يمكن استخدام الانعكاس لاكتشاف واستخدام تلك البيانات الوصفية.

المزينات في أطر العمل والمكتبات الشائعة

أصبحت المزينات أجزاء لا يتجزأ من العديد من أطر العمل والمكتبات الحديثة لـ JavaScript. تساعدك معرفة تطبيقاتها على فهم بنية إطار العمل وكيفية تبسيطه للمهام المختلفة.

توضح هذه الأطر والمكتبات كيف تعزز المزينات تنظيم الكود، وتبسط المهام الشائعة، وتعزز قابلية الصيانة في التطبيقات الواقعية.

التحديات والاعتبارات

الخاتمة

تعد مزينات TypeScript ميزة برمجة وصفية قوية يمكنها تحسين بنية الكود الخاص بك وقابليته لإعادة الاستخدام والصيانة بشكل كبير. من خلال فهم الأنواع المختلفة من المزينات، وكيفية عملها، وأفضل الممارسات لاستخدامها، يمكنك الاستفادة منها لإنشاء تطبيقات أنظف وأكثر تعبيرًا وكفاءة. سواء كنت تبني تطبيقًا بسيطًا أو نظامًا معقدًا على مستوى الشركات، توفر المزينات أداة قيمة لتعزيز سير عمل التطوير الخاص بك. يتيح تبني المزينات تحسينًا كبيرًا في جودة الكود. من خلال فهم كيفية تكامل المزينات داخل أطر العمل الشائعة مثل Angular و NestJS، يمكن للمطورين الاستفادة من إمكاناتهم الكاملة لبناء تطبيقات قابلة للتطوير والصيانة وقوية. المفتاح هو فهم الغرض منها وكيفية تطبيقها في السياقات المناسبة، مع التأكد من أن الفوائد تفوق أي عيوب محتملة.

من خلال تنفيذ المزينات بفعالية، يمكنك تعزيز الكود الخاص بك بهيكل أكبر، وقابلية للصيانة، وكفاءة. يقدم هذا الدليل نظرة عامة شاملة حول كيفية استخدام مزينات TypeScript. بهذه المعرفة، أنت مؤهل لإنشاء كود TypeScript أفضل وأكثر قابلية للصيانة. انطلق وزيّن أكوادك!

مزينات TypeScript: إطلاق العنان لقوة البرمجة الوصفية | MLOG