فارسی

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

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

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

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

تایپ‌های کمکی سناریوهای رایج دستکاری تایپ را پوشش می‌دهند. با استفاده از آن‌ها، می‌توانید:

تایپ‌های کمکی اصلی

Partial

Partial تایپی می‌سازد که در آن تمام خصوصیات T به صورت اختیاری (optional) تنظیم شده‌اند. این ویژگی به خصوص زمانی مفید است که می‌خواهید یک تایپ برای به‌روزرسانی‌های جزئی یا اشیاء پیکربندی ایجاد کنید.

مثال:

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


interface Customer {
  id: string;
  firstName: string;
  lastName: string;
  email: string;
  phoneNumber: string;
  address: {
    street: string;
    city: string;
    country: string;
    postalCode: string;
  };
  preferences?: {
    language: string;
    currency: string;
  }
}

هنگام به‌روزرسانی اطلاعات یک مشتری، ممکن است نخواهید همه فیلدها را الزامی کنید. Partial به شما این امکان را می‌دهد که تایپی تعریف کنید که در آن تمام خصوصیات Customer اختیاری هستند:


type PartialCustomer = Partial<Customer>;

function updateCustomer(id: string, updates: PartialCustomer): void {
  // ... پیاده‌سازی برای به‌روزرسانی مشتری با شناسه داده شده
}

updateCustomer("123", { firstName: "John", lastName: "Doe" }); // معتبر
updateCustomer("456", { address: { city: "London" } }); // معتبر

Readonly

Readonly تایپی می‌سازد که در آن تمام خصوصیات T به صورت readonly (فقط-خواندنی) تنظیم شده‌اند و از تغییر آن‌ها پس از مقداردهی اولیه جلوگیری می‌کند. این ویژگی برای تضمین تغییرناپذیری (immutability) ارزشمند است.

مثال:

یک شیء پیکربندی برای برنامه جهانی خود را در نظر بگیرید:


interface AppConfig {
  apiUrl: string;
  theme: string;
  supportedLanguages: string[];
  version: string; // نسخه اضافه شد
}

const config: AppConfig = {
  apiUrl: "https://api.example.com",
  theme: "dark",
  supportedLanguages: ["en", "fr", "de", "es", "zh"],
  version: "1.0.0"
};

برای جلوگیری از تغییر تصادفی پیکربندی پس از مقداردهی اولیه، می‌توانید از Readonly استفاده کنید:


type ReadonlyAppConfig = Readonly<AppConfig>;

const readonlyConfig: ReadonlyAppConfig = {
  apiUrl: "https://api.example.com",
  theme: "dark",
  supportedLanguages: ["en", "fr", "de", "es", "zh"],
  version: "1.0.0"
};

// readonlyConfig.apiUrl = "https://newapi.example.com"; // خطا: نمی‌توان به 'apiUrl' مقدار تخصیص داد زیرا یک خاصیت فقط-خواندنی است.

Pick

Pick تایپی می‌سازد که با انتخاب مجموعه‌ای از خصوصیات K از T ایجاد می‌شود، جایی که K یک union از تایپ‌های string literal است که نام خصوصیات مورد نظر شما را نشان می‌دهد.

مثال:

فرض کنید یک اینترفیس Event با خصوصیات مختلف دارید:


interface Event {
  id: string;
  title: string;
  description: string;
  location: string;
  startTime: Date;
  endTime: Date;
  organizer: string;
  attendees: string[];
}

اگر برای یک کامپوننت نمایشی خاص فقط به title، location و startTime نیاز دارید، می‌توانید از Pick استفاده کنید:


type EventSummary = Pick<Event, "title" | "location" | "startTime">;

function displayEventSummary(event: EventSummary): void {
  console.log(`Event: ${event.title} at ${event.location} on ${event.startTime}`);
}

Omit

Omit تایپی می‌سازد که با حذف مجموعه‌ای از خصوصیات K از T ایجاد می‌شود، جایی که K یک union از تایپ‌های string literal است که نام خصوصیات مورد نظر برای حذف را نشان می‌دهد. این برعکس Pick است.

مثال:

با استفاده از همان اینترفیس Event، اگر می‌خواهید تایپی برای ایجاد رویدادهای جدید بسازید، ممکن است بخواهید خصوصیت id را حذف کنید که معمولاً توسط بک‌اند تولید می‌شود:


type NewEvent = Omit<Event, "id">;

function createEvent(event: NewEvent): void {
  // ... پیاده‌سازی برای ایجاد یک رویداد جدید
}

Record

Record یک تایپ شیء می‌سازد که کلیدهای خصوصیات آن K و مقادیر خصوصیات آن T هستند. K می‌تواند یک union از تایپ‌های string literal، number literal یا symbol باشد. این برای ایجاد دیکشنری‌ها یا مپ‌ها عالی است.

مثال:

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


type Translations = Record<string, string>;

const enTranslations: Translations = {
  "hello": "Hello",
  "goodbye": "Goodbye",
  "welcome": "Welcome to our platform!"
};

const frTranslations: Translations = {
  "hello": "Bonjour",
  "goodbye": "Au revoir",
  "welcome": "Bienvenue sur notre plateforme !"
};

function translate(key: string, language: string): string {
  const translations = language === "en" ? enTranslations : frTranslations; // ساده‌سازی شده
  return translations[key] || key; // اگر ترجمه یافت نشد، به کلید بازگشت داده می‌شود
}

console.log(translate("hello", "en")); // خروجی: Hello
console.log(translate("hello", "fr")); // خروجی: Bonjour
console.log(translate("nonexistent", "en")); // خروجی: nonexistent

Exclude

Exclude تایپی می‌سازد که با حذف تمام اعضای union از T که به U قابل تخصیص هستند، ایجاد می‌شود. این برای فیلتر کردن تایپ‌های خاص از یک union مفید است.

مثال:

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


type EventType = "concert" | "conference" | "workshop" | "webinar";

اگر می‌خواهید تایپی ایجاد کنید که رویدادهای "webinar" را حذف کند، می‌توانید از Exclude استفاده کنید:


type PhysicalEvent = Exclude<EventType, "webinar">;

// PhysicalEvent اکنون برابر است با "concert" | "conference" | "workshop"

function attendPhysicalEvent(event: PhysicalEvent): void {
  console.log(`Attending a ${event}`);
}

// attendPhysicalEvent("webinar"); // خطا: آرگومان از نوع '"webinar"' قابل تخصیص به پارامتر از نوع '"concert" | "conference" | "workshop"' نیست.

attendPhysicalEvent("concert"); // معتبر

Extract

Extract تایپی می‌سازد که با استخراج تمام اعضای union از T که به U قابل تخصیص هستند، ایجاد می‌شود. این برعکس Exclude است.

مثال:

با استفاده از همان EventType، می‌توانید نوع رویداد وبینار را استخراج کنید:


type OnlineEvent = Extract<EventType, "webinar">;

// OnlineEvent اکنون برابر است با "webinar"

function attendOnlineEvent(event: OnlineEvent): void {
  console.log(`Attending a ${event} online`);
}

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

NonNullable

NonNullable تایپی می‌سازد که با حذف null و undefined از T ایجاد می‌شود.

مثال:


type MaybeString = string | null | undefined;

type DefinitelyString = NonNullable<MaybeString>;

// DefinitelyString اکنون برابر است با string

function processString(str: DefinitelyString): void {
  console.log(str.toUpperCase());
}

// processString(null); // خطا: آرگومان از نوع 'null' قابل تخصیص به پارامتر از نوع 'string' نیست.
// processString(undefined); // خطا: آرگومان از نوع 'undefined' قابل تخصیص به پارامتر از نوع 'string' نیست.
processString("hello"); // معتبر

ReturnType

ReturnType تایپی می‌سازد که شامل تایپ بازگشتی تابع T است.

مثال:


function greet(name: string): string {
  return `Hello, ${name}!`;
}

type Greeting = ReturnType<typeof greet>;

// Greeting اکنون برابر است با string

const message: Greeting = greet("World");

console.log(message);

Parameters

Parameters یک تایپ tuple از تایپ‌های پارامترهای یک نوع تابع T می‌سازد.

