فارسی

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

انواع ابزاری TypeScript: ابزارهای داخلی برای دستکاری نوع

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

انواع ابزاری TypeScript چه هستند؟

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

آن‌ها را مانند توابعی در نظر بگیرید که به جای مقادیر، بر روی انواع عمل می‌کنند. آن‌ها یک نوع را به عنوان ورودی می‌گیرند و یک نوع اصلاح‌شده را به عنوان خروجی برمی‌گردانند. این به شما امکان می‌دهد تا روابط و تبدیل‌های پیچیده نوع را با حداقل کد ایجاد کنید.

چرا از انواع ابزاری استفاده کنیم؟

چندین دلیل قانع‌کننده برای گنجاندن انواع ابزاری در پروژه‌های تایپ‌اسکریپت شما وجود دارد:

انواع ابزاری ضروری TypeScript

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

1. Partial<T>

نوع ابزاری Partial<T> تمام خصوصیات نوع T را اختیاری می‌کند. این زمانی مفید است که می‌خواهید یک نوع جدید ایجاد کنید که برخی یا تمام خصوصیات یک نوع موجود را داشته باشد، اما نمی‌خواهید همه آن‌ها الزامی باشند.

سینتکس:

type Partial<T> = { [P in keyof T]?: T[P]; };

مثال:

interface User {
 id: number;
 name: string;
 email: string;
}

type OptionalUser = Partial<User>; // همه خصوصیات اکنون اختیاری هستند

const partialUser: OptionalUser = {
 name: "Alice", // فقط خصوصیت name ارائه شده است
};

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

2. Required<T>

نوع ابزاری Required<T> تمام خصوصیات نوع T را الزامی می‌کند. این برعکس Partial<T> است. این زمانی مفید است که شما یک نوع با خصوصیات اختیاری دارید و می‌خواهید اطمینان حاصل کنید که تمام خصوصیات وجود دارند.

سینتکس:

type Required<T> = { [P in keyof T]-?: T[P]; };

مثال:

interface Config {
 apiKey?: string;
 apiUrl?: string;
}

type CompleteConfig = Required<Config>; // همه خصوصیات اکنون الزامی هستند

const config: CompleteConfig = {
 apiKey: "your-api-key",
 apiUrl: "https://example.com/api",
};

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

3. Readonly<T>

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

سینتکس:

type Readonly<T> = { readonly [P in keyof T]: T[P]; };

مثال:

interface Product {
 id: number;
 name: string;
 price: number;
}

type ImmutableProduct = Readonly<Product>; // همه خصوصیات اکنون فقط خواندنی هستند

const product: ImmutableProduct = {
 id: 123,
 name: "Example Product",
 price: 25.99,
};

// product.price = 29.99; // خطا: نمی‌توان به 'price' مقدار اختصاص داد زیرا یک خصوصیت فقط خواندنی است.

مورد استفاده: ایجاد ساختارهای داده تغییرناپذیر، مانند اشیاء پیکربندی یا اشیاء انتقال داده (DTOs)، که نباید پس از ایجاد تغییر کنند. این به ویژه در پارادایم‌های برنامه‌نویسی تابعی مفید است.

4. Pick<T, K extends keyof T>

نوع ابزاری Pick<T, K extends keyof T> با انتخاب مجموعه‌ای از خصوصیات K از نوع T یک نوع جدید ایجاد می‌کند. این زمانی مفید است که شما فقط به زیرمجموعه‌ای از خصوصیات یک نوع موجود نیاز دارید.

سینتکس:

type Pick<T, K extends keyof T> = { [P in K]: T[P]; };

مثال:

interface Employee {
 id: number;
 name: string;
 department: string;
salary: number;
}

type EmployeeNameAndDepartment = Pick<Employee, "name" | "department">; // فقط name و department را انتخاب کن

const employeeInfo: EmployeeNameAndDepartment = {
 name: "Bob",
 department: "Engineering",
};

