الأنواع الخاصة (Any, Unknown, Void, Never) في TypeScript

مقدمة عن الأنواع الخاصة في TypeScript

في TypeScript، توجد أنواع متعددة تساهم في توسيع قدرات اللغة وتدعم أنواع البيانات الأساسية. هذه الأنواع تشمل: الأعداد (number)، السلاسل النصية (string)، القيم المنطقية (boolean)، إضافة إلى الأنواع الخاصة والمركبة التي تمنح اللغة قوتها وأمانها النوعي.

الأنواع الأساسية (Primitive Types) وأنواع القيم (Value Types)

في رحلتنا لاكتشاف قوة TypeScript، نصل الآن إلى اللبنات الأساسية التي تُبنى عليها جميع أنواع البيانات الأخرى: الأنواع الأساسية (Primitive Types). هذه الأنواع هي حجر الزاوية في أي لغة برمجة، وتوفر TypeScript لها دعماً قوياً وميزات تحقق الأمان النوعي (Type Safety) التي تجعل تطوير التطبيقات أكثر موثوقية وأقل عرضة للأخطاء.

الفرق الجوهري بين أنواع القيم وأنواع البيانات المجردة هو أن أنواع القيم تتعامل مع القيم الفعلية الملموسة التي يمكن ملاحظتها واستخدامها مباشرة داخل التعبيرات والمتغيرات والدوال.

أنواع القيم العددية (Number)

يعتبر النوع number في TypeScript تمثيلاً للأعداد الصحيحة والواقعية، ويشمل جميع الأرقام الصحيحة والعشرية على حد سواء دون تفريق بين (Integer) و (Float):

  • العدد الصحيح السالب: -100
  • العدد الصحيح الموجب: 100
  • العدد العشري: 3.14159

يدعم TypeScript أربعة أنظمة عددية رئيسية، بالإضافة إلى القيم العددية الخاصة مثل NaN و Infinity:

let decimalNumber: number = 42;
let binaryNumber: number = 0b101010;       // ثنائي يساوي 42
let hexNumber: number = 0x2A;              // سداسي عشري يساوي 42
let octalNumber: number = 0o52;            // ثماني يساوي 42
let floatNumber: number = 3.14159;
let negativeNumber: number = -100;

let notANumber: number = NaN;
let infinity: number = Infinity;
let negInfinity: number = -Infinity;

console.log(typeof notANumber);   // "number"
console.log(typeof infinity);     // "number"

يُوفِّر TypeScript أيضاً أنواعاً فرعية عددية مُدمجة مثل Int8Array، و Uint8Array، و Float64Array وغيرها، والتي تُستخدم عند الحاجة إلى تحكّم أدق في حجم الذاكرة ودقة القيم في التطبيقات ذات البيانات الضخمة.

أنواع القيم النصية (String)

يُمثَّل النص في TypeScript بالكلمة المفتاحية string، ويُستخدم لتخزين البيانات النصية الحرفية. يدعم TypeScript ثلاث طرق لإنشاء النصوص: النصوص بين علامتي تنصيص مفردتين، والنصوص بين علامتي تنصيص مزدوجتين، والنصوص القالبية (Template Literals) المحاطة بعلامة (Backtick):

let singleQuoteText: string = 'نص فردي';
let doubleQuoteText: string = "نص مزدوج";
let userName: string = "أحمد";
let templateLiteralText: string = `مرحباً، اسم المستخدم هو ${userName}`;

النوع المنطقي (boolean)

النوع boolean هو أبسط أنواع البيانات وأكثرها أساسية، حيث لا يمكن أن يحمل سوى قيمتين: true (صحيح) أو false (خطأ). يُستخدم هذا النوع بشكل واسع لتمثيل الحالات المنطقية، مثل ما إذا كان المستخدم مسجلاً للدخول، أو ما إذا كانت ميزة معينة مفعّلة في النظام:

const isEmpty: boolean = true;

