TypeScript کا نیا const type parameter سنٹیکس کسی فنکشن کو اس قابل بناتا ہے کہ وہ literal types کو برقرار رکھ سکے، بغیر اس کے کہ کالرز (callers) کو ہر جگہ as const کا استعمال کرنا پڑے۔ اس سے type-widening بگز (bugs) کا سب سے عام ذریعہ ختم ہو جاتا ہے۔
وہ widening کا مسئلہ جو generic کوڈ کو پریشان کرتا ہے
جب ایک generic فنکشن کو object literal موصول ہوتا ہے، تو کمپائلر کسی بھی literal property کو اس کی وسیع تر primitive type میں تبدیل (widen) کر دیتا ہے۔
function call<T>(arg: T) {}
call({ method: "GET" }) // T is inferred as { method: string }
Literal "GET" اب string بن جاتا ہے۔ اس کے بعد کا کوڈ (downstream code) جو بالکل درست ویلیو پر منحصر ہو—جیسے کہ discriminated unions یا template-literal extraction—خراب ہو جاتا ہے کیونکہ type اب وہ درست literal فراہم نہیں کرتی۔ ڈویلپرز طویل عرصے سے اس کا حل call site پر { method: "GET" } as const لکھ کر نکالتے رہے ہیں، جو کمپائلر کو literal برقرار رکھنے کا حکم دیتا ہے، لیکن یہ حل کالر (caller) کے ہاتھ میں ہوتا ہے، فنکشن کی تعریف (definition) میں نہیں۔
Const type parameters: signature-level حل
Type parameter پر نیا const modifier کمپائل器 کو بتاتا ہے کہ اس generic argument کے لیے ممکنہ حد تک تنگ ترین (narrowest possible) type کا اندازہ لگائے۔ کسی فنکشن کو function foo<const T>(arg: T) کے طور پر قرار دینے سے T خود بخود ایسے کام کرتا ہے جیسے کالر نے as const لکھا ہو۔
- String, number, boolean literals اپنی اصل ویلیوز برقرار رکھتے ہیں (
stringکے بجائے"GET")۔ - Arrays readonly tuples بن جاتے ہیں جن میں ہر element کی type بالکل درست ہوتی ہے۔
- Objects گہرے (deeply) readonly structures میں تبدیل ہو جاتے ہیں، جو ہر nesting level پر literal types کو محفوظ رکھتے ہیں۔
چونکہ یہ پابندی (constraint) فنکشن کے signature میں موجود ہے، اس لیے ہر کالر کو خود بخود فائدہ پہنچتا ہے؛ اب cast کرنا بھول جانا unsoundness کا باعث نہیں بنتا۔
یہ کلاسک as const ہیک پر کیوں غالب ہے
as const ایک caller-side حل ہے۔ اس کے لیے generic فنکشن کے ہر صارف (consumer) کو assertion شامل کرنا یاد رکھنا پڑتا ہے۔ اگر ایک بھی کال رہ جائے، تو type safety ختم ہو جاتی ہے۔ Const type parameter اس ذمہ داری کو خود API ڈیزائن میں منتقل کر دیتا ہے: فنکشن اعلان کرتا ہے کہ "مجھے آپ کی بھیجی گئی چیز کی سب سے تنگ ترین شکل (narrowest shape) چاہیے،" اور کمپائلر اس پر عمل درآمد کرتا ہے۔
یہ تبدیلی ان لائبریریز اور یوٹیلیٹیز کے لیے سب سے زیادہ اہم ہے جو generic builders، configuration factories، یا ایسی کسی بھی API کو فراہم کرتی ہیں جہاں کسی فیلڈ کی literal value type logic کو چلاتی ہے۔ لائبریری کا مصنف downstream کوڈ کی نگرانی کیے بغیر درست inference کی ضمانت دے سکتا ہے۔
وہ حقیقی دنیا کے منظرنامے (scenarios) جو اس سے فائدہ اٹھاتے ہیں
- Configuration builders – ماحول کے نام (
"dev" | "prod") literal رہتے ہیں، جس سے اضافی casts کے بغیر discriminated-union چیکنگ ممکن ہو جاتی ہے۔ - API route definitions – path strings بالکل درست رہتے ہیں، جس سے template-literal types کو پیرامیٹرز نکالنے کی اجازت ملتی ہے (
"/users/:id"→\/users/${string}``)۔ - State-machine helpers – method chaining کے ذریعے state identifiers مستقل literals رہتے ہیں، جس سے غلطی سے ہونے والے state mismatches سے بچا جا سکتا ہے۔
ہر صورت میں، const parameter بار بار استعمال ہونے والے as const boilerplate کو ختم کرتا ہے اور باریک بگ (subtle bugs) کے نکل جانے کے امکان کو کم کرتا ہے۔
satisfies operator کے ساتھ جوڑنا
satisfies operator اس بات کی تصدیق کرتا ہے کہ ایک ویلیو structural type کے مطابق ہے جبکہ اس کی اصل literal معلومات کو بھی برقرار رکھتا ہے۔ دونوں کو ایک ساتھ استعمال کرنے سے بہترین نتائج ملتے ہیں: const parameters تنگ ترین (narrow) inference فراہم کرتے ہیں، اور satisfies اس بات کو یقینی بناتا ہے کہ ویلیو مطلوبہ شکل (shape) کے مطابق ہو۔
function makeConfig<const C>(cfg: C) {
// cfg is inferred with exact literals
}
const cfg = {
env: "staging",
ports: [8080, 8443],
} satisfies { env: string; ports: number[] };
makeConfig(cfg); // works, literals stay intact
کب as const کو برقرار رکھنا چاہیے
Const parameter اس وقت بہترین کام کرتا ہے جب آپ فنکشن کے signature کو کنٹرول کرتے ہیں۔ اگر آپ ایسی تھرڈ پارٹی فنکشنز کے ساتھ کام کر رہے ہیں جن میں یہ modifier موجود نہیں ہے، یا آپ کو کسی لوکل ویری ایبل کے لیے صرف ایک بار literal preservation کی ضرورت ہے، تو as const ہی صحیح ٹول ہے۔ یہ اب بھی کال شدہ API کو تبدیل کیے بغیر کسی ویلیو کو فریز کرنے کا بہترین طریقہ ہے۔
آگے کیا نظر آئے گا
یہ فیچر ابھی نیا ہے، اس لیے ٹولنگ (tooling) اور کمیونٹی کے پیٹرنز ارتقاء پذیر ہیں۔ IDE سپورٹ میں ایسی اپ ڈیٹس کی توقع رکھیں جو autocomplete اور quick-fix تجاویز میں نئے سنٹیکس کو ظاہر کریں گی۔ لائبریری کے مینٹینرز پر نظر رکھیں: بہت سے لوگ پبلک generics کو const parameters میں منتقل کرنا شروع کر دیں گے، جس سے اس کوڈ میں breaking changes آ سکتے ہیں جو پہلے واضح as const casts پر انحصار کرتا تھا۔
خلاصہ (Takeaway): literal preservation کو براہ راست فنکشن کے type parameters میں شامل کر کے، TypeScript کے const type parameters widening errors کے ایک عام ذریعے کو ختم کرتے ہیں اور safety کی ذمہ داری کالر سے واپس API ڈیزائنر کو منتقل کر دیتے ہیں۔ انہیں اپنے کسی بھی generic entry point کے لیے استعمال کریں؛ as const کو لوکل ویلیوز یا بیرونی APIs کے لیے محفوظ رکھیں۔
