TypeScript لتطوير الواجهة الخلفية

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



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



لم تثبت TypeScript نفسها جيدًا بين مطوري الواجهة الخلفية. ربما لأنه يُعرف بمجموعة من الملفات التعريفية لإضافة بعض الكتابة إلى JavaScript. ولكن لا يزال هناك الكثير من المنطق الذي يتطلب تمثيل عشرات الأسطر من Java ، ويمكن تمثيل ذلك في بضعة أسطر من TypeScript.

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



لقد استضفنا مستودعًا على Github مع تطبيق ويب Node / TypeScript مصمم خصيصًا ، جنبًا إلى جنب مع بعض التفسيرات الإضافية. يوجد أيضًا فرع متقدم مع مثال على هندسة البصل والمزيد من مفاهيم الكتابة غير التافهة.



تقديم TypeScript



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



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



إدارة التبعية سهلة مع مدير الحزم المرن والفعال في Node ، npm.



أساسيات



عند تحديد الفئات public، يتم دعم مُعدِّلات التحكم في الوصول ، protectedوهي privateمعروفة جيدًا لمعظم المطورين:



class Order {

    private status: OrderStatus;

    constructor(public readonly id: string, isSpecialOrder: boolean) {
        [...]
    }
}


يحتوي الفصل الآن على Orderسمتين: حقل خاص statusوحقل عام idللقراءة فقط. في TypeScript ، وسيطات المُنشئ باستخدام الكلمات الأساسية public، protectedأو privateتصبح سمات فئة تلقائيًا.



interface User {
    id?: string;
    name: string;
    t_registered: Date;
}

const user: User = { name: 'Bob', t_registered: new Date() };


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



يتم التعبير عن العوامل العامة في TypeScript بنفس الطريقة كما في Java:



class Repository<T extends StoredEntity> {
    findOneById(id: string): T {
        [...]
    }
}


نظام نوع قوي



في قلب نظام الكتابة القوي في TypeScript ، يوجد استدلال الكتابة. كما أنه يدعم الكتابة الثابتة. ومع ذلك ، تعد التعليقات التوضيحية من النوع الثابت اختيارية إذا كان من الممكن استنتاج نوع الإرجاع أو نوع المعلمة من السياق.



يسمح TypeScript أيضًا باستخدام الأنواع الموحدة والأنواع الجزئية وتقاطعات الكتابة ، مما يمنح اللغة مرونة كبيرة مع تجنب التعقيد غير الضروري. في TypeScript ، يمكنك أيضًا استخدام قيمة معينة كنوع ، وهو أمر مفيد للغاية في مجموعة متنوعة من المواقف.



التعداد ، نوع الاستدلال ، وأنواع الاتحاد



ضع في اعتبارك موقفًا شائعًا تحتاج فيه حالة الطلب إلى تمثيل من النوع الآمن (كتعداد) ، ولكن تمثيل السلسلة مطلوب أيضًا لتسلسل JSON. في Java ، سيكون هذا تعدادًا ، جنبًا إلى جنب مع المُنشئ والجالب لقيم السلسلة.



في المثال الأول ، تسمح لك تعدادات TypeScript بإضافة تمثيل سلسلة مباشرة. هذا يترك لنا تمثيل تعداد النوع الآمن الذي يقوم تلقائيًا بترتيب تمثيل السلسلة المرتبطة به.



enum Status {
    ORDER_RECEIVED = 'order_received',
    PAYMENT_RECEIVED = 'payment_received',
    DELIVERED = 'delivered',
}

interface Order {
    status: Status;
}

const order: Order = { status: Status.ORDER_RECEIVED };


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



صحيح ، اتضح أنه من خلال مشاركة استدلال الأنواع وأنواع الاتحاد مع بعضها البعض ، يمكن حل هذه المهمة بشكل أسهل:



interface Order {
    status: 'order_received' | 'payment_received' | 'delivered';
}

const orderA: Order = { status: 'order_received' }; // 
const orderB: Order = { status: 'new' }; //  


لن يقبل برنامج التحويل البرمجي TypeScript سوى السلسلة المقدمة كحالة طلب صالحة (لاحظ أن هذا سيتطلب التحقق من JSON الوارد).



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



Lambdas والحجج الوظيفية



نظرًا لأن TypeScript هي لغة برمجة وظيفية ، فهي تدعم الوظائف المجهولة ، والتي تسمى أيضًا lambdas ، في جوهرها.



const evenNumbers = [ 1, 2, 3, 4, 5, 6 ].filter(i => i % 2 == 0);


المثال أعلاه .filter()يأخذ وظيفة من النوع (a: T) => boolean. يتم تمثيل هذه الوظيفة بواسطة لامدا مجهول i => i % 2 == 0. على عكس Java ، حيث يجب أن يكون للمعلمات الوظيفية نوع واضح وواجهة وظيفية ، يمكن أيضًا تمثيل نوع lambda بشكل مجهول:



class OrderService {
    constructor(callback: (order: Order) => void) {
        [...]
    }
}


البرمجة غير المتزامنة



نظرًا لأن TypeScript ، مع جميع المحاذير ، هي مجموعة شاملة من JavaScript ، فإن البرمجة غير المتزامنة هي مفهوم أساسي في هذه اللغة. نعم ، يمكنك استخدام lambdas و callbacks هنا ، يحتوي TypeScript على آليتين رئيسيتين لمساعدتك على تجنب جحيم رد الاتصال: وعود ونمط جميل async/await. الوعد هو في الأساس قيمة عائد فورية تعد بإرجاع قيمة محددة لاحقًا.



