TypeScript للبرمجة النظامية: تحالف غير متوقع

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

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

ابدأ الدورة

TypeScript للبرمجة النظامية: عندما يلتقي JavaScript مع العتاد



عندما نقول "البرمجة النظامية"، يتبادر إلى الذهن عادةً C، Rust، وربما Go أو Zig. لكن TypeScript؟ نفس اللغة التي "تترجم فقط إلى JavaScript"؟ صدقوني، هذه ليست مزحة. TypeScript يتسلل إلى حيث كانت تسود المؤشرات، الذاكرة الخام، والإدارة اليدوية للموارد. ويفعل ذلك بأناقة.



في هذه المقالة، سنستعرض كيف يمكن استخدام TypeScript في المهام التي تُعتبر تقليديًا "نظامية": التعامل مع الذاكرة، التفاعل مع الكود الأصلي (FFI)، الإدخال/الإخراج غير المتزامن على مستوى النواة، كتابة برامج التشغيل، وحتى التطوير المضمن. لن ندّعي أن TypeScript سيحل محل C - لكننا سنظهر أين تمنحه نقاط قوته أفضلية على الأساليب التقليدية.



لماذا يُنظر إلى TypeScript أصلاً للبرمجة النظامية؟



الحجة التقليدية ضد TypeScript هي جمع القمامة والآلة الافتراضية. نعم، هذا صحيح. لكن Node.js الحديث (و Deno، و Bun) يعمل على محرك V8، الذي يقترب في الأداء من اللغات المترجمة في العديد من السيناريوهات. والأهم من ذلك - يمنحك TypeScript أمان الأنواع، ونظامًا بيئيًا غنيًا، وسرعة في التطوير لا تتوفر في C الخالص.



بالنسبة لـ البرمجة النظامية باستخدام TypeScript، تصبح المزايا الرئيسية:



  • الكتابة الصارمة - تكتشف الأخطاء في مرحلة الترجمة، وليس أثناء التنفيذ
  • async/await الحديث - مثالي للإدخال/الإخراج غير المحظور
  • إمكانيات غنية للبرمجة الفوقية (المزينات، الأنواع العامة، الأنواع الشرطية)
  • النظام البيئي npm - آلاف الوحدات الجاهزة للتعامل مع الاستدعاءات النظامية
  • إمكانية الكتابة بلغة واحدة للواجهة الأمامية، والخلفية، والأدوات النظامية


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



التعامل مع الذاكرة في TypeScript: ليس مخيفًا كما يبدو



أحد أكبر مخاوف مبرمجي الأنظمة هو فقدان السيطرة على الذاكرة. في TypeScript، لا يمكنك تخصيص وتحرير الذاكرة مباشرة كما في C. لكن يمكنك استخدام ArrayBuffer و DataView و TypedArray للتعامل مع البيانات الثنائية الخام.



إليك مثال حقيقي - قراءة رأس ثنائي لملف PNG بدون مكتبات خارجية:



function readPNGHeader(buffer: ArrayBuffer): { width: number, height: number } | null {  const view = new DataView(buffer);    // التحقق من توقيع PNG (8 بايت)  const signature = new Uint8Array(buffer, 0, 8);  const pngSignature = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]);    for (let i = 0; i < 8; i++) {    if (signature[i] !== pngSignature[i]) return null;  }

// قراءة كتلة IHDR (إزاحة 16 بايت: 8 توقيع + 4 طول + 4 نوع) const width = view.getUint32(16, false); // Big-endian const height = view.getUint32(20, false); // Big-endian

return { width, height };}

// الاستخدام:const fs = require('fs');const data = fs.readFileSync('image.png');const result = readPNGHeader(data.buffer);console.log(result); // { width: 1920, height: 1080 }


كما ترون، نحن نتعامل مع الذاكرة تقريبًا على مستوى C - نستخدم الإزاحات، ترتيب البايت، والمخازن المؤقتة الخام. وفي الوقت نفسه، يتحقق TypeScript من الأنواع ويحمي من الأخطاء السخيفة مثل تمرير سلسلة نصية بدلاً من رقم.



FFI (واجهة الوظائف الأجنبية) - جسر إلى الكود الأصلي



عندما لا يكون أداء TypeScript كافيًا، يأتي FFI للإنقاذ. باستخدام وحدة ffi-napi (لـ Node.js)، يمكنك استدعاء وظائف من المكتبات الأصلية (.so، .dll، .dylib) مباشرة.



مثال - استدعاء وظيفة getpid() من libc على Linux:



import ffi from 'ffi-napi';

const libc

المدونات

توصيات الكتب