if (isEmpty) {
    // يتم تنفيذ هذا الكود فقط إذا كانت isEmpty تساوي true
    console.log("القيمة فارغة بالفعل");
}

const isEmptyVoid: boolean = true;

الأنواع الخاصة (Special Types)

الأنواع غير المعروفة (Unknown Types) مقابل Any

في بعض الأحيان، قد لا نعرف نوع البيانات الدقيقة لقيمة معينة. في TypeScript، يمكننا استخدام النوع any لتجاوز فحص الأنواع، ولكن الأفضل استخدام النوع غير المعروف unknown كحل آمن للتعامل مع القيم التي لا تنتمي إلى أي نوع معروف مسبقاً.

تُعرف الأنواع غير المعروفة بأنها نوع خاص يمكن استخدامه لتمثيل القيم التي لا يمكن تحديد نوعها بسهولة، ويمكن تعريفها باستخدام الكلمة الأساسية type:

type NonKnownInteger = number;
type NonKnownString = string;

type NonKnownCustom = {
  value: unknown;
};

في المثال الأخير، تعرّفنا على نوع غير معروف مخصص يُدعى NonKnownCustom ويحتوي على خاصية value التي يمكن أن تحمل أي نوع بأمان.

أمثلة عملية على استخدام الأنواع غير المعروفة وطرق فحص النوع (Narrowing):

const unknownValue: unknown = 42;
console.log(unknownValue); // الناتج سيكون رقمًا (number)

function getValue(value: unknown): number | string {
  if (typeof value === 'number') {
    return value;
  } else if (typeof value === 'string') {
    return value.length;
  }
  return 'unknown';
}

function calculateUnknownValue(unknownValue: unknown): void {
  console.log(unknownValue);
}

calculateUnknownValue(10); // الناتج سيكون الرقم 10
calculateUnknownValue('مرحبا'); // الناتج سيكون النص "مرحبا"

class Person {
  name: string;
  age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  calculateAge(): number {
    return this.age * 2;
  }
}

const person1 = new Person('John Doe', 30);
const person2 = new Person('Jane Doe', 25);

const result1 = person1.calculateAge(); // الناتج سيكون العدد 60
const result2 = person2.calculateAge(); // الناتج سيكون العدد 50

النوع الفارغ (Void Type)

في TypeScript، يوجد مفهوم يسمى النوع الخاص void والذي يُستخدم للتعامل مع القيم الفارغة أو غير المستخدمة في الأكواد. يُعتبر النوع void نوعاً خاصاً يُستخدم للتعبير عن غياب القيمة العائدة في الدوال:

function helloVoid(): void {
  // الدالة لا تعيد قيمة
  console.log("هذه الدالة لا ترجع أي قيمة");
}

function greetUser(name: string): void {
  console.log(`مرحبا ${name}!`);
}

استخدام النوع الخاص Void مع الكائنات: لا يمكن استخدام void كنوع لخصائص الكائنات، بل نستخدم التحقق من وجود الحقول بواسطة undefined:

interface User {
  name: string;
  age: number;
}

let user: User = {
  name: "John Doe",
  age: 30
};

// التحقق من عدم وجود حقول فارغة أو غير مستخدمة
if (user.name == undefined || user.age == undefined) {
  console.log("يجب توفير قيم للحقول");
}

الأنواع التي لا تتحقق أبداً (Never Type)

يُعتبر never نوعاً خاصاً يُستخدم للتعامل مع الحالات الاستثنائية أو الأخطاء في الكود البرمجي، ولتحديد الحالات التي يستحيل الوصول إليها إطلاقاً (مثل الدوال التي ترمي أخطاء دائمة أو التي تحتوي على حلقات تكرار لا تنتهي أبداً):

  • تحديد الأنواع غير المسموح بها: يساعد في التحقق الصارم من الأنماط ومنع الأخطاء في وقت التشغيل.
  • مراجعة الأخطاء والحالات الاستثنائية: تمثيل مسارات الخطأ التي توقف تنفيذ البرنامج.
  • تحسين قراءة الكود: توضيح أن دالة معينة لا يمكن أن ترجع بأي نتيجة تحت أي ظرف.
