التواريخ في TypeScript: كيفية تعريف النوع بشكل صحيح وعدم فقدان الصواب

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

لماذا يعتبر "Date" في TypeScript فخًا؟

\n

تخيل: أنت تكتب تطبيقًا، كل شيء يعمل بشكل مثالي. ولكن في يوم من الأيام يأتي تقرير خطأ: "المستخدم من طوكيو يرى التاريخ قبل يوم واحد". تفتح الكود وترى الكلاسيكية: let date: Date = "2024-12-01". أو ما هو أسوأ — any. هل هذا مألوف؟

\n

السؤال "How do I express a date type in TypeScript?" على Stack Overflow حصل على عشرات الآلاف من المشاهدات. وليس من دون سبب. يبدو أن الإجابة بسيطة: استخدم Date. ولكن في الواقع، الأمر أكثر تعقيدًا. TypeScript هي طبقة فوق JavaScript، و Date في JS هو كائن يتصرف... بشكل غريب.

\n

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

\n\n

1. الكتابة الأساسية: Date أم string أم number؟

\n

الخيار الأكثر وضوحًا هو النوع المدمج Date:

\n
let birthday: Date = new Date('1990-05-15');
\n

هل يعمل؟ نعم. ولكن هناك فارق بسيط: TypeScript لا يتحقق مما إذا كان التاريخ صالحًا. يمكنك كتابة new Date('abc') — لن يكون هناك خطأ في الترجمة، ولكن في وقت التشغيل ستحصل على Invalid Date. ومحاولة استدعاء .getTime() ستعيد NaN.

\n

الخيار الأكثر صرامة هو استخدام سلاسل نصية بتنسيق ISO 8601:

\n
type ISODateString = string;\nlet eventDate: ISODateString = '2024-12-01T10:00:00Z';
\n

الميزة: نوع بسيط، يسهل تسلسله إلى JSON. العيب: لا يوجد أي تحقق — يمكنك تعيين 'hello'، ولن يمانع TS.

\n

الخيار الثالث هو timestamp (number):

\n
type Timestamp = number;\nlet ms: Timestamp = Date.now();
\n

مثالي للعمليات الحسابية، لكنه غير قابل للقراءة للبشر. ومرة أخرى — أي رقم سيجتاز التحقق.

\n\n

ملخص الأنواع الأساسية

\n

إذا كنت بحاجة إلى كتابة سريعة وغير دقيقة — استخدم Date. إذا كنت تعمل مع API — استخدم string. إذا كنت بحاجة فقط إلى حسابات — استخدم number. ولكن بالنسبة للمشاريع الجادة، هذا غير كافٍ. دعنا نتعمق أكثر.

\n\n

2. الأنواع المخصصة وكتابة العلامات التجارية (branded types)

\n

لمنع TypeScript من الخلط بين سلسلة التاريخ والسلسلة العادية، يتم استخدام الأنواع ذات العلامات التجارية. إنها مثل ملصق "أنا تاريخ، تم التحقق منه!":

\n
type DateString = string & { __brand: 'DateString' };\n\nfunction toDateString(date: Date): DateString {\n    return date.toISOString() as DateString;\n}\n\nfunction processDate(d: DateString) {\n    // هنا d هي بالتأكيد سلسلة ISO\n}\n\nconst iso = toDateString(new Date());\nprocessDate(iso); // صحيح\n// processDate('hello'); // ❌ خطأ!
\n

هذا يضيف أمانًا في مرحلة الترجمة. الثمن هو المزيد من الكود قليلاً. ولكن بالنسبة للمشاريع الكبيرة (خاصة إذا كان لديك خدمات مصغرة أو REST API) فهذا هو المعيار الذهبي.

\n\n

3. المكتبات للعمل مع التواريخ في TypeScript

\n

استخدام Date الخام هو شر. إنه قابل للتغيير، ويوزع السلاسل بشكل غير متسق، والمناطق الزمنية هي ألم منفصل. لذلك في المشاريع الحقيقية، يتم استخدام المكتبات. إليك ثلاثة مفضلة:

\n\n

3.1. date-fns (خفيفة ومعيارية)

\n
import { format, parseISO } from 'date-fns';\n\nconst date: Date = parseISO('2024-12-01T10:00:00Z');\nconst formatted: string = format(date, 'dd.MM.yyyy');
\n

الميزة: tree-shaking، غير قابل للتغيير (يعيد تواريخ جديدة)، كتابة ممتازة.

\n\n

3.2. Day.js (بسيطة)

\n
import dayjs, { Dayjs } from 'dayjs';\n\nconst now: Dayjs = dayjs();\nconst formatted: string = now.format('DD.MM.YYYY');
\n

Day.js تشبه Moment.js، ولكن بدون حزمة ضخمة. النوع Dayjs هو غلاف يسهل تمريره بين الدوال.

\n\n

3.3. Luxon (

المدونات

توصيات الكتب