فارسی

انواع لیترال در TypeScript، یک ویژگی قدرتمند برای اعمال محدودیت‌های دقیق مقادیر، بهبود خوانایی کد و جلوگیری از خطاها را کاوش کنید. با مثال‌های عملی و تکنیک‌های پیشرفته بیاموزید.

انواع لیترال در TypeScript: تسلط بر محدودیت‌های دقیق مقادیر

تایپ‌اسکریپت، که یک ابرمجموعه از جاوااسکریپت است، تایپ‌دهی استاتیک را به دنیای پویای توسعه وب می‌آورد. یکی از قدرتمندترین ویژگی‌های آن مفهوم انواع لیترال (literal types) است. انواع لیترال به شما اجازه می‌دهند مقدار دقیقی را که یک متغیر یا ویژگی می‌تواند نگه دارد، مشخص کنید، که این امر ایمنی نوع را افزایش داده و از خطاهای غیرمنتظره جلوگیری می‌کند. این مقاله به طور عمیق به بررسی انواع لیترال می‌پردازد و سینتکس، کاربردها و مزایای آن‌ها را با مثال‌های عملی پوشش می‌دهد.

انواع لیترال چه هستند؟

برخلاف انواع سنتی مانند string، number یا boolean، انواع لیترال دسته‌بندی گسترده‌ای از مقادیر را نشان نمی‌دهند. در عوض، آنها مقادیر ثابت و مشخصی را نمایندگی می‌کنند. تایپ‌اسکریپت از سه نوع لیترال پشتیبانی می‌کند:

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

انواع لیترال رشته‌ای

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

سینتکس پایه

سینتکس تعریف یک نوع لیترال رشته‌ای ساده است:


type AllowedValues = "value1" | "value2" | "value3";

این یک نوع به نام AllowedValues تعریف می‌کند که فقط می‌تواند رشته‌های "value1"، "value2" یا "value3" را در خود نگه دارد.

مثال‌های عملی

۱. تعریف یک پالت رنگ:

تصور کنید در حال ساخت یک کتابخانه رابط کاربری هستید و می‌خواهید اطمینان حاصل کنید که کاربران فقط می‌توانند رنگ‌ها را از یک پالت از پیش تعریف شده مشخص کنند:


type Color = "red" | "green" | "blue" | "yellow";

function paintElement(element: HTMLElement, color: Color) {
  element.style.backgroundColor = color;
}

paintElement(document.getElementById("myElement")!, "red"); // معتبر
paintElement(document.getElementById("myElement")!, "purple"); // خطا: آرگومان از نوع '"purple"' قابل تخصیص به پارامتر از نوع 'Color' نیست.

این مثال نشان می‌دهد که چگونه انواع لیترال رشته‌ای می‌توانند مجموعه‌ای دقیق از مقادیر مجاز را اعمال کنند و از استفاده تصادفی توسعه‌دهندگان از رنگ‌های نامعتبر جلوگیری کنند.

۲. تعریف اندپوینت‌های API:

هنگام کار با APIها، اغلب نیاز دارید که اندپوینت‌های مجاز را مشخص کنید. انواع لیترال رشته‌ای می‌توانند به اعمال این محدودیت کمک کنند:


type APIEndpoint = "/users" | "/posts" | "/comments";

function fetchData(endpoint: APIEndpoint) {
  // ... پیاده‌سازی برای دریافت داده از اندپوینت مشخص شده
  console.log(`در حال دریافت داده از ${endpoint}`);
}

fetchData("/users"); // معتبر
fetchData("/products"); // خطا: آرگومان از نوع '"/products"' قابل تخصیص به پارامتر از نوع 'APIEndpoint' نیست.

این مثال تضمین می‌کند که تابع fetchData فقط با اندپوینت‌های معتبر API فراخوانی شود و خطر خطاهای ناشی از غلط‌های املایی یا نام‌های نادرست اندپوینت را کاهش می‌دهد.

۳. مدیریت زبان‌های مختلف (بین‌المللی‌سازی - i18n):

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


type Language = "en" | "es" | "fr" | "de" | "zh";

function translate(text: string, language: Language): string {
  // ... پیاده‌سازی برای ترجمه متن به زبان مشخص شده
  console.log(`در حال ترجمه '${text}' به ${language}`);
  return "متن ترجمه شده"; // مقدار موقت
}