function handleUnknown(value: unknown): never {
  if (value !== undefined) {
    throw new Error("حالة استثنائية: القيمة غير فارغة");
  }
  throw new Error("حدث خطأ غير متوقع");
}

الأنواع المركبة في TypeScript (Composite Types)

مفهوم السجل (Record Type)

في TypeScript، السجل (Record Type) هو نوع بيانات يسمح لك بتعريف قاموس من المعرفات والقيم المرتبطة بها:

interface UserInfo {
  name: string;
  age: number;
  email: string;
}

type RolesMap = Record<string, string>;

مفهوم الجدول الموجه (Map Type)

يُستخدم لتخزين قيم متصلة بمفاتيح محددة بطريقة منظمة وفعالة:

const pets = new Map([
  ['جون', 'cat'],
  ['سارة', 'dog'],
  ['أحمد', 'bird']
]);

مفهوم المجموعة الفريدة (Set Type)

يُستخدم لتخزين قيم فريدة فقط مع ضمان عدم تكرار العناصر:

const primes = new Set([1, 2, 3, 5, 7, 11, 13]);

مفهوم الصفوف الثابتة (Tuple) والمصفوفات (Array)

نوع الـ Tuple يتيح تخزين مجموعة من القيم من أنواع مختلفة بترتيب وطول محددين، بينما تمثل المصفوفة مجموعة من العناصر من نفس النوع:

// مثال على Tuple
const tuple: [number, number, number, number, number] = [2, 4, 6, 8, 10];

// مثال على Array نصوص وأرقام
const colors: string[] = ["red", "blue", "green", "yellow", "orange", "indigo"];
const numbers: number[] = [1, 2, 3, 5, 7, 11, 13];

أنواع المراجع (Reference Types)

تعتبر أنواع المراجع (Reference Types) في TypeScript جزءاً أساسياً لفهم كيفية التعامل مع المتغيرات والكائنات والبرمجة الكائنية:

الفئات (Classes)

  • الفئة هي تعريف لنوع جديد من الكائنات.
  • يمكن استخدام الخصائص والطرق لإنشاء سلوكيات محددة لكل كائن من النوع.
  • الفئة تسمح بتعريف واجهة وبنية مشتركة بين الكائنات.
class Example {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  sayName(): void {
    console.log(`مرحبا، اسمي ${this.name}`);
  }
}

let example = new Example("John Doe");
example.sayName(); // Output: مرحبا، اسمي John Doe

الواجهات (Interfaces)

الواجهة (Interface) هي تعريف لمجموعة من الخصائص والطرق التي يجب أن تتوفر في صنف معين، وتعمل كعقد يسهل التواصل بين الأصناف ويحسن تنظيم الكود:

interface IExample {
    name: string;
    sayHello(): void;
}

class ExampleClass implements IExample {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    sayHello(): void {
        console.log("مرحباً!");
    }
}

let exampleInstance = new ExampleClass("John");
exampleInstance.sayHello(); // Output: مرحباً!

interface Student {
 name: string;
 age: number;
 courses: string[];
}

let student: Student = {
 name: "John",
 age: 25,
 courses: ["Math", "English", "Science"]
};

العدادات والتعدادات (Enums)

العداد (Enum) هو نوع خاص في TypeScript يسمح بتنظيم القيم ضمن مجموعة محددة ذات أسماء وصفية، ويمكن استخدامه بمفرده أو مدمجاً مع الواجهات:

enum MyEnum {
    value1 = 'القيمة 1',
    value2 = 'القيمة 2',
    value3 = 'القيمة 3'
}

console.log(MyEnum.value1); // Output: القيمة 1
console.log(MyEnum.value2); // Output: القيمة 2
console.log(MyEnum.value3); // Output: القيمة 3

