الأنواع الأساسية في TypeScript

من موسوعة حسوب
اذهب إلى التنقل اذهب إلى البحث


مقدمة

علينا أن نتمكن من العمل مع أنواع بسيطة من البيانات لتكون برامجنا مُفيدة، مثل الأعداد (numbers)، والسلاسل النصية (strings)، الهياكل (structures)، القيم المنطقية (boolean values)، وغيرها من أنواع البيانات. تدعم TypeScript تقريبًا نفس الأنواع التي تدعمها لغة JavaScript، إضافة إلى نوعٍ جديدٍ للثوابت المتعددة (enumeration type) للمساعدة على بناء تطبيقات أفضل.

القيم المنطقية (boolean)

القيمتان ‎true‎ و‎false‎ من أكثر أنواع البيانات بساطة، وتُسمى في JavaScript وTypeScript بالقيمة المنطقية، أو النوع ‎boolean‎.

let isDone: boolean = false;

الأعداد (number)

كما هو الحال مع JavaScript، فجميع الأعداد في TypeScript قيم نقطة عائمة (floating point values). وهذه الأعداد العشرية ذات النقطة العائمة ترتبط بالنوع ‎number‎. وتدعم TypeScript كذلك البيانات الثنائية (binary) والبيانات الثمانية (octal) التي قدّمتها نسخة ECMAScript 2015، إضافةً إلى الأعداد الست عشرية (Hexadecimal) والبيانات الحرفية العشرية (decimal literals).

let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;

السلاسل النصية (string)

