تیم TypeScript در نسخه ۶.۰ یک پرچم کامپایلر جدید را عرضه کرده است: --noPropertyAccessFromIndexSignature. وقتی این پرچم فعال باشد، کامپایلر اجازه دسترسی به ویژگی‌ها (properties) از طریق dot-notation را برای ویژگی‌هایی که از یک امضای ایندکس (index signature) می‌آیند نمی‌دهد و توسعه‌دهندگان را مجبور می‌کند از bracket notation استفاده کنند. این کار باعث می‌شود مقادیر احتمالی undefined به جای زمان اجرا، در زمان کامپایل شناسایی شوند.

چرا این پرچم اهمیت دارد

در JavaScript، اشیاء اغلب به عنوان دیکشنری عمل می‌کنند و TypeScript به شما اجازه می‌دهد چنین ساختارهایی را با یک امضای ایندکس، مثلاً Record<string, T>، تایپ‌گذاری کنید. این زبان obj.key و obj["key"] را یکسان در نظر می‌گیرد، بنابراین کامپایلر فرض می‌کند که ویژگی وجود دارد، حتی زمانی که کلید فقط در زمان اجرا مشخص می‌شود. این فرض خاموش، منشأ بسیاری از کرش‌هاست: کدی که به obj.missingProp دسترسی پیدا می‌کند، بدون مشکل کامپایل و اجرا می‌شود، اما سپس به دلیل undefined بودن مقدار، خطا می‌دهد.

Dot notation یک تضمین ضمنی به همراه دارد؛ به خوانندگان و بررسی‌کننده‌ی تایپ می‌گوید که آن ویژگی قطعاً وجود دارد. در مقابل، bracket notation نشان‌دهنده‌ی عدم قطعیت است؛ ممکن است کلید وجود نداشته باشد و نتیجه می‌تواند undefined باشد. پرچم --noPropertyAccessFromIndexSignature این تمایز بصری و معنایی را اعمال می‌کند و یک دسته‌ی از خطاهای زمان اجرا را به تشخیص‌های زمان کامپایل تبدیل می‌کند.

این پرچم چگونه کار می‌کند

وقتی این پرچم فعال باشد، هر عبارتی که از طریق dot notation به ویژگی‌ای از طریق یک امضای ایندکس دسترسی پیدا می‌کند، به عنوان خطا علامت‌گذاری می‌شود. کد باید برای استفاده از کروشه بازنویسی شود:

// Before
const name = userData.name;          // OK even if "name" is not in the index

// After enabling the flag
const name = userData["name"];       // Error unless brackets are used

سپس کامپایلر همان قوانین مدیریت undefined را که قبلاً برای دسترسی کروشه‌ای استفاده می‌کرد، اعمال می‌کند. اگر --noUncheckedIndexedAccess نیز فعال باشد، تایپ userData["name"] به T | undefined تبدیل می‌شود و توسعه‌دهنده را مجبور می‌کند که حالت نبودن مقدار را بررسی کند.

مراحل عملی مهاجرت

۱. فعال کردن پرچم در tsconfig.json:

{
  "compilerOptions": {
    "noPropertyAccessFromIndexSignature": true
  }
}

۲. اجرای بررسی‌کننده‌ی تایپ. تمام دسترسی‌های dot-notation به کلیدهای امضای ایندکس به عنوان خطا ظاهر خواهند شد.

۳. جایگزینی نقاط با کروشه. این تغییر مکانیکی است و بر عملکرد زمان اجرا تأثیری ندارد.

۴. رسیدگی به تایپ‌های undefined حاصل. در صورت نیاز، از nullish coalescing، optional chaining یا بررسی‌های صریح استفاده کنید.

۵. در نظر گرفتن ترکیب با --noUncheckedIndexedAccess برای داشتن قوی‌ترین شبکه ایمنی. این دو با هم تضمین می‌کنند که هر دسترسی به سبک دیکشنری، به عنوان دسترسی به مقداری که احتمالاً وجود ندارد، در نظر گرفته شود.

چه زمانی ویژگی‌های صریح را حفظ کنیم

اگر یک فیلد بخشی از یک قرارداد API پایدار است، آن را به جای تکیه بر یک امضای ایندکس، به عنوان یک ویژگی صریح (explicit property) تعریف کنید. ویژگی‌های صریح همچنان اجازه استفاده از dot notation را می‌دهند و این تضمین را حفظ می‌کنند که فیلد همیشه وجود خواهد داشت (تا جایی که سیستم تایپ می‌تواند تأیید کند). امضاهای ایندکس را برای داده‌های واقعاً پویا که کلیدهای آن‌ها از قبل مشخص نیست، رزرو کنید.

نکته مقابل: افزایش شلوغی کد

برخی تیم‌ها ممکن است کروشه‌های اضافی را آزاردهنده بدانند، به‌ویژه در کدهایی که به شدت از اشیاء منعطف استفاده می‌کنند. این پرچم انضباط سخت‌گیرانه‌تری را تحمیل می‌کند که می‌تواند مستلزم بازنویسی (refactor) قابل توجهی برای پروژه‌های قدیمی باشد. برای این موارد، می‌توان پرچم را به صورت تدریجی معرفی کرد، مثلاً محدود به ماژول‌های جدید، در حالی که کل کد به مرور زمان این الگو را می‌پذیرد.

آنچه باید در آینده زیر نظر داشت

این پرچم بخشی از یک حرکت گسترده‌تر در TypeScript 6.0 به سمت ایمنی تایپ (type safety) سخت‌گیرانه‌تر است. نسخه‌های آینده ممکن است بررسی‌های اضافی را در مورد object spread، optional chaining یا استفاده از any استنتاج‌شده معرفی کنند. زیر نظر داشتن نقشه راه (roadmap) TypeScript به تیم‌ها کمک می‌کند تا تصمیم بگیرند چه زمانی مجموعه‌ی بعدی ویژگی‌های ایمنی را بدون مختل کردن برنامه‌های تحویل پروژه، اتخاذ کنند.

نتیجه‌گیری: فعال کردن --noPropertyAccessFromIndexSignature تمایز بین «این ویژگی تضمین شده است» و «این ویژگی ممکن است وجود نداشته باشد» را در کد صریح می‌کند و یک دسته‌ی کامل از باگ‌ها را قبل از رسیدن به مرحله تولید شناسایی می‌کند. تبدیل یک شکست خاموش در زمان اجرا به یک خطای زمان کامپایل، تغییری کوچک با تأثیری بسیار بزرگ بر قابلیت اطمینان است.