مقدمة: لماذا يتطلب الكود القديم (Legacy) في TypeScript نهجًا خاصًا
العمل مع الكود الموروث (Legacy) هو حقيقة لا مفر منها لأي مطور، خاصة في المشاريع الكبيرة. غالبًا ما يتم كتابة هذا الكود دون كتابة أنواع صارمة (Strict Typing)، مع استخدام any في كل زاوية، ويمثل كتلة متشابكة من التبعيات. إعادة الهيكلة البسيطة لا تنجح هنا - بل هناك حاجة إلى نهج منهجي ومتقدم.
في هذه المقالة، سننظر في استراتيجيات إعادة هيكلة الكود القديم (Legacy) إلى TypeScript التي تتجاوز الأدلة الأساسية. ستتعلم كيفية إدخال كتابة الأنواع الصارمة بأمان، وعزل التغييرات باستخدام الأنماط (Patterns)، واستخدام إمكانيات المترجم (Compiler) لأتمتة المهام الروتينية.
الاستراتيجية 1: الإدخال التدريجي لكتابة الأنواع الصارمة عبر "جسور الأنواع"
إحدى المشكلات الرئيسية في الكود القديم (Legacy) هي الاستخدام الشامل لـ any. محاولة استبدال جميع any بأنواع محددة دفعة واحدة ستؤدي إلى مئات الأخطاء وشلل التطوير. بدلاً من ذلك، نستخدم تقنية "جسور الأنواع".
جوهر الطريقة: نحن لا نعيد كتابة الوحدة بأكملها مرة واحدة، بل ننشئ أنواعًا وسيطة (جسورًا) تحل محل any تدريجيًا.
مثال: كتابة أنواع لوحدة "رمادية"
لنفترض أن لدينا وحدة قديمة للعمل مع المستخدمين:
// legacyModule.tsexport function processUser(data: any): any { // ... الكثير من الكود غير المقروء return { id: data.id, name: data.name, age: data.age };}بدلاً من كتابة أنواع للدالة بأكملها فورًا، نقوم بإنشاء UserData و User:
// types.tsexport interface UserData { id: number; name: string; age?: number; // الحقل قد يكون غائبًا}
export interface User { id: number; fullName: string; isAdult: boolean;}الآن نستخدم هذه الأنواع، لكننا نترك التوقيع القديم للتوافق العكسي:
// legacyModule.ts (إعادة هيكلة الخطوة 1)import { UserData, User } from './types';
export function processUser(data: UserData): User { const isAdult = data.age ? data.age >= 18 : false; return { id: data.id, fullName: data.name, isAdult, };}
// التوقيع القديم للتوافقexport function processUserLegacy(data: any): any { return processUser(data as UserData);}بعد أن ينتقل جميع المستهلكين إلى الدالة الجديدة، نقوم بحذف processUserLegacy. هذا النهج يقلل من خطر كسر التبعيات.
الاستراتيجية 2: عزل التبعيات "القذرة" باستخدام نمط "المهايئ" (Adapter)
غالبًا ما يكون الكود القديم (Legacy) مرتبطًا بشدة بالمكتبات الخارجية، أو الحالات العامة (Global States)، أو واجهات API القديمة. لإجراء إعادة الهيكلة بأمان، نحتاج إلى عزل هذه التبعيات. أفضل أداة لذلك هي نمط "المهايئ" (Adapter) مع الواجهات (Interfaces).
مثال: عزل واجهة API قديمة
// oldApi.ts (قديم)export function fetchOldData(callback: (data: any) => void): void { // API قديم مع دوال الاستدعاء (Callbacks) setTimeout(() => callback({ value: 42 }), 100);}نقوم بإنشاء واجهة للنهج الجديد (قائم على Promise) ومهايئ:
// apiAdapter.tsimport { fetchOldData } from './oldApi';
export interface IDataFetcher { fetchData(): Promise<{ value: number }>;}
export class LegacyApiAdapter implements IDataFetcher { fetchData(): Promise<{ value: number }> { return new Promise((resolve, reject) => { fetchOldData((data) => { if (data) { resolve(data); } else { reject(new Error('No data')); } }); }); }}الآن جميع الكود الجديد يستخدم IDataFetcher. عندما تعيد كتابة API القديم بالكامل، ستحتاج فقط إلى استبدال تنفيذ المهايئ، دون لمس باقي الكود. هذا يقلل من خطر الانحدار (Regression) إلى الحد الأدنى.