Kodokon kodokon.com

الأنواع العامة المتقدمة: القيود والقيم الافتراضية

تقيّد مُعاملات أنواعك بـextends وتوفّر قيما افتراضية لبناء واجهات برمجية عامة مريحة.

10 دقيقة · 3 أسئلة

افتح هذا الدرس في Kodokon

النوع العام غير المقيّد معتم: داخل جسم الدالة، T لا يسمح لك بفعل أي شيء. جملة extends تُنشئ عقدا أدنى على مُعامل النوع، فتفتح الوصول إلى الخصائص المضمونة مع الحفاظ على النوع الدقيق للمُنادي. هذا هو الفرق الجوهري عن مُعامل مُنوَّع بواجهة: القيد يحفظ المعلومة، والواجهة تمحوها.

TYPESCRIPT
function longest<T extends { length: number }>(
  a: T,
  b: T
): T {
  return a.length >= b.length ? a : b;
}

const word = longest("hello", "worldwide");
const list = longest([1, 2], [3, 4, 5]);
يبقى word مُنوَّعا كـstring، وlist كـnumber[].

ثنائي K extends keyof T هو النمط الأكثر ربحية في الاستعمال اليومي: يربط مُعاملَي نوع معا ويضمن استاتيكيا أن المفتاح ينتمي فعلا إلى الكائن. نوع العائد T[K] (وهو نوع وصول مُفهرَس) يتتبّع بدقة الخاصية المطلوبة. هكذا بالضبط تُنوَّع دوال مثل pick أو pluck من مكتبات الأدوات.

TYPESCRIPT
function getProp<T extends object, K extends keyof T>(
  obj: T,
  key: K
): T[K] {
  return obj[key];
}

const config = { port: 8080, host: "localhost" };
const port = getProp(config, "port");
const host = getProp(config, "host");
يُستنتَج port كـnumber، وhost كـstring؛ وأي مفتاح آخر يُرفض.

القيم الافتراضية (T = string) تجعل النوع العام اختياريا عند موضع النداء: لا يقدّم المستعملون وسيط النوع إلا حين يريدون الخروج عن الحالة الشائعة. وباجتماعها مع قيد (T extends X = Y)، تُحدّد الحدّ والسلوك الضمني معا. يجب أن تُحقّق القيمة الافتراضية القيدَ، وإلا فإن التصريح نفسه لا يُصرَّف. هذا هو النمط المعياري لعملاء HTTP والمخازن ومُصدِرات الأحداث المُنوَّعة.

TYPESCRIPT
interface ApiResponse<T = unknown> {
  status: number;
  data: T;
}

async function fetchJson<T = unknown>(
  url: string
): Promise<ApiResponse<T>> {
  const res = await fetch(url);
  return { status: res.status, data: await res.json() };
}

interface Post { id: number; title: string }
const typed = await fetchJson<Post>("/api/posts/1");
const loose = await fetchJson("/api/health");
من دون وسيط نوع، يرتد data إلى unknown لا any.

اختبار المعرفة

تأكّد من أنك تذكّرت النقاط الأساسية في هذا الدرس.

  1. ماذا يضمن القيد K extends keyof T في توقيع عام؟
    • أن K هو أي نص كان
    • أن K مفتاح موجود من مفاتيح T، مُتحقَّق منه وقت التصريف
    • أن T يحتوي مفتاحا اختياريا واحدا على الأقل
    • أن K وT من النوع نفسه
  2. ما الميزة التي يملكها longest<T extends { length: number }> على longest(a: { length: number }, b: ...) بلا أنواع عامة؟
    • يعمل أسرع
    • يحفظ النوع الدقيق للمُنادي في القيمة العائدة
    • يقبل أنواع مُدخلات أكثر
  3. لماذا نفضّل <T = unknown> على <T = any> قيمة افتراضية؟
    • unknown يُصرَّف أسرع من any
    • any ممنوع في الوضع الصارم
    • unknown يفرض على المُنادي تضييق النوع قبل الاستعمال، وany يُعطّل كل تحقق
    • unknown يقبل قيما أقل من any