translate("Hello", "en"); // معتبر
translate("Hello", "ja"); // خطا: آرگومان از نوع '"ja"' قابل تخصیص به پارامتر از نوع 'Language' نیست.

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

انواع لیترال عددی

انواع لیترال عددی به شما اجازه می‌دهند مشخص کنید که یک متغیر یا ویژگی فقط می‌تواند یک مقدار عددی خاص را در خود نگه دارد.

سینتکس پایه

سینتکس تعریف یک نوع لیترال عددی شبیه به انواع لیترال رشته‌ای است:


type StatusCode = 200 | 404 | 500;

این یک نوع به نام StatusCode تعریف می‌کند که فقط می‌تواند اعداد 200، 404 یا 500 را در خود نگه دارد.

مثال‌های عملی

۱. تعریف کدهای وضعیت HTTP:

می‌توانید از انواع لیترال عددی برای نمایش کدهای وضعیت HTTP استفاده کنید و اطمینان حاصل کنید که فقط کدهای معتبر در برنامه شما استفاده می‌شوند:


type HTTPStatus = 200 | 400 | 401 | 403 | 404 | 500;

function handleResponse(status: HTTPStatus) {
  switch (status) {
    case 200:
      console.log("موفقیت!");
      break;
    case 400:
      console.log("درخواست نامعتبر");
      break;
    // ... سایر موارد
    default:
      console.log("وضعیت نامشخص");
  }
}

handleResponse(200); // معتبر
handleResponse(600); // خطا: آرگومان از نوع '600' قابل تخصیص به پارامتر از نوع 'HTTPStatus' نیست.

این مثال استفاده از کدهای وضعیت HTTP معتبر را اعمال می‌کند و از خطاهای ناشی از استفاده از کدهای نادرست یا غیراستاندارد جلوگیری می‌کند.

۲. نمایش گزینه‌های ثابت:

می‌توانید از انواع لیترال عددی برای نمایش گزینه‌های ثابت در یک شیء پیکربندی استفاده کنید:


type RetryAttempts = 1 | 3 | 5;

interface Config {
  retryAttempts: RetryAttempts;
}

const config1: Config = { retryAttempts: 3 }; // معتبر
const config2: Config = { retryAttempts: 7 }; // خطا: نوع '{ retryAttempts: 7; }' قابل تخصیص به نوع 'Config' نیست.

این مثال مقادیر ممکن برای retryAttempts را به مجموعه‌ای خاص محدود می‌کند و خوانایی و قابلیت اطمینان پیکربندی شما را بهبود می‌بخشد.

انواع لیترال بولی

انواع لیترال بولی مقادیر خاص true یا false را نمایندگی می‌کنند. اگرچه ممکن است نسبت به انواع لیترال رشته‌ای یا عددی کمتر انعطاف‌پذیر به نظر برسند، اما در سناریوهای خاصی می‌توانند مفید باشند.

سینتکس پایه

سینتکس تعریف یک نوع لیترال بولی به این صورت است:


type IsEnabled = true | false;

با این حال، استفاده مستقیم از true | false اضافی است زیرا معادل نوع boolean است. انواع لیترال بولی زمانی مفیدتر هستند که با انواع دیگر یا در انواع شرطی ترکیب شوند.

مثال‌های عملی

۱. منطق شرطی با پیکربندی:

می‌توانید از انواع لیترال بولی برای کنترل رفتار یک تابع بر اساس یک فلگ پیکربندی استفاده کنید:


interface FeatureFlags {
  darkMode: boolean;
  newUserFlow: boolean;
}

function initializeApp(flags: FeatureFlags) {
  if (flags.darkMode) {
    // فعال کردن حالت تاریک
    console.log("در حال فعال‌سازی حالت تاریک...");
  } else {
    // استفاده از حالت روشن
    console.log("در حال استفاده از حالت روشن...");
  }

  if (flags.newUserFlow) {
    // فعال کردن جریان کاربری جدید
    console.log("در حال فعال‌سازی جریان کاربری جدید...");
  } else {
    // استفاده از جریان کاربری قدیمی
    console.log("در حال استفاده از جریان کاربری قدیمی...");
  }
}

initializeApp({ darkMode: true, newUserFlow: false });

در حالی که این مثال از نوع استاندارد boolean استفاده می‌کند، شما می‌توانید آن را با انواع شرطی (که بعداً توضیح داده می‌شود) ترکیب کنید تا رفتار پیچیده‌تری ایجاد کنید.