العمل مع البيانات النصية من الأجزاء الأساسية في برامج JavaScript كذلك. وكما في اللغات الأخرى، فلغة TypeScript تعتمد على النوع ‎string‎ للإشارة إلى أنواع البيانات النصية هذه. وكما في JavaScript، فلغة TypeScript تعتمد كذلك على علامات التنصيص المزدوجة ("‎) أو المفردة ‎(') لإحاطة بيانات السلسلة النصية.

let color: string = "blue";
color = 'red';

يُمكنك كذلك استعمال سلاسل القوالب النصيّة (template strings)، والتي يُمكن لها تخزين عدّة أسطر والاحتواء على تعابير مُضمّنة. هذه السلاسل النصية مُحاطة بالمحرف (‎`‎)، ويُمكن تضمين التعابير فيها على شكل ‎‎${ expr }‎‎.

let fullName: string = `Bob Bobbington`;
let age: number = 37;
let sentence: string = `Hello, my name is ${ fullName }.

I'll be ${ age + 1 } years old next month.`;

هذا مُكافئ للتصريح عن المتغيّر ‎sentence‎ كما يلي:

let sentence: string = "Hello, my name is " + fullName + ".\n\n" +
    "I'll be " + (age + 1) + " years old next month.";

المصفوفات (Array)

تسمح TypeScript بالعمل مع مصفوفات من القيم كما في JavaScript. يُمكن كتابة أنواع المصفوفات بطريقتين. إما عبر استعمال نوع عناصر المصفوفة متبوعًا بالقوسين ‎[]‎ لتعريف مصفوفةٍ بنوع العناصر المُعطى، في المثال التالي نُعرّف مصفوفة أعداد باستخدام النوع ‎‎‎number:

let list: number[] = [1, 2, 3];

الطريقة الأخرى هي باستخدام نوع مصفوفة عمومي (generic array type) على الشكل ‎‎Array‎<elemType>‎‎ مع استبدال elemType‎ بالنوع المرغوب:

let list: Array<number> = [1, 2, 3];

الصفوف (Tuple)

تسمح الصفوف بالتعبير عن مصفوفة يكون فيها عدد أنواع العناصر معروفًا، دون أن تكون ذات نفس النوع. على سبيل المثال، قد ترغب بتمثيل قيمة كزوج سلسلة نصيّة ‎string‎ وعدد ‎number‎:

// تعريف نوع صفّ
let x: [string, number];
// تخزين البيانات في الصف
x = ["hello", 10]; // عبارة صحيحة
// تخزين البيانات في الصف بطريقة خاطئة
x = [10, "hello"]; // خطأ

عند الوصول إلى عنصر يكون فهرسه (index) معروفًا، فسيُجلب النوع المناسب:

console.log(x[0].substr(1)); // OK
console.log(x[1].substr(1)); // خطأ لأن العدد لا يملك هذا التابع / 'number' does not have 'substr'

عند الوصول إلى عنصر خارج مجموعة الفهارس المعروفة، فسيُستعمل نوع اتحاد (union type) عوضًا عن ذلك:

// يُمكن تعيين سلسلة نصية لأنها عنصر من المجموعة (سلسلة نصية أو عدد)
x[3] = "world"; // OK, 'string' can be assigned to 'string | number'

// التابع موجود في كلا النوعين
console.log(x[5].toString()); // OK, 'string' and 'number' both have 'toString'

// (سلسلة نصية أو عدد) خطأ، القيمة المنطقية ليست من المجموعة
x[6] = true; // Error, 'boolean' isn't 'string | number'

أنواع الاتحاد من المواضيع المتقدمة التي سنتطرق إليها في فصل آخر.

الثوابت المتعددة (enum)

النوع ‎enum‎ من أنواع البيانات المفيدة المُضافة على الأنواع القياسية الموجودة في JavaScript. ويمثل هذا النوعُ نوعَ الثوابت المتعددة الذي يعمل بطريقة مشابهة لما في لغات أخرى مثل C#‎، وهو نوع يسمح بتسمية مجموعات القيم العددية (sets of numeric values) بتسميات واضحة.

enum Color {Red, Green, Blue}
let c: Color = Color.Green;

افتراضيًا، يبدأ عدّ عناصر الثوابت المتعددة من ‎0‎. يُمكنك تغيير ذلك عبر ضبط قيمة أحد العناصر. على سبيل المثال، يُمكننا أن نبدأ المثال السابق من العدد ‎1‎ عوضًا عن ‎0‎:

enum Color {Red = 1, Green, Blue}
let c: Color = Color.Green;

أو يُمكن ضبط جميع قيم الثوابت المتعددة يدويا:

enum Color {Red = 1, Green = 2, Blue = 4}
let c: Color = Color.Green;

توفّر الثوابت المتعددة ميّزة تُمكنك من الوصول إلى اسم القيمة من قيمتها العددية. مثلًا، إن كانت لدينا القيمة ‎2‎ ولم نعرف بماذا هي مُرتبطة في الثابتة المتعددة ‎Color‎ أعلاه، فيُمكننا البحث عن اسم القيمة كما يلي:

enum Color {Red = 1, Green, Blue}
let colorName: string = Color[2];

alert(colorName); // 'Green'

لاحظ أن الاسم الذي يُعرض هو Green، وذلك لأن قيمته هي ‎2‎ في الثابتة المتعددة ‎Color‎ أعلاه.

النوع Any

قد نحتاج إلى وصف نوع مُتغيّرات نَجهَلُ نوعها عند كتابة التطبيق. يُمكن لهذه القيم أن تأتي من محتوى ديناميكي، كمُستخدم أو مكتبة طرف ثالث (3rd party library). في هذه الحالات، نُريد أن نُلغي التحقق من الأنواع ونسمح للقيم بالمرور عبر نقاط التفتيش في زمن الترجمة (compile-time checks). للقيام بذلك، نستعمل النوع ‎any‎ لوصف هذه المتغيّرات:

let notSure: any = 4;
notSure = "ربما القيمةُ سلسلة نصيّةٌ وليست عددًا";
notSure = false; // بل هي قيمة منطقية

يُعدّ النوع ‎any‎ طريقة فعّالة للعمل مع شيفرات JavaScript الموجودة مُسبقًا، ما يسمح لك بالتدرج في استخدام التحقق من الأنواع عند الترجمة. قد تتوقّع للنوع ‎Object‎ أن يعمل بشكل مُشابه كما في لغات البرمجة الأخرى. لكن المتغيرات من النوع Object تسمح فقط بتعيين أي نوع من القيم لها، ولا يُمكنك استدعاء توابع اعتباطية (arbitrary methods) عليها، حتى ولو كانت هذه التوابع موجودة بالفعل:

let notSure: any = 4;
// يُمكن للتابع
// ifItExists
// أن يكون موجودًا عند زمن التشغيل
// (runtime)
notSure.ifItExists();
notSure.toFixed(); // التابع موجود، لكن المُترجم لا يتحقق من ذلك

let prettySure: Object = 4;
// خطأ التابع ليس موجودًا
prettySure.toFixed(); // Error: Property 'toFixed' doesn't exist on type 'Object'.

النّوع ‎any‎ مُفيد كذلك عندما تعرف جزءًا من نوع المتغيّر، لكن ليس بشكل كامل. مثلًا، يُمكن أن تمتلك مصفوفةً أنواع عناصرها مُختلفة كما يلي:

let list: any[] = [1, true, "free"];

list[1] = 100;

النوع Void

النوع ‎void‎ هو المقابل للنوع ‎any‎ نوعًا ما، ويشير إلى غياب النوع. وستراه عادةً كنوعٍ للقيمة المُعادة (return type) للدوال التي لا تُعيد أية قيمة:

function warnUser(): void {
    alert("This is my warning message");
}

تصريح مُتغيّر بالنوع ‎void‎ غير مُفيد لأن المتغير لن يقبل سوى تعيين القيمة ‎undefined‎ أو القيمة ‎null‎ له.

// هذا المتغير لا قيمة له
let unusable: void = undefined;

النوعان Null وUndefined

في لغة TypeScript، يكون للقيمتان ‎undefined‎ و‎null‎ نوعان خاصان بهما يُسميان بنفس الاسم ‎undefined‎ و‎null‎. وكما الحال مع النوع ‎void‎، فهما نوعان غير مفيدان كثيرًا عندما يكونان مُنفردين:

// لا يُمكننا تعيين أية قيمة مُغايرة للمتغيرين التاليين
let u: undefined = undefined;
let n: null = null;

افتراضيا، يُعتبر النوعان ‎undefined‎ و‎null‎ أنواعًا فرعيّة لجميع الأنواع الأخرى. ما يعني بأنك تستطيع تعيين ‎undefined‎ و‎null‎ للنوع number على سبيل المثال.

لكن عند استخدام الراية (flag) ‎‎‎--‎strictNullChecks‎‎ مع المُترجم (compiler)، ستتمكن من تعيين ‎undefined‎ و‎null‎ للنوع void فقط إضافةً إلى نوعيهما المناسبين. ويُساعد هذا على تجنّب الكثير من الأخطاء الشائعة. وفي حالة أردت تمرير قيمة من النوع string أو النوع ‎undefined‎ أو النوع ‎null‎، فيُمكنك استخدام نوع الاتّحاد (union type) string | null | undefined. وللمزيد عن أنواع الاتّحاد، انظر صفحة الأنواع المتقدمة.

مُلاحظة: من المستحسن استخدام الخيار ‎‎‎--‎strictNullChecks‎‎ كلما أمكن ذلك، لكنّ هذا التوثيق يعتبر الخيار مُعطّلًا عندك.

النوع Never

يُمثّل النوع ‎never‎ نوع القيم التي لا تظهر أبدًا. مثلًا، تُعاد قيمة من النوع never من طرف تعبير دالة أو تعبير دالة سهميّة (arrow function expression) التي تُطلق دائمًا استثناءًا (exception) أو دالّة لا تُعيد أيّ شيء أبدًا؛ وقد تحمل المتغيراتُ النوعَ never عندما يُحدّ بأي حارس أنواع (type guard) لا يُمكن له أن يكون ذا قيمة منطقية صحيحة أبدًا.

النوع never نوع فرعي لجميع الأنواع ويُمكن تعيينه لجميع الأنواع كذلك؛ لكن لا يوجد نوع فرعي له، ولا يُمكن تعيين أي نوعٍ للنوع never (إلا هو ذاته). وحتى النوع any غير قابل لتعيينه للنوع never.

بعض الأمثلة على دوال تُعيد النوع never:

// الدالة التي تُعيد النوع لا تكون ذات نقطة نهاية يُمكن الوصول إليها
// Function returning never must have unreachable end point
function error(message: string): never {
    throw new Error(message);
}

// لأن الدالة تستدعي دالةً ترمي استثناءً، ومن ذلك يُستنتج النوع
// Inferred return type is never
function fail() {
    return error("Something failed");
}

// دالة تعمل لا نهائيًّا ولا تُعيد أي شيء أبدًا
function infiniteLoop(): never {
    while (true) {
    }
}

النوع Object

يُمثّل النوع ‎object‎ الأنواع غير الأوليّة (non-primitive). أي كلّ نوعٍ مُغايرٍ لأحد الأنواع التالية: ‎number‎، و ‎string‎، و ‎boolean‎، و ‎symbol‎، و ‎null‎، و ‎undefined‎.

يُوفّر النوع object طريقةً جيدةً لتمثيل واجهات برمجيّة (APIs) مثل Object.create. على سبيل المثال:

declare function create(o: object | null): void;

create({ prop: 0 }); // OK
create(null); // OK

create(42); // Error
create("string"); // Error
create(false); // Error
create(undefined); // Error

إثباتات الأنواع (Type assertions)

قد تقع أحيانًا في حالة تعرف فيها معلومات عن قيمة ما أكثر من معرفة TypeScript بها. هذا يحدث عادةً عندما تعلم أن نوع كائن يُمكن أن يكون أكثر تحديدًا من نوعه الحالي.

إثباتات الأنواع طريقة لإخبار المُترجم (compiler) بأنّك تعرف ما تفعله وأنّك أدرى بنوع الكائن منه. وإثبات النوع شبيه بجبائر الأنواع (type cast) في لغات البرمجة الأخرى، لكنه لا يقوم بأي تحقّق خاصّ أو إعادة هيكلة للبيانات. ولا تأثير له في زمن التشغيل (no runtime impact)، ويُستعمل من طرف المُترجم فقط. ولغة TypeScript تعتبر أنّكَ -بصفتك المُبرمج- قد تحقّقت من البيانات كما يجب.

لإثباتات الأنواع شكلان. الشكل الأول يكون باستعمال أقواس الزاوية (angle-bracket):

let someValue: any = "this is a string";

let strLength: number = (<string>someValue).length;

والشكل الآخر يكون باستعمال الكلمة المفتاحية ‎as‎:

let someValue: any = "this is a string";

let strLength: number = (someValue as string).length;

المثالان أعلاه مُتكافئان. واستعمال شكل دون آخر مجرّد اختيار يختاره المبرمج؛ لكن عند استخدام JSX مع TypeScript، فطريقة الكلمة المفتاحية as هي المسموح بها فقط.

ملاحظة حول الكلمة المفتاحية let

قد تُلاحظ بأنّنا نستخدم الكلمة المفتاحيّة ‎let‎ عوضًا عن الكلمة المفتاحيّة ‎var‎ المعروفة في لغة JavaScript. الكلمة المفتاحيّة let من مزايا لغة JavaScript الجديدة، والتي تُتيحها لغة TypeScript. يُمكن حل العديد من مشاكل JavaScript عبر استخدام الكلمة المفتاحيّة let، لذا من المستحسن استعمالها عوضًا عن var متى أمكن ذلك.

مصادر