// استخدام الـ Enum والـ Interface معاً
enum OrderStatus {
 processed,
 shipped,
 delivered,
 cancelled
}

interface Order {
 orderId: number;
 customerName: string;
 status: OrderStatus;
}

let order: Order = {
 orderId: 123,
 customerName: "John Doe",
 status: OrderStatus.shipped
};

console.log(order.status); // طباعة حالة الطلب

الفئات البنائية والواجهات (Constructor Implementations)

interface MyInterface {
    name: string;
    age: number;
    color: string;
}

class MyClass implements MyInterface {
    name: string;
    age: number;
    color: string;

    constructor(name: string, age: number, color: string) {
        this.name = name;
        this.age = age;
        this.color = color;
    }

    sayHello(): void {
        console.log(`مرحباً من ${this.name}`);
    }
}

let instance: MyClass = new MyClass("John Doe", 30, "blue");
instance.sayHello();

أنواع القيم المعقدة (Complex Value Types)

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

الأنواع الحرفية (Literal Types): تضييق نطاق القيم

تسمح لك هذه الأنواع بتحديد قيمة واحدة دقيقة مقبولة لمتغير معين، بدلاً من السماح بجميع القيم الممكنة من نوع ما:

type Direction = "up" | "down" | "left" | "right";

function move(dir: Direction) {
  console.log(`التحرك باتجاه: ${dir}`);
}

move("up");    // ✅ صحيح
// move("north"); // ❌ خطأ في التحقق من الأنواع

type StatusCode = 200 | 404 | 500;
type ActiveState = true;

const code: StatusCode = 200;   // ✅
const active: ActiveState = true; // ✅

التجميع بين الأنواع باستخدام Union Types والعوامل الشرطية

عندما يحتاج متغير إلى قبول أكثر من نوع أساسي، نستخدم أنواع الاتحاد (Union Types) التي تربط بين الأنواع باستخدام العامل |:

function printId(id: string | number) {
  if (typeof id === "number") {
    console.log(`الرقم: ${id}`);
  } else {
    console.log(`النص: ${id}`);
  }
}

printId(42);    // ✅
printId("ABC"); // ✅

type MyData = {
    name: string;
    age: number;
    color: string;
};

type MyResult = MyData | null;

let data: MyResult = null; // يمكن أن تكون كائناً من MyData أو null

الأنواع المتقاطعة (Intersection Types): دمج الخصائص

بينما تجمع أنواع الاتحاد بين إمكانية واحدة من عدة أنواع، فإن الأنواع المتقاطعة (Intersection Types) تدمج عدة أنواع في نوع واحد يحتوي على كل الخصائص مجتمعة باستخدام العامل &:

type PersonType = { 
  name: string; 
  age: number; 
};

type EmployeeType = { 
  department: string; 
  salary: number; 
};

type EmployeePerson = PersonType & EmployeeType;

const staffMember: EmployeePerson = {
  name: "سارة",
  age: 28,
  department: "تطوير البرمجيات",
  salary: 5000
};

console.log(staffMember.name, staffMember.department);

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

في هذا المقال الشامل، استعرضنا أنواع البيانات في TypeScript: بدءاً من الأنواع الأساسية (Number, String, Boolean)، والأنواع الخاصة (Void, Unknown, Never)، والأنواع المركبة (Record, Map, Set, Array, Tuple)، وصولاً إلى أنواع المراجع (Classes, Interfaces, Enums) والأنواع المتقدمة كالاتحاد والتقاطع والأنواع الحرفية.

  • الموقع والتوثيق الرسمي للغة TypeScript للتعرف على المزيد حول الأنواع الأساسية والمتقدمة.

اكتشاف المزيد من كود التطور

اشترك للحصول على أحدث التدوينات المرسلة إلى بريدك الإلكتروني.

اترك رد

Scroll to Top