برای توسعهدهندگان میانرده تایپاسکریپت، سفر از نحو پایه به الگوهای معماری قوی اغلب با دیواری به نام منحنی یادگیری دستکاری تایپهای پیشرفته روبرو میشود. در حالی که string، number و interface پایه را تشکیل میدهند، قدرت واقعی تایپاسکریپت در توانایی آن برای بیان منطق پیچیده تنها از طریق سیستم تایپ آن نهفته است. با بهرهگیری از تایپهای پیشرفته، میتوانید باگهایی را در زمان کامپایل شناسایی کنید که در غیر این صورت به محیط تولید راه مییافتند، کدهای تکراری (boilerplate) را کاهش دهید و APIهای خودمستند ایجاد کنید.
این پست به بررسی مهمترین تایپهای پیشرفته در تایپاسکریپت میپردازد—یونیونها (Unions)، اینترسکشنها (Intersections)، یونیونهای تمیز (Discriminated Unions)، محافظهای تایپ (Type Guards) و تایپهای شرطی (Conditional Types)—و برای هر کدام مثالهای عملی ارائه میدهد تا به شما کمک کند کیفیت کد خود را ارتقا دهید.
قدرت تایپهای یونیون و اینترسکشن
تایپهای یونیون به یک متغیر اجازه میدهند بیش از یک نوع را در خود نگه دارد که با استفاده از کاراکتر لوله | اعلام میشود. در مقابل، تایپهای اینترسکشن از علامت آندرساند & برای ترکیب چندین تایپ در یک تایپ استفاده میکنند. اگرچه اینها ساده به نظر میرسند، درک رفتار آنها برای مدیریت ساختارهای داده پویا حیاتی است.
سناریویی را در نظر بگیرید که پروفایل کاربر میتواند یا یک شیء استاندارد باشد یا یک شناسه رشتهای سادهشده هنگام بارگیری از حافظه پنهان (cache):
// Union Type: Accepts User object OR a string ID
type UserId = User | string;
function greetUser(input: UserId) {
if (typeof input === 'string') {
console.log(`Hello user ${input}`);
} else {
console.log(`Hello ${input.name}`);
}
}
// Intersection Type: Combines properties
interface Admin {
permissions: string[];
}
interface User {
name: string;
}
type SuperUser = Admin & User;
// SuperUser now requires both 'permissions' and 'name'
یونیونهای تمیز و محافظهای تایپ
یکی از کاربردیترین کاربردهای تایپهای پیشرفته، یونیون تمیز (که به آن یونیون نشاندار یا Tagged Union نیز گفته میشود) است. این الگو شامل یک تایپ یونیون است که هر عضو آن دارای یک خاصیت مشترک با یک نوع لیترال است که به آن تمایزدهنده (discriminant) گفته میشود. این امر به تایپاسکریپت اجازه میدهد تا تایپ را درون بلوکهای جریان کنترل (control flow) محدود کند.
سیستمی را تصور کنید که انواع مختلف درخواستهای شبکه را مدیریت میکند:
type NetworkSuccess = { status: number; response: string };
type NetworkFail = { status: number; reason: string };
type NetworkEvent = NetworkSuccess | NetworkFail;
function handleNetworkEvent(event: NetworkEvent) {
// TypeScript narrows the type based on the 'status' property
if (event.status === 200) {
// Here, event is narrowed to NetworkSuccess
console.log(`Success: ${event.response}`);
} else {
// Here, event is narrowed to NetworkFail
console.log(`Failed due to: ${event.reason}`);
}
}
این الگو در کتابخانههای مدیریت وضعیت مانند Redux ضروری است، جایی که اکشنها اغلب ساختارهای پیکربندی (payload) متفاوتی را بسته به عملیات حمل میکنند.
تایپهای شرطی برای منطق پویا
تایپهای شرطی به شما امکان میدهند نگاشتهای تایپ غیریکنواخت را بیان کنید. آنها از نحو T extends U ? X : Y پیروی میکنند، به این معنی که "اگر T به U قابل انتساب باشد، از تایپ X استفاده کن، در غیر این صورت از تایپ Y استفاده کن." این موضوع برای ایجاد تایپهای کاربردی (utility types) یا تبدیل پویای ساختارهای داده بسیار مفید است.
بیایید یک تایپ شرطی ایجاد کنیم که نوع بازگشتی یک تابع را استخراج کند:
// Define the conditional type
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
// Usage
type MyFunc = () => Promise<string>;
type Result = GetReturnType<MyFunc>;
// Result is now inferred as: Promise<string>
// This prevents errors if a non-function type is passed
// type BadResult = GetReturnType<number>; // Results in 'never'
این رویکرد به طور گسترده در ابزارهای داخلی تایپاسکریپت مانند ReturnType<T> و Pick<T, K> استفاده میشود. درک نحوه نوشتن تایپهای شرطی خود به شما اجازه میدهد کامپوننتهای بسیار قابل استفاده مجدد و ژنریک بسازید.
تایپهای رشتهای قالببندی شده (Template Literal Types)
تایپهای رشتهای قالببندی شده که در تایپاسکریپت 4.1 معرفی شدند، به شما امکان میدهند تایپهای رشتهای را بر اساس سایر تایپها ایجاد کنید. این موضوع برای سیستمهای استایلدهی یا تولید URLهای API به ویژه مفید است.
type EventName = 'click' | 'hover' | 'submit';
// Create a prefixed type string
type OnEvent = `on${Capitalize<EventName>}`;
// Result: 'onClick' | 'onHover' | 'onSubmit'
نتیجهگیری
تسلط بر تایپهای پیشرفته تایپاسکریپت شما را از توسعهدهندهای که صرفاً از تایپاسکریپت برای بررسی نحو استفاده میکند به توسعهدهندهای تبدیل میکند که از آن برای طراحی معماری بهره میبرد. با استفاده از یونیونها، اینترسکشنها، یونیونهای تمیز و تایپهای شرطی، میتوانید کدی بنویسید که نه تنها ایمنتر، بلکه بیانگر و قابل نگهداریتر باشد. اگرچه منحنی یادگیری اولیه میتواند شیبدار باشد، اما مزایای بلندمدت در کاهش خطاهای زمان اجرا و بهبود تجربه توسعهدهنده انکارناپذیر است. شروع به گنجاندن این الگوها در پروژه بعدی خود کنید تا ببینید چگونه سیستم تایپ میتواند به اندازه منطق شما برای شما سخت کار کند.