فارسی

راهنمای جامع امضاهای ایندکس تایپ‌اسکریپت برای دسترسی پویا به خصوصیات، ایمنی نوع و ساختارهای داده انعطاف‌پذیر در توسعه نرم‌افزار بین‌المللی.

امضاهای ایندکس در تایپ‌اسکریپت: تسلط بر دسترسی پویا به خصوصیات

در دنیای توسعه نرم‌افزار، انعطاف‌پذیری و ایمنی نوع (type safety) اغلب به عنوان دو نیروی متضاد دیده می‌شوند. تایپ‌اسکریپت، که یک ابرمجموعه از جاوااسکریپت است، به زیبایی این شکاف را پر می‌کند و ویژگی‌هایی را ارائه می‌دهد که هر دو را تقویت می‌کنند. یکی از این ویژگی‌های قدرتمند امضاهای ایندکس است. این راهنمای جامع به پیچیدگی‌های امضاهای ایندکس تایپ‌اسکریپت می‌پردازد و توضیح می‌دهد که چگونه آن‌ها دسترسی پویا به خصوصیات را ضمن حفظ بررسی نوع قوی امکان‌پذیر می‌کنند. این امر به ویژه برای برنامه‌هایی که با داده‌هایی از منابع و فرمت‌های متنوع در سطح جهانی تعامل دارند، حیاتی است.

امضاهای ایندکس در تایپ‌اسکریپت چیستند؟

امضاهای ایندکس روشی برای توصیف انواع خصوصیات یک شیء ارائه می‌دهند، زمانی که نام خصوصیات را از قبل نمی‌دانید یا زمانی که نام خصوصیات به صورت پویا تعیین می‌شود. آن‌ها را به عنوان راهی برای گفتن این جمله در نظر بگیرید: «این شیء می‌تواند هر تعداد خصوصیت از این نوع خاص را داشته باشد.» آن‌ها در یک اینترفیس یا type alias با استفاده از سینتکس زیر تعریف می‌شوند:


interface MyInterface {
  [index: string]: number;
}

در این مثال، [index: string]: number امضای ایندکس است. بیایید اجزای آن را بررسی کنیم:

بنابراین، MyInterface یک شیء را توصیف می‌کند که در آن هر خصوصیت رشته‌ای (مانند "age"، "count"، "user123") باید یک مقدار عددی داشته باشد. این امر هنگام کار با داده‌هایی که کلیدهای دقیق آن‌ها از قبل مشخص نیست، انعطاف‌پذیری را فراهم می‌کند، که در سناریوهای مربوط به APIهای خارجی یا محتوای تولید شده توسط کاربر رایج است.

چرا از امضاهای ایندکس استفاده کنیم؟

امضاهای ایندکس در سناریوهای مختلفی ارزشمند هستند. در اینجا برخی از مزایای کلیدی آورده شده است:

امضاهای ایندکس در عمل: مثال‌های کاربردی

بیایید چند مثال کاربردی را برای نشان دادن قدرت امضاهای ایندکس بررسی کنیم.

مثال ۱: نمایش یک دیکشنری از رشته‌ها

تصور کنید نیاز به نمایش یک دیکشنری دارید که در آن کلیدها کدهای کشور (مانند "US", "CA", "GB") و مقادیر نام کشورها هستند. می‌توانید از یک امضای ایندکس برای تعریف نوع استفاده کنید:


interface CountryDictionary {
  [code: string]: string; // کلید کد کشور (رشته) است، مقدار نام کشور (رشته) است
}

const countries: CountryDictionary = {
  "US": "United States",
  "CA": "Canada",
  "GB": "United Kingdom",
  "DE": "Germany"
};

console.log(countries["US"]); // خروجی: United States

// خطا: نوع 'number' قابل اختصاص به نوع 'string' نیست.
// countries["FR"] = 123; 

این مثال نشان می‌دهد که چگونه امضای ایندکس اطمینان حاصل می‌کند که همه مقادیر باید رشته باشند. تلاش برای اختصاص یک عدد به یک کد کشور منجر به خطای نوع می‌شود.

مثال ۲: مدیریت پاسخ‌های API