مثال:


function logEvent(eventName: string, eventData: object): void {
  console.log(`Event: ${eventName}`, eventData);
}

type LogEventParams = Parameters<typeof logEvent>;

// LogEventParams اکنون برابر است با [eventName: string, eventData: object]

const params: LogEventParams = ["user_login", { userId: "123", timestamp: Date.now() }];

logEvent(...params);

ConstructorParameters

ConstructorParameters یک تایپ tuple یا آرایه از تایپ‌های پارامترهای یک نوع تابع سازنده T می‌سازد. این تایپ‌ها، تایپ آرگومان‌هایی را که باید به سازنده یک کلاس پاس داده شوند، استنتاج می‌کند.

مثال:


class Greeter {
  greeting: string;

  constructor(message: string) {
    this.greeting = message;
  }

  greet() {
    return "Hello, " + this.greeting;
  }
}


type GreeterParams = ConstructorParameters<typeof Greeter>;

// GreeterParams اکنون برابر است با [message: string]

const paramsGreeter: GreeterParams = ["World"];
const greeterInstance = new Greeter(...paramsGreeter);

console.log(greeterInstance.greet()); // خروجی: Hello, World

Required

Required تایپی می‌سازد که شامل تمام خصوصیات T است که همگی به صورت الزامی (required) تنظیم شده‌اند. این تایپ تمام خصوصیات اختیاری را الزامی می‌کند.

مثال:


interface UserProfile {
  name: string;
  age?: number;
  email?: string;
}

type RequiredUserProfile = Required<UserProfile>;

// RequiredUserProfile اکنون برابر است با { name: string; age: number; email: string; }

const completeProfile: RequiredUserProfile = {
  name: "Alice",
  age: 30,
  email: "alice@example.com"
};

// const incompleteProfile: RequiredUserProfile = { name: "Bob" }; // خطا: خاصیت 'age' در تایپ '{ name: string; }' وجود ندارد اما در تایپ 'Required' الزامی است.

تایپ‌های کمکی پیشرفته

Template Literal Types

Template literal types به شما امکان می‌دهد با الحاق تایپ‌های string literal، number literal و غیره، تایپ‌های string literal جدیدی بسازید. این ویژگی امکان دستکاری قدرتمند تایپ‌ها بر اساس رشته را فراهم می‌کند.

مثال:


type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE";
type APIEndpoint = `/api/users` | `/api/products`;

type RequestURL = `${HTTPMethod} ${APIEndpoint}`;

// RequestURL اکنون برابر است با "GET /api/users" | "POST /api/users" | ...

function makeRequest(url: RequestURL): void {
  console.log(`Making request to ${url}`);
}

makeRequest("GET /api/users"); // معتبر
// makeRequest("INVALID /api/users"); // خطا

Conditional Types

Conditional types به شما امکان می‌دهند تایپ‌هایی را تعریف کنید که به یک شرط که به صورت یک رابطه تایپی بیان شده است، بستگی دارند. آن‌ها از کلمه کلیدی infer برای استخراج اطلاعات تایپ استفاده می‌کنند.

مثال:


type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;

// اگر T یک Promise باشد، تایپ برابر U است؛ در غیر این صورت، تایپ برابر T است.

async function fetchData(): Promise<number> {
  return 42;
}


type Data = UnwrapPromise<ReturnType<typeof fetchData>>;

// Data اکنون برابر است با number

function processData(data: Data): void {
  console.log(data * 2);
}

processData(await fetchData());

کاربردهای عملی و سناریوهای واقعی

بیایید سناریوهای پیچیده‌تر و واقعی‌تری را که در آن‌ها تایپ‌های کمکی می‌درخشند، بررسی کنیم.

۱. مدیریت فرم

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


interface FormData {
  firstName: string;
  lastName: string;
  email: string;
  country: string; // الزامی
  city?: string; // اختیاری
  postalCode?: string;
  newsletterSubscription?: boolean;
}

// مقادیر اولیه فرم (فیلدهای اختیاری)
type InitialFormValues = Partial<FormData>;