//  ,  
function fetchUserProfiles(url: string): Promise<UserProfile[]> {
    [...]
}

//     
function getActiveProfiles(): Promise<UserProfile[]> {
    return fetchUserProfiles(URL)
        .then(profiles => profiles.filter(profile => profile.active))
        .catch(error => handleError(error));
}


نظرًا لأنه .then()يمكن ربط التعليمات بأي رقم ، في بعض الحالات يمكن أن يؤدي النمط أعلاه إلى رمز مربك إلى حد ما. من خلال إعلان وظيفة asyncواستخدامها awaitأثناء انتظار الوعد بحل ، يمكنك كتابة نفس الرمز بأسلوب متزامن أكثر. أيضًا في هذه الحالة ، تفتح فرصة لاستخدام المشغلين المعروفين try/catch:



//  async/await ( ,  fetchUserProfiles  )
async function getActiveProfiles(): Promise<UserProfile[]> {
    const allProfiles = await fetchUserProfiles(URL);
    return allProfiles.filter(profile => profile.active);
}

//   try/catch
async function getActiveProfilesSafe(): Promise<UserProfile[]> {
    try {
        const allProfiles = await fetchUserProfiles(URL);
        return allProfiles.filter(profile => profile.active);
    } catch (error) {
        handleError(error);
        return [];
    }
}


لاحظ أنه على الرغم من أن الكود أعلاه يبدو متزامنًا ، إلا أنه مرئي فقط (حيث يتم إرجاع وعد آخر هنا).



عامل الامتداد وعامل الراحة: اجعل حياتك أسهل



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



في TypeScript ، يمكن التعامل مع كل هذا دون عناء باستخدام السكر النحوي الآمن من النوع الحلو: عوامل الانتشار والراحة.



أولاً ، دعنا نستخدم عامل توسيع المصفوفة ... لفك ضغط المصفوفة:



const a = [ 'a', 'b', 'c' ];
const b = [ 'd', 'e' ];

const result = [ ...a, ...b, 'f' ];
console.log(result);

// >> [ 'a', 'b', 'c', 'd', 'e', f' ]


هذا مناسب بالطبع ، لكن TypeScript الحقيقي يبدأ عندما تدرك أنه يمكنك فعل الشيء نفسه مع الكائنات:



interface UserProfile {
    userId: string;
    name: string;
    email: string;
    lastUpdated?: Date;
}

interface UserProfileUpdate {
    name?: string;
    email?: string;
}

const userProfile: UserProfile = { userId: 'abc', name: 'Bob', email: 'bob@example.com' };
const update: UserProfileUpdate = { email: 'bob@example.com' };

const updated: UserProfile = { ...userProfile, ...update, lastUpdated: new Date() };

console.log(updated);

// >> { userId: 'abc', name: 'Bob', email: 'bob@example.com', lastUpdated: 2019-12-19T16:09:45.174Z}


دعونا نرى ما يحدث هنا. بشكل أساسي ، updatedيتم إنشاء كائن باستخدام مُنشئ الدعامة المتعرجة. داخل هذا المُنشئ ، تقوم كل معلمة بالفعل بإنشاء كائن جديد ، بدءًا من اليسار.



لذلك ، يتم استخدام الكائن الموسع userProfile؛ أول شيء يفعله هو تقليد نفسه. في الخطوة الثانية ، يتم updateدمج الكائن الممتد فيه وإعادة تعيينه إلى الكائن الأول ؛ هذا ، مرة أخرى ، يخلق كائنًا جديدًا. في الخطوة الأخيرة ، يتم دمج الحقل وإعادة تعيينه lastUpdated، ثم يتم إنشاء كائن جديد ، ونتيجة لذلك ، يتم إنشاء الكائن النهائي.



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



يحتوي عامل الامتداد أيضًا على مكافئ مدمر يسمى كائن الراحة :



const userProfile: UserProfile = { userId: 'abc', name: 'Bob', email: 'bob@example.com' };
const { userId, ...details } = userProfile;
console.log(userId);
console.log(details);

// >> 'abc'
// >> { name: 'Bob', email: 'bob@example.com' }


الآن هو الوقت المناسب للجلوس وتخيل كل التعليمات البرمجية التي يجب عليك كتابتها في Java لأداء مثل هذه العمليات كما هو موضح أعلاه.



خاتمة. قليلا عن المزايا والعيوب



أداء



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



نوع الرقم



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



النظام البيئي



نظرًا لشعبية Node.js ، فلا ينبغي أن يكون مفاجئًا وجود مئات الآلاف من الحزم لها اليوم. ولكن نظرًا لأن Node أصغر من Java ، فإن العديد من الحزم لم تنجو مثل العديد من الإصدارات ، ومن الواضح أن جودة الشفرة في بعض المكتبات ضعيفة.



من بين أمور أخرى، ومن الجدير بالذكر بضعة المكتبات ذات الجودة العالية التي هي مريحة للغاية للعمل مع: على سبيل المثال، ل خوادم الويب ، حقن التبعية و الشروح تحكم . ولكن ، إذا كانت الخدمة ستعتمد بشكل خطير على العديد من برامج الجهات الخارجية المدعومة جيدًا ، فمن الأفضل استخدام Python أو Java أو Clojure.



تطوير الميزات المعجل



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



أخيرًا ، يعد TypeScript محنكًا جيدًا بالسكر النحوي ، لذا فإن التطوير باستخدامه أمر رائع وسريع.



All Articles