یک API را در نظر بگیرید که پروفایل‌های کاربران را برمی‌گرداند. این API ممکن است شامل فیلدهای سفارشی باشد که از کاربری به کاربر دیگر متفاوت است. می‌توانید از یک امضای ایندکس برای نمایش این فیلدهای سفارشی استفاده کنید:


interface UserProfile {
  id: number;
  name: string;
  email: string;
  [key: string]: any; // اجازه دادن به هر خصوصیت رشته‌ای دیگر با هر نوعی
}

const user: UserProfile = {
  id: 123,
  name: "Alice",
  email: "alice@example.com",
  customField1: "Value 1",
  customField2: 42,
};

console.log(user.name); // خروجی: Alice
console.log(user.customField1); // خروجی: Value 1

در این حالت، امضای ایندکس [key: string]: any به اینترفیس UserProfile اجازه می‌دهد تا هر تعداد خصوصیت رشته‌ای اضافی با هر نوعی داشته باشد. این امر انعطاف‌پذیری را فراهم می‌کند در حالی که همچنان اطمینان می‌دهد که خصوصیات id، name و email به درستی تایپ شده‌اند. با این حال، استفاده از `any` باید با احتیاط انجام شود، زیرا ایمنی نوع را کاهش می‌دهد. در صورت امکان، از یک نوع مشخص‌تر استفاده کنید.

مثال ۳: اعتبارسنجی پیکربندی پویا

فرض کنید یک شیء پیکربندی دارید که از یک منبع خارجی بارگیری شده است. می‌توانید از امضاهای ایندکس برای اعتبارسنجی اینکه مقادیر پیکربندی با انواع مورد انتظار مطابقت دارند، استفاده کنید:


interface Config {
  [key: string]: string | number | boolean;
}

const config: Config = {
  apiUrl: "https://api.example.com",
  timeout: 5000,
  debugMode: true,
};

function validateConfig(config: Config): void {
  if (typeof config.timeout !== 'number') {
    console.error("Invalid timeout value");
  }
  // اعتبارسنجی بیشتر...
}

validateConfig(config);

در اینجا، امضای ایندکس اجازه می‌دهد مقادیر پیکربندی رشته، عدد یا بولین باشند. تابع validateConfig سپس می‌تواند بررسی‌های بیشتری را برای اطمینان از معتبر بودن مقادیر برای استفاده مورد نظر خود انجام دهد.

امضاهای ایندکس رشته‌ای در مقابل عددی

همانطور که قبلاً ذکر شد، تایپ‌اسکریپت هم از امضاهای ایندکس string و هم number پشتیبانی می‌کند. درک تفاوت‌های آن‌ها برای استفاده مؤثر از آنها حیاتی است.

امضاهای ایندکس رشته‌ای

امضاهای ایندکس رشته‌ای به شما اجازه می‌دهند با استفاده از کلیدهای رشته‌ای به خصوصیات دسترسی پیدا کنید. این رایج‌ترین نوع امضای ایندکس است و برای نمایش اشیائی که نام خصوصیات آن‌ها رشته است، مناسب است.


interface StringDictionary {
  [key: string]: any;
}

const data: StringDictionary = {
  name: "John",
  age: 30,
  city: "New York"
};

console.log(data["name"]); // خروجی: John

امضاهای ایندکس عددی

امضاهای ایندکس عددی به شما اجازه می‌دهند با استفاده از کلیدهای عددی به خصوصیات دسترسی پیدا کنید. این معمولاً برای نمایش آرایه‌ها یا اشیاء شبه آرایه استفاده می‌شود. در تایپ‌اسکریپت، اگر یک امضای ایندکس عددی تعریف کنید، نوع ایندکسر عددی باید زیرمجموعه‌ای از نوع ایندکسر رشته‌ای باشد.


interface NumberArray {
  [index: number]: string;
}

const myArray: NumberArray = [
  "apple",
  "banana",
  "cherry"
];

console.log(myArray[0]); // خروجی: apple

نکته مهم: هنگام استفاده از امضاهای ایندکس عددی، تایپ‌اسکریپت به طور خودکار اعداد را هنگام دسترسی به خصوصیات به رشته تبدیل می‌کند. این بدان معناست که myArray[0] معادل myArray["0"] است.

تکنیک‌های پیشرفته امضای ایندکس

فراتر از اصول اولیه، می‌توانید از امضاهای ایندکس با سایر ویژگی‌های تایپ‌اسکریپت برای ایجاد تعاریف نوع قدرتمندتر و انعطاف‌پذیرتر استفاده کنید.