// مقادیر به‌روز شده فرم (ممکن است برخی فیلدها وجود نداشته باشند)
type UpdatedFormValues = Partial<FormData>;

// فیلدهای الزامی برای ارسال
type RequiredForSubmission = Required<Pick<FormData, 'firstName' | 'lastName' | 'email' | 'country'>>;

// استفاده از این تایپ‌ها در کامپوننت‌های فرم خود
function initializeForm(initialValues: InitialFormValues): void { }
function updateForm(updates: UpdatedFormValues): void {}
function submitForm(data: RequiredForSubmission): void {}


const initialForm: InitialFormValues = { newsletterSubscription: true };

const updateFormValues: UpdatedFormValues = {
    firstName: "John",
    lastName: "Doe"
};

// const submissionData: RequiredForSubmission = { firstName: "test", lastName: "test", email: "test" }; // خطا: 'country' وجود ندارد 
const submissionData: RequiredForSubmission = { firstName: "test", lastName: "test", email: "test", country: "USA" }; // صحیح


۲. تبدیل داده‌های API

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


interface APIResponse {
  user_id: string;
  first_name: string;
  last_name: string;
  email_address: string;
  profile_picture_url: string;
  is_active: boolean;
}

// تبدیل پاسخ API به یک فرمت خواناتر
type UserData = {
  id: string;
  fullName: string;
  email: string;
  avatar: string;
  active: boolean;
};

function transformApiResponse(response: APIResponse): UserData {
  return {
    id: response.user_id,
    fullName: `${response.first_name} ${response.last_name}`,
    email: response.email_address,
    avatar: response.profile_picture_url,
    active: response.is_active
  };
}

function fetchAndTransformData(url: string): Promise<UserData> {
    return fetch(url)
        .then(response => response.json())
        .then(data => transformApiResponse(data));
}


// حتی می‌توانید تایپ را به این صورت اعمال کنید:

function saferTransformApiResponse(response: APIResponse): UserData {
    const {user_id, first_name, last_name, email_address, profile_picture_url, is_active} = response;
    const transformed: UserData = {
        id: user_id,
        fullName: `${first_name} ${last_name}`,
        email: email_address,
        avatar: profile_picture_url,
        active: is_active
    };

    return transformed;
}

۳. مدیریت اشیاء پیکربندی

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


interface AppSettings {
  theme: "light" | "dark";
  language: string;
  notificationsEnabled: boolean;
  apiUrl?: string; // URL API اختیاری برای محیط‌های مختلف
  timeout?: number;  // اختیاری
}

// تنظیمات پیش‌فرض
const defaultSettings: AppSettings = {
  theme: "light",
  language: "en",
  notificationsEnabled: true
};

// تابعی برای ادغام تنظیمات کاربر با تنظیمات پیش‌فرض
function mergeSettings(userSettings: Partial<AppSettings>): AppSettings {
  return { ...defaultSettings, ...userSettings };
}

// استفاده از تنظیمات ادغام شده در برنامه شما
const mergedSettings = mergeSettings({ theme: "dark", apiUrl: "https://customapi.example.com" });
console.log(mergedSettings);

نکاتی برای استفاده مؤثر از تایپ‌های کمکی

  • ساده شروع کنید: قبل از رفتن به سراغ تایپ‌های پیچیده‌تر، با تایپ‌های کمکی اصلی مانند Partial و Readonly شروع کنید.
  • از نام‌های توصیفی استفاده کنید: برای بهبود خوانایی، به تایپ‌های مستعار خود نام‌های معنادار بدهید.
  • تایپ‌های کمکی را ترکیب کنید: می‌توانید چندین تایپ کمکی را برای دستیابی به تبدیل‌های تایپ پیچیده ترکیب کنید.
  • از پشتیبانی ویرایشگر بهره ببرید: از پشتیبانی عالی ویرایشگر TypeScript برای بررسی تأثیرات تایپ‌های کمکی استفاده کنید.
  • مفاهیم اساسی را درک کنید: درک قوی از سیستم تایپ TypeScript برای استفاده مؤثر از تایپ‌های کمکی ضروری است.

نتیجه‌گیری

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