PROGRAMMING · TYPESCRIPT

TypeScript للمبتدئين: تعلّم الأنواع واكتب JavaScript بأخطاء أقل

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

تعلم TypeScript للمبتدئين وكتابة كود JavaScript مع أنواع بيانات واضحة
تساعد الأنواع الواضحة على فهم الكود واكتشاف أخطاء شائعة مبكرًا.

ما هي TypeScript؟ وهل تحتاج إلى تعلّمها؟

TypeScript هي لغة مبنية على JavaScript تضيف نظامًا للأنواع (Types). تكتب بها الكود ثم يحوّله المترجم إلى JavaScript يمكن تشغيله في المتصفح أو بيئة مثل Node.js. لا تستبدل TypeScript لغة JavaScript وقت التشغيل، ولا تحتاج المتصفحات عادةً إلى تشغيل ملفات TypeScript مباشرة.

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

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

1. تثبيت TypeScript وتشغيل أول مثال

تحتاج إلى Node.js وnpm. أنشئ مجلدًا للمشروع ثم نفّذ الأوامر التالية:

mkdir typescript-starter
cd typescript-starter
npm init -y
npm install --save-dev typescript
npx tsc --init

أنشئ ملفًا باسم src.ts واكتب فيه:

const greeting: string = "مرحبًا بك";
const visitors: number = 12;

function welcome(name: string): string {
  return `أهلًا ${name}`;
}

console.log(greeting);
console.log(welcome("سارة"));
console.log(visitors);

حوّل الملف إلى JavaScript بالأمر npx tsc src.ts. سيُنشأ ملف JavaScript بحسب إعدادات المترجم. في مشروع حقيقي، اضبط tsconfig.json ومجلدات المصدر والإخراج لتناسب بنية المشروع، بدل الاعتماد على الإعدادات الافتراضية دون مراجعة.

2. الأنواع الأساسية التي ستستخدمها كثيرًا

النصوص والأرقام والقيم المنطقية

let username: string = "Imad";
let age: number = 28;
let isPublished: boolean = false;

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

المصفوفات

const scores: number[] = [90, 85, 100];
const tags: Array<string> = ["seo", "web", "code"];

كلا الأسلوبين صالح. اختر الشكل الأكثر وضوحًا لفريقك، وكن متسقًا في المشروع.

الكائنات وخصائصها

const article: {
  title: string;
  published: boolean;
  views?: number;
} = {
  title: "دليل TypeScript",
  published: true
};

علامة الاستفهام بعد views تعني أن الخاصية اختيارية. إذا تكرر شكل الكائن في أكثر من موضع، فالأفضل غالبًا تعريف type أو interface باسمه بدل إعادة كتابة البنية.

3. متى تستخدم type ومتى تستخدم interface؟

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

interface User {
  id: number;
  name: string;
  email: string;
}

type ArticleStatus = "draft" | "published" | "archived";

type Article = {
  title: string;
  author: User;
  status: ArticleStatus;
};

const post: Article = {
  title: "مقدمة في TypeScript",
  author: { id: 1, name: "Imad", email: "imad@example.com" },
  status: "draft"
};

لاحظ أن ArticleStatus يسمح بثلاث قيم محددة فقط؛ كتابة حالة مثل "done" ستنتج خطأً وقت الفحص. أما البريد في المثال فهو قيمة توضيحية لا عنوانًا حقيقيًا.

4. فهم Union Types وNarrowing

أحيانًا يمكن أن تكون القيمة واحدة من أنواع متعددة. مثلًا، قد تقبل دالة رقمًا أو نصًا:

function formatId(id: number | string): string {
  if (typeof id === "number") {
    return id.toFixed(0);
  }

  return id.trim();
}

يسمى فحص النوع داخل الشرط Narrowing؛ إذ يضيّق الاحتمالات المتاحة داخل كل فرع. لا تستخدم قيمة من النوع الموحد كما لو كانت نوعًا واحدًا قبل التحقق من الحالة المناسبة.

من الأنماط المفيدة أيضًا جعل حالات الطلب صريحة:

type RequestState =
  | { status: "loading" }
  | { status: "success"; data: string[] }
  | { status: "error"; message: string };

function describeState(state: RequestState): string {
  switch (state.status) {
    case "loading":
      return "جارٍ التحميل";
    case "success":
      return `عدد النتائج: ${state.data.length}`;
    case "error":
      return state.message;
  }
}

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

5. الأنواع لا تتحقق من البيانات الخارجية وحدها

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

const user = await response.json() as User;

فإن as User يخبر المترجم أن يتعامل مع القيمة على أنها User، لكنه لا يثبت أن البيانات صحيحة بالفعل. تحقّق من البيانات الخارجية باستخدام فحوص واضحة أو مكتبة تحقق وقت التشغيل مثل Zod قبل الاعتماد عليها.

type ApiUser = {
  id: number;
  name: string;
};

function isApiUser(value: unknown): value is ApiUser {
  if (typeof value !== "object" || value === null) return false;

  const candidate = value as Record<string, unknown>;
  return typeof candidate.id === "number"
    && typeof candidate.name === "string";
}

في الأنظمة الأكبر، قد تكون مكتبة مخططات تحقق خيارًا أنسب لتوحيد رسائل الأخطاء والتحقق من الكائنات المتداخلة. اختر الحل بناءً على حجم البيانات وحساسية النظام.

6. إعدادات تساعدك على كتابة كود أكثر أمانًا

راجع ملف tsconfig.json وفعّل الخيارات الصارمة تدريجيًا إذا كان المشروع قديمًا. من الخيارات المهمة:

  • strict: يفعّل مجموعة من الفحوص الصارمة.
  • noUncheckedIndexedAccess: يساعد على إظهار احتمال أن تكون نتيجة الوصول إلى عنصر بمؤشر غير موجودة.
  • noEmitOnError: يمنع إصدار ملفات JavaScript عند وجود أخطاء ترجمة.

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

7. خطة بسيطة لتعلّم TypeScript خلال أسبوع

  1. اليوم الأول: راجع الأنواع الأساسية والمتغيرات والدوال.
  2. اليوم الثاني: طبّق الأنواع على المصفوفات والكائنات.
  3. اليوم الثالث: أنشئ أنواعًا باستخدام type وinterface.
  4. اليوم الرابع: تدرّب على Union Types وNarrowing.
  5. اليوم الخامس: عرّف أنواع استجابات API وتحقق من البيانات الخارجية.
  6. اليوم السادس: فعّل strict في مشروع صغير وأصلح الأخطاء.
  7. اليوم السابع: انقل وحدة صغيرة من JavaScript إلى TypeScript، ثم شغّل الاختبارات.

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

أخطاء شائعة عند تعلّم TypeScript

  • الإفراط في استخدام any: يوقف كثيرًا من فوائد الفحص. فضّل unknown عندما لا تعرف النوع، ثم افحص القيمة.
  • الاعتماد على type assertions: استخدام as لا يتحقق من صحة البيانات في وقت التشغيل.
  • تعقيد الأنواع مبكرًا: ابدأ بالأنواع البسيطة ثم زد التعقيد عندما تفرضه الحاجة.
  • اعتبار نجاح الترجمة ضمانًا للجودة: ما زلت تحتاج إلى اختبارات، ومراجعة منطق العمل، والتحقق من المدخلات والأمان.

الخلاصة

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

ما أكثر نوع من الأخطاء تتمنى أن يكتشفه المترجم في مشروعك؟ شارك تجربتك مع المطورين الآخرين.

الكاتب وصاحب الموقع: imad limrani

مقالات ذات صلة