مورد استفاده: ایجاد اشیاء انتقال داده (DTOs) تخصصی که فقط شامل داده‌های لازم برای یک عملیات خاص هستند. این می‌تواند عملکرد را بهبود بخشد و میزان داده‌های منتقل شده از طریق شبکه را کاهش دهد. تصور کنید جزئیات کاربر را به کلاینت ارسال می‌کنید اما اطلاعات حساس مانند حقوق را حذف می‌کنید. می‌توانید از Pick برای ارسال فقط `id` و `name` استفاده کنید.

5. Omit<T, K extends keyof any>

نوع ابزاری Omit<T, K extends keyof any> با حذف مجموعه‌ای از خصوصیات K از نوع T یک نوع جدید ایجاد می‌کند. این برعکس Pick<T, K extends keyof T> است و زمانی مفید است که می‌خواهید خصوصیات خاصی را از یک نوع موجود حذف کنید.

سینتکس:

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

مثال:

interface Event {
 id: number;
 title: string;
description: string;
 date: Date;
 location: string;
}

type EventSummary = Omit<Event, "description" | "location">; // description و location را حذف کن

const eventPreview: EventSummary = {
 id: 1,
 title: "Conference",
 date: new Date(),
};

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

6. Exclude<T, U>

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

سینتکس:

type Exclude<T, U> = T extends U ? never : T;

مثال:

type AllowedFileTypes = "image" | "video" | "audio" | "document";
type MediaFileTypes = "image" | "video" | "audio";

type DocumentFileTypes = Exclude<AllowedFileTypes, MediaFileTypes>; // "document"

const fileType: DocumentFileTypes = "document";

مورد استفاده: فیلتر کردن یک نوع union برای حذف انواع خاصی که در یک زمینه خاص مرتبط نیستند. به عنوان مثال، ممکن است بخواهید انواع فایل خاصی را از لیستی از انواع فایل مجاز حذف کنید.

7. Extract<T, U>

نوع ابزاری Extract<T, U> با استخراج تمام انواعی از T که به U قابل انتساب هستند، یک نوع جدید ایجاد می‌کند. این برعکس Exclude<T, U> است و زمانی مفید است که می‌خواهید انواع خاصی را از یک نوع union انتخاب کنید.

سینتکس:

type Extract<T, U> = T extends U ? T : never;

مثال:

type InputTypes = string | number | boolean | null | undefined;
type PrimitiveTypes = string | number | boolean;

type NonNullablePrimitives = Extract<InputTypes, PrimitiveTypes>; // string | number | boolean

const value: NonNullablePrimitives = "hello";

مورد استفاده: انتخاب انواع خاص از یک نوع union بر اساس معیارهای خاص. به عنوان مثال، ممکن است بخواهید تمام انواع اولیه (primitive) را از یک نوع union که شامل انواع اولیه و انواع شیء است، استخراج کنید.

8. NonNullable<T>

نوع ابزاری NonNullable<T> با حذف null و undefined از نوع T یک نوع جدید ایجاد می‌کند. این زمانی مفید است که می‌خواهید اطمینان حاصل کنید که یک نوع نمی‌تواند null یا undefined باشد.

سینتکس:

type NonNullable<T> = T extends null | undefined ? never : T;

مثال:

type MaybeString = string | null | undefined;

type DefinitelyString = NonNullable<MaybeString>; // string

const message: DefinitelyString = "Hello, world!";

مورد استفاده: اطمینان از اینکه یک مقدار قبل از انجام عملیات روی آن null یا undefined نیست. این می‌تواند به جلوگیری از خطاهای زمان اجرا ناشی از مقادیر غیرمنتظره null یا undefined کمک کند. سناریویی را در نظر بگیرید که نیاز به پردازش آدرس کاربر دارید و حیاتی است که آدرس قبل از هر عملیاتی null نباشد.

9. ReturnType<T extends (...args: any) => any>

نوع ابزاری ReturnType<T extends (...args: any) => any> نوع بازگشتی یک نوع تابع T را استخراج می‌کند. این زمانی مفید است که می‌خواهید نوع مقداری را که یک تابع برمی‌گرداند، بدانید.