ترکیب امضاهای ایندکس با خصوصیات خاص

شما می‌توانید امضاهای ایندکس را با خصوصیات تعریف شده به صراحت در یک اینترفیس یا type alias ترکیب کنید. این به شما امکان می‌دهد خصوصیات مورد نیاز را به همراه خصوصیات اضافه شده به صورت پویا تعریف کنید.


interface Product {
  id: number;
  name: string;
  price: number;
  [key: string]: any; // اجازه به خصوصیات اضافی از هر نوع
}

const product: Product = {
  id: 123,
  name: "Laptop",
  price: 999.99,
  description: "High-performance laptop",
  warranty: "2 years"
};

در این مثال، اینترفیس Product به خصوصیات id، name و price نیاز دارد در حالی که از طریق امضای ایندکس به خصوصیات اضافی نیز اجازه می‌دهد.

استفاده از جنریک‌ها با امضاهای ایندکس

جنریک‌ها راهی برای ایجاد تعاریف نوع قابل استفاده مجدد ارائه می‌دهند که می‌توانند با انواع مختلف کار کنند. شما می‌توانید از جنریک‌ها با امضاهای ایندکس برای ایجاد ساختارهای داده جنریک استفاده کنید.


interface Dictionary {
  [key: string]: T;
}

const stringDictionary: Dictionary = {
  name: "John",
  city: "New York"
};

const numberDictionary: Dictionary = {
  age: 30,
  count: 100
};

در اینجا، اینترفیس Dictionary یک تعریف نوع جنریک است که به شما امکان می‌دهد دیکشنری‌هایی با انواع مقادیر مختلف ایجاد کنید. این کار از تکرار تعریف امضای ایندکس یکسان برای انواع داده‌های مختلف جلوگیری می‌کند.

امضاهای ایندکس با انواع Union

شما می‌توانید از انواع union با امضاهای ایندکس استفاده کنید تا به خصوصیات اجازه دهید انواع مختلفی داشته باشند. این امر هنگام کار با داده‌هایی که می‌توانند چندین نوع ممکن داشته باشند، مفید است.


interface MixedData {
  [key: string]: string | number | boolean;
}

const mixedData: MixedData = {
  name: "John",
  age: 30,
  isActive: true
};

در این مثال، اینترفیس MixedData به خصوصیات اجازه می‌دهد که رشته، عدد یا بولین باشند.

امضاهای ایندکس با انواع Literal

شما می‌توانید از انواع literal برای محدود کردن مقادیر ممکن ایندکس استفاده کنید. این می‌تواند زمانی مفید باشد که می‌خواهید مجموعه‌ای خاص از نام‌های خصوصیات مجاز را اعمال کنید.


type AllowedKeys = "name" | "age" | "city";

interface RestrictedData {
  [key in AllowedKeys]: string | number;
}

const restrictedData: RestrictedData = {
  name: "John",
  age: 30,
  city: "New York"
};

این مثال از یک نوع literal به نام AllowedKeys برای محدود کردن نام‌های خصوصیات به "name"، "age" و "city" استفاده می‌کند. این امر بررسی نوع سخت‌گیرانه‌تری را در مقایسه با یک ایندکس string عمومی فراهم می‌کند.

استفاده از نوع کاربردی `Record`

تایپ‌اسکریپت یک نوع کاربردی داخلی به نام `Record` ارائه می‌دهد که اساساً یک شکل کوتاه برای تعریف یک امضای ایندکس با یک نوع کلید و نوع مقدار خاص است.


// معادل با: { [key: string]: number }
const recordExample: Record = {
  a: 1,
  b: 2,
  c: 3
};

// معادل با: { [key in 'x' | 'y']: boolean }
const xyExample: Record<'x' | 'y', boolean> = {
  x: true,
  y: false
};

نوع `Record` سینتکس را ساده کرده و خوانایی را هنگامی که به یک ساختار شبه‌دیکشنری پایه نیاز دارید، بهبود می‌بخشد.

استفاده از انواع Mapped با امضاهای ایندکس

انواع Mapped به شما امکان می‌دهند خصوصیات یک نوع موجود را تغییر دهید. آنها می‌توانند در ترکیب با امضاهای ایندکس برای ایجاد انواع جدید بر اساس انواع موجود استفاده شوند.


interface Person {
  name: string;
  age: number;
  email?: string; // خصوصیت اختیاری
}

// تمام خصوصیات Person را الزامی می‌کند
type RequiredPerson = { [K in keyof Person]-?: Person[K] };

const requiredPerson: RequiredPerson = {
  name: "Alice",
  age: 30,   // Email اکنون الزامی است.
  email: "alice@example.com" 
};

در این مثال، نوع RequiredPerson از یک نوع mapped با یک امضای ایندکس استفاده می‌کند تا تمام خصوصیات اینترفیس Person را الزامی کند. `-?` اصلاح‌کننده اختیاری را از خصوصیت email حذف می‌کند.

بهترین شیوه‌ها برای استفاده از امضاهای ایندکس

در حالی که امضاهای ایندکس انعطاف‌پذیری زیادی را ارائه می‌دهند، مهم است که از آنها با دقت برای حفظ ایمنی نوع و وضوح کد استفاده کنید. در اینجا برخی از بهترین شیوه‌ها آورده شده است:

اشتباهات رایج و نحوه اجتناب از آنها

حتی با درک کامل از امضاهای ایندکس، افتادن در برخی از تله‌های رایج آسان است. در اینجا مواردی است که باید مراقب آنها باشید:

ملاحظات بین‌المللی‌سازی و محلی‌سازی

هنگام توسعه نرم‌افزار برای مخاطبان جهانی، در نظر گرفتن بین‌المللی‌سازی (i18n) و محلی‌سازی (l10n) بسیار مهم است. امضاهای ایندکس می‌توانند در مدیریت داده‌های محلی‌سازی شده نقش داشته باشند.

مثال: متن محلی‌سازی شده

شما ممکن است از امضاهای ایندکس برای نمایش مجموعه‌ای از رشته‌های متنی محلی‌سازی شده استفاده کنید، که در آن کلیدها کدهای زبان (مانند "en", "fr", "de") و مقادیر رشته‌های متنی مربوطه هستند.


interface LocalizedText {
  [languageCode: string]: string;
}

const localizedGreeting: LocalizedText = {
  "en": "Hello",
  "fr": "Bonjour",
  "de": "Hallo"
};

function getGreeting(languageCode: string): string {
  return localizedGreeting[languageCode] || "Hello"; // در صورت عدم یافتن، به انگلیسی پیش‌فرض می‌شود
}

console.log(getGreeting("fr")); // خروجی: Bonjour
console.log(getGreeting("es")); // خروجی: Hello (پیش‌فرض)

این مثال نشان می‌دهد که چگونه می‌توان از امضاهای ایندکس برای ذخیره و بازیابی متن محلی‌سازی شده بر اساس کد زبان استفاده کرد. در صورتی که زبان درخواستی یافت نشود، یک مقدار پیش‌فرض ارائه می‌شود.

نتیجه‌گیری

امضاهای ایندکس تایپ‌اسکریپت یک ابزار قدرتمند برای کار با داده‌های پویا و ایجاد تعاریف نوع انعطاف‌پذیر هستند. با درک مفاهیم و بهترین شیوه‌های ذکر شده در این راهنما، می‌توانید از امضاهای ایندکس برای افزایش ایمنی نوع و سازگاری کد تایپ‌اسکریپت خود استفاده کنید. به یاد داشته باشید که از آنها با دقت استفاده کنید و مشخص بودن و وضوح را برای حفظ کیفیت کد در اولویت قرار دهید. همانطور که سفر تایپ‌اسکریپت خود را ادامه می‌دهید، کاوش در امضاهای ایندکس بدون شک امکانات جدیدی را برای ساخت برنامه‌های قوی و مقیاس‌پذیر برای مخاطبان جهانی باز خواهد کرد. با تسلط بر امضاهای ایندکس، می‌توانید کدی گویاتر، قابل نگهداری‌تر و با ایمنی نوع بالاتر بنویسید و پروژه‌های خود را قوی‌تر و سازگارتر با منابع داده متنوع و نیازهای در حال تحول کنید. قدرت تایپ‌اسکریپت و امضاهای ایندکس آن را برای ساخت نرم‌افزار بهتر، با هم در آغوش بگیرید.