Menu
flag Ar iconالعربيةdown icon

دالة البناء (Constructor) في TypeScript: خصائص المعاملات والتحميل

كيف تحدد أنواع دالة البناء لصنف في TypeScript: معاملات بأنواع ومعاملات اختيارية، وخصائص المعاملات مثل constructor(private name: string)، وترتيب تهيئة الحقول، وتحميل دالة البناء، واستدعاء super في الأصناف الفرعية، ودوال البناء الخاصة، وأنواع دوال البناء مع new.

تحتوي هذه الصفحة على محررات قابلة للتشغيل - حرّر، شغّل، وشاهد النتيجة فوراً.

دالة البناء في TypeScript هي التابع constructor في الصنف مع معاملات بأنواع محددة. ليس لها توصيف لنوع القيمة المُعادة: فهي تنتج دائمًا نسخة من الصنف.

كتابة constructor(): Product هي الخطأ TS1093 (Type annotation cannot appear on a constructor declaration). وكل ما عدا ذلك في طريقة عمل new هو JavaScript عادية.

خصائص المعاملات

النمط السابق (تعريف حقل، واستقبال معامل، ونسخه إلى الحقل) شائع جدًا حتى إن لـ TypeScript اختصارًا له. ضع public أو private أو protected أو readonly قبل معامل دالة البناء فيصبح حقلًا:

هذه من الميزات القليلة في TypeScript التي تولّد كودًا. يكتب المترجم عمليات الإسناد نيابة عنك:

class Product {
    name;
    price;
    sku;
    constructor(name, price, sku) {
        this.name = name;
        this.price = price;
        this.sku = sku;
    }
    // ...
}

ولأنها ليست مجرد أنواع تُحذف، لا تعمل خصائص المعاملات مع إزالة الأنواع المدمجة في Node (يفشل node file.ts بالخطأ ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX)، ويبلّغ عنها خيار المترجم erasableSyntaxOnly بالخطأ TS1294. المشاريع التي تشغّل ملفات .ts مباشرة تكتب الحقول يدويًا.

المعاملات الاختيارية والافتراضية

تتبع معاملات دالة البناء قواعد معاملات الدوال نفسها: ? تجعل المعامل اختياريًا، والقيمة الافتراضية تجعله اختياريًا وتملؤه، والمعاملات المطلوبة تأتي أولًا.

يمكن أن يكون لخصائص المعاملات قيم افتراضية أيضًا. نوع color داخل الصنف هو string | undefined، فيجب على الكود الذي يستخدمه أن يفحص undefined أولًا.

لدوال البناء ذات الخيارات الكثيرة، كائن خيارات واحد أسهل قراءة من قائمة موضعية طويلة: constructor(opts: { prefix: string; level?: "info" | "debug" }).

ترتيب تهيئة الحقول

تعمل القيم الابتدائية للحقول قبل جسم دالة البناء، وتُسند خصائص المعاملات في بداية جسم دالة البناء. لذلك لا تستطيع القيمة الابتدائية لحقل أن تقرأ خاصية معامل. وتكتشف TypeScript ذلك:

index.ts(3,18): error TS2729: Property 'size' is used before its initialization.
index.ts(3,30): error TS2729: Property 'size' is used before its initialization.

وقت التشغيل كانت القيمة الابتدائية سترى undefined وتنتج NaN. احسب القيمة في جسم دالة البناء بدلًا من ذلك:

class Grid {
    cells: number;

    constructor(public size: number) {
        this.cells = size * size;
    }
}

console.log(new Grid(3).cells); // 9

تحميل دالة البناء

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

يصعب قراءة التحميل بعد شكلين أو ثلاثة. توابع المصنع الثابتة ذات الأسماء الواضحة (Color.fromHex("#ff8800")، Color.fromRgb(10, 20, 30)) كثيرًا ما تكون أوضح ولا تحتاج إلى تضييق الأنواع داخل جسم واحد.

استدعاء super في صنف فرعي

الصنف المشتق الذي يعرّف دالة بناء خاصة به يجب أن يستدعي super(...) بوسائط الصنف الأب، وأن يفعل ذلك قبل استخدام this. نسيان الاستدعاء هو الخطأ TS2377 (Constructors for derived classes must contain a 'super' call)، واستخدام this أولًا هو TS17009 ('super' must be called before accessing 'this' in the constructor of a derived class). وكلاهما يعكس قواعد JavaScript وقت التشغيل.

class Animal {
    constructor(public name: string) {}
}

class Dog extends Animal {
    tricks: string[];

    constructor(name: string, ...tricks: string[]) {
        super(name); // must come first
        this.tricks = tricks;
    }
}

const rex = new Dog("Rex", "sit", "roll");
console.log(rex); // Dog { name: 'Rex', tricks: [ 'sit', 'roll' ] }

الصنف الفرعي الذي لا يضيف دالة بناء يرث دالة الأب بأنواع المعاملات نفسها. المزيد عن الأصناف الفرعية في الوراثة.

دوال البناء الخاصة والتهيئة غير المتزامنة

لا يمكن استدعاء دالة بناء private إلا من داخل الصنف. أما من خارجه فـ new هو الخطأ TS2673. هكذا تُجبر المستدعين على المرور عبر مصنع، لنمط singleton أو لتهيئة يجب أن تكون غير متزامنة (دوال البناء لا يمكن أن تكون async).

private هنا قاعدة وقت الترجمة فقط. يحتوي JavaScript الناتج على دالة بناء عادية، فيستطيع كود JavaScript عادي أن يستدعي new Connection(...).

أنواع دوال البناء

لقبول صنف كقيمة، حدد نوع المعامل بتوقيع إنشاء: new (...args) => Instance. يعمل typeof MyClass أيضًا، لكنه يربط المعامل بتوقيع دالة البناء والأعضاء الثابتة لذلك الصنف وحده.

يستخرج ConstructorParameters وInstanceType الـ tuple الخاص بالمعاملات ونوع النسخة من نوع دالة بناء؛ وهما مشروحان مع بقية أدوات الدوال في ReturnType وParameters.

الأسئلة الشائعة

ما هي خصائص المعاملات (parameter properties) في TypeScript؟

معامل دالة بناء يحمل معدّلًا (public أو private أو protected أو readonly) يعرّف حقلًا ويسنده في خطوة واحدة. الكتابة constructor(private name: string) {} اختصار لتعريف private name: string وكتابة this.name = name. ويولّد المترجم هذا الإسناد في الناتج.

هل يمكن أن يكون لصنف في TypeScript أكثر من دالة بناء؟

ليس أكثر من تنفيذ واحد. للصنف دالة بناء واحدة، لكن يمكنك كتابة عدة توقيعات تحميل (overload) قبلها فيرى المستدعون قوائم معاملات مختلفة. وكثيرًا ما تكون توابع المصنع الثابتة مثل Color.fromHex() وColor.fromRgb() أوضح من التحميل.

هل يمكن أن تكون دالة البناء async في TypeScript؟

لا. تعيد دالة البناء دائمًا النسخة الجديدة، ولا تعيد promise أبدًا، ويُرفض async constructor() بالخطأ TS1089 ('async' modifier cannot appear on a constructor declaration). استخدم دالة بناء خاصة مع تابع ثابت async create() ينجز العمل غير المتزامن ثم يستدعي new.

كيف أحدد نوع دالة بناء صنف كمعامل؟

استخدم توقيع إنشاء (construct signature): new (name: string) => User، أو typeof User لذلك الصنف بالتحديد. ويبدو المصنع العام هكذا: function make<T>(ctor: new () => T): T { return new ctor(); }. ويعطي ConstructorParameters<typeof User> قائمة المعاملات على شكل tuple.

لماذا تظهر لي الرسالة "'super' must be called before accessing 'this'"؟

هذا هو الخطأ TS17009. في صنف يستخدم extends لصنف آخر، دالة البناء في الصنف الأب هي التي تنشئ الكائن، فلا يوجد this حتى يُنفَّذ super(...). انقل استدعاء super إلى ما قبل أي سطر يستخدم this.

Coddy programming languages illustration

تعلّم البرمجة مع Coddy

ابدأ الآن