سینتکس:

type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;

مثال:

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

type FetchDataReturnType = ReturnType<typeof fetchData>; // Promise<{ data: any }>

async function processData(data: FetchDataReturnType) {
 // ...
}

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

10. Parameters<T extends (...args: any) => any>

نوع ابزاری Parameters<T extends (...args: any) => any> انواع پارامترهای یک نوع تابع T را به صورت یک تاپل (tuple) استخراج می‌کند. این زمانی مفید است که می‌خواهید انواع آرگومان‌هایی را که یک تابع می‌پذیرد، بدانید.

سینتکس:

type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;

مثال:

function createUser(name: string, age: number, email: string): void {
 // ...
}

type CreateUserParams = Parameters<typeof createUser>; // [string, number, string]

function logUser(...args: CreateUserParams) {
 console.log("Creating user with:", args);
}

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

11. ConstructorParameters<T extends abstract new (...args: any) => any>

نوع ابزاری ConstructorParameters<T extends abstract new (...args: any) => any> انواع پارامترهای یک نوع تابع سازنده (constructor) T را به صورت یک تاپل استخراج می‌کند. این زمانی مفید است که می‌خواهید انواع آرگومان‌هایی را که یک سازنده می‌پذیرد، بدانید.

سینتکس:

type ConstructorParameters<T extends abstract new (...args: any) => any> = T extends abstract new (...args: infer P) => any ? P : never;

مثال:

class Logger {
 constructor(public prefix: string, public enabled: boolean) {}
 log(message: string) {
 if (this.enabled) {
 console.log(`${this.prefix}: ${message}`);
 }
 }
}

type LoggerConstructorParams = ConstructorParameters<typeof Logger>; // [string, boolean]

function createLogger(...args: LoggerConstructorParams) {
 return new Logger(...args);
}

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

12. InstanceType<T extends abstract new (...args: any) => any>

نوع ابزاری InstanceType<T extends abstract new (...args: any) => any> نوع نمونه (instance type) یک نوع تابع سازنده T را استخراج می‌کند. این زمانی مفید است که می‌خواهید نوع شیئی را که یک سازنده ایجاد می‌کند، بدانید.

سینتکس:

type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any;

مثال:

class Greeter {
 greeting: string;
 constructor(message: string) {
 this.greeting = message;
 }
 greet() {
 return "Hello, " + this.greeting;
 }
}

type GreeterInstance = InstanceType<typeof Greeter>; // Greeter

const myGreeter: GreeterInstance = new Greeter("World");
console.log(myGreeter.greet());

مورد استفاده: تعیین نوع شیء ایجاد شده توسط یک سازنده، که هنگام کار با وراثت یا چندریختی (polymorphism) مفید است. این یک روش ایمن از نظر نوع برای ارجاع به نمونه یک کلاس فراهم می‌کند.

13. Record<K extends keyof any, T>

نوع ابزاری Record<K extends keyof any, T> یک نوع شیء می‌سازد که کلیدهای خصوصیات آن K و مقادیر خصوصیات آن T هستند. این برای ایجاد انواع شبه-دیکشنری که کلیدهای آن را از قبل می‌دانید، مفید است.

سینتکس:

type Record<K extends keyof any, T> = { [P in K]: T; };

مثال:

type CountryCode = "US" | "CA" | "GB" | "DE";

type CurrencyMap = Record<CountryCode, string>; // { US: string; CA: string; GB: string; DE: string; }

const currencies: CurrencyMap = {
 US: "USD",
 CA: "CAD",
 GB: "GBP",
 DE: "EUR",
};

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

انواع ابزاری سفارشی

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

مثال:

// یک نوع ابزاری برای دریافت کلیدهای یک شیء که نوع خاصی دارند
type KeysOfType<T, U> = { [K in keyof T]: T[K] extends U ? K : never }[keyof T];

interface Person {
 name: string;
 age: number;
 address: string;
 phoneNumber: number;
}

type StringKeys = KeysOfType<Person, string>; // "name" | "address"

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

نتیجه‌گیری

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

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