۲. Unionهای تفکیک‌شده (Discriminated Unions):

انواع لیترال بولی می‌توانند به عنوان تفکیک‌کننده در انواع union استفاده شوند. مثال زیر را در نظر بگیرید:


interface SuccessResult {
  success: true;
  data: any;
}

interface ErrorResult {
  success: false;
  error: string;
}

type Result = SuccessResult | ErrorResult;

function processResult(result: Result) {
  if (result.success) {
    console.log("موفقیت:", result.data);
  } else {
    console.error("خطا:", result.error);
  }
}

processResult({ success: true, data: { name: "John" } });
processResult({ success: false, error: "دریافت داده ناموفق بود" });

در اینجا، ویژگی success که یک نوع لیترال بولی است، به عنوان یک تفکیک‌کننده عمل می‌کند و به تایپ‌اسکریپت اجازه می‌دهد تا نوع result را در داخل عبارت if محدود کند.

ترکیب انواع لیترال با انواع Union

انواع لیترال زمانی قدرتمندتر هستند که با انواع union (با استفاده از عملگر |) ترکیب شوند. این به شما امکان می‌دهد نوعی را تعریف کنید که می‌تواند یکی از چندین مقدار خاص را در خود نگه دارد.

مثال‌های عملی

۱. تعریف یک نوع وضعیت:


type Status = "pending" | "in progress" | "completed" | "failed";

interface Task {
  id: number;
  description: string;
  status: Status;
}

const task1: Task = { id: 1, description: "پیاده‌سازی ورود", status: "in progress" }; // معتبر
const task2: Task = { id: 2, description: "پیاده‌سازی خروج", status: "done" };       // خطا: نوع '{ id: number; description: string; status: string; }' قابل تخصیص به نوع 'Task' نیست.

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

۲. تعریف یک نوع دستگاه:

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


type DeviceType = "mobile" | "tablet" | "desktop";

function logDeviceType(device: DeviceType) {
  console.log(`نوع دستگاه: ${device}`);
}

logDeviceType("mobile"); // معتبر
logDeviceType("smartwatch"); // خطا: آرگومان از نوع '"smartwatch"' قابل تخصیص به پارامتر از نوع 'DeviceType' نیست.

این مثال تضمین می‌کند که تابع logDeviceType فقط با انواع دستگاه معتبر فراخوانی می‌شود.

انواع لیترال با نام‌های مستعار نوع (Type Aliases)

نام‌های مستعار نوع (با استفاده از کلمه کلیدی type) راهی برای نام‌گذاری یک نوع لیترال فراهم می‌کنند که کد شما را خواناتر و قابل نگهداری‌تر می‌کند.

مثال‌های عملی

۱. تعریف یک نوع کد ارز:


type CurrencyCode = "USD" | "EUR" | "GBP" | "JPY";

function formatCurrency(amount: number, currency: CurrencyCode): string {
  // ... پیاده‌سازی برای قالب‌بندی مبلغ بر اساس کد ارز
  console.log(`در حال قالب‌بندی ${amount} در ${currency}`);
  return "مبلغ قالب‌بندی شده"; // مقدار موقت
}

formatCurrency(100, "USD"); // معتبر
formatCurrency(200, "CAD"); // خطا: آرگومان از نوع '"CAD"' قابل تخصیص به پارامتر از نوع 'CurrencyCode' نیست.

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

۲. تعریف یک نوع روز هفته:


type DayOfWeek = "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday" | "Saturday" | "Sunday";

function isWeekend(day: DayOfWeek): boolean {
  return day === "Saturday" || day === "Sunday";
}

console.log(isWeekend("Monday"));   // false
console.log(isWeekend("Saturday")); // true
console.log(isWeekend("Funday"));   // خطا: آرگومان از نوع '"Funday"' قابل تخصیص به پارامتر از نوع 'DayOfWeek' نیست.

استنتاج لیترال (Literal Inference)

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

مثال‌های عملی

۱. استنتاج انواع لیترال رشته‌ای:


const apiKey = "your-api-key"; // تایپ‌اسکریپت نوع apiKey را به عنوان "your-api-key" استنتاج می‌کند

function validateApiKey(key: "your-api-key") {
  return key === "your-api-key";
}

console.log(validateApiKey(apiKey)); // true

const anotherKey = "invalid-key";
console.log(validateApiKey(anotherKey)); // خطا: آرگومان از نوع 'string' قابل تخصیص به پارامتر از نوع '"your-api-key"' نیست.

در این مثال، تایپ‌اسکریپت نوع apiKey را به عنوان نوع لیترال رشته‌ای "your-api-key" استنتاج می‌کند. با این حال، اگر یک مقدار غیر ثابت را به یک متغیر اختصاص دهید، تایپ‌اسکریپت معمولاً نوع گسترده‌تر string را استنتاج می‌کند.

۲. استنتاج انواع لیترال عددی:


const port = 8080; // تایپ‌اسکریپت نوع port را به عنوان 8080 استنتاج می‌کند

function startServer(portNumber: 8080) {
  console.log(`در حال شروع سرور روی پورت ${portNumber}`);
}

startServer(port); // معتبر

const anotherPort = 3000;
startServer(anotherPort); // خطا: آرگومان از نوع 'number' قابل تخصیص به پارامتر از نوع '8080' نیست.

استفاده از انواع لیترال با انواع شرطی

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

سینتکس پایه

سینتکس یک نوع شرطی به این صورت است:


TypeA extends TypeB ? TypeC : TypeD

این به این معنی است: اگر TypeA قابل تخصیص به TypeB باشد، نوع حاصل TypeC است؛ در غیر این صورت، نوع حاصل TypeD خواهد بود.

مثال‌های عملی

۱. نگاشت وضعیت به پیام:


type Status = "pending" | "in progress" | "completed" | "failed";

type StatusMessage = T extends "pending"
  ? "در انتظار اقدام"
  : T extends "in progress"
  ? "در حال پردازش"
  : T extends "completed"
  ? "وظیفه با موفقیت به پایان رسید"
  : "خطایی رخ داده است";

function getStatusMessage(status: T): StatusMessage {
  switch (status) {
    case "pending":
      return "در انتظار اقدام" as StatusMessage;
    case "in progress":
      return "در حال پردازش" as StatusMessage;
    case "completed":
      return "وظیفه با موفقیت به پایان رسید" as StatusMessage;
    case "failed":
      return "خطایی رخ داده است" as StatusMessage;
    default:
      throw new Error("وضعیت نامعتبر");
  }
}

console.log(getStatusMessage("pending"));    // در انتظار اقدام
console.log(getStatusMessage("in progress")); // در حال پردازش
console.log(getStatusMessage("completed"));   // وظیفه با موفقیت به پایان رسید
console.log(getStatusMessage("failed"));      // خطایی رخ داده است

این مثال یک نوع StatusMessage را تعریف می‌کند که هر وضعیت ممکن را با استفاده از انواع شرطی به یک پیام متناظر نگاشت می‌کند. تابع getStatusMessage از این نوع برای ارائه پیام‌های وضعیت با ایمنی نوع استفاده می‌کند.

۲. ایجاد یک کنترل‌کننده رویداد با ایمنی نوع:


type EventType = "click" | "mouseover" | "keydown";

type EventData = T extends "click"
  ? { x: number; y: number; } // داده‌های رویداد کلیک
  : T extends "mouseover"
  ? { target: HTMLElement; }   // داده‌های رویداد ماوس‌اور
  : { key: string; }             // داده‌های رویداد کی‌داون

function handleEvent(type: T, data: EventData) {
  console.log(`در حال مدیریت رویداد نوع ${type} با داده:`, data);
}

handleEvent("click", { x: 10, y: 20 }); // معتبر
handleEvent("mouseover", { target: document.getElementById("myElement")! }); // معتبر
handleEvent("keydown", { key: "Enter" }); // معتبر

handleEvent("click", { key: "Enter" }); // خطا: آرگومان از نوع '{ key: string; }' قابل تخصیص به پارامتر از نوع '{ x: number; y: number; }' نیست.

این مثال یک نوع EventData ایجاد می‌کند که ساختارهای داده متفاوتی را بر اساس نوع رویداد تعریف می‌کند. این به شما امکان می‌دهد اطمینان حاصل کنید که برای هر نوع رویداد، داده‌های صحیح به تابع handleEvent ارسال می‌شود.

بهترین شیوه‌ها برای استفاده از انواع لیترال

برای استفاده مؤثر از انواع لیترال در پروژه‌های تایپ‌اسکریپت خود، بهترین شیوه‌های زیر را در نظر بگیرید:

مزایای استفاده از انواع لیترال

نتیجه‌گیری

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