توسعهدهندگان جاوااسکریپت که به TypeScript مهاجرت میکنند، اغلب با انواع اولیه، رابطها و انواع اتحاد شروع میکنند. اگرچه این موارد بسیاری از سناریوهای روزمره را پوشش میدهند، اما ساخت برنامههای مقیاسپذیر و قابل نگهداری نیازمند درک عمیقتری از سیستم نوع است. انواع پیشرفته در TypeScript ابزارهای لازم را برای ایجاد ساختارهای کد انعطافپذیر، قابل استفاده مجدد و به شدت نظارتشده فراهم میکنند. این پست به مهمترین ویژگیهای نوع پیشرفته میپردازد: جنریکها، انواع کاربردی و انواع نگاشتشده.
جنریکها: نوشتن کد قابل استفاده مجدد و ایمن از نظر نوع
جنریکها به شما امکان میدهند کامپوننتهایی ایجاد کنید که با طیف وسیعی از انواع کار میکنند، نه فقط یک نوع خاص. آنها به شما امکان میدهند «جایگاههایی» برای انواع تعریف کنید، اطمینان حاصل کنید که اطلاعات نوع در سراسر کدبیس حفظ شود و از روی آوردن به any پرهیز کنید.
یک دستگیرنده پاسخ API ساده را در نظر بگیرید. بدون استفاده از جنریکها، ممکن است یک شیء عمومی را برگردانید یا به تایپ پویا تکیه کنید که ایمنی زمان کامپایل را از بین میبرد. با استفاده از جنریکها، میتوانید اطمینان حاصل کنید که بدنه پاسخ با رابط مورد انتظار مطابقت دارد.
interface ApiResponse<T> {
data: T;
status: number;
message: string;
}
function handleResponse<T>(response: Response): Promise<ApiResponse<T>> {
return response.json().then((data) => ({
data,
status: response.status,
message: 'Success'
}));
}
// Usage: TypeScript now knows the shape of 'user'
const user = await handleResponse<{ id: number; name: string }>(response);
console.log(user.data.name);
در این مثال، نوع جنریک T به رابط ApiResponse انعطافپذیری میدهد. هنگام فراخوانی handleResponse، TypeScript ساختار خاص داده را استنتاج میکند و برای user.data.name تکمیل خودکار و بررسی نوع را فراهم میکند.
انواع کاربردی: بهرهبرداری از سیستم نوع
TypeScript مجموعهای از انواع کاربردی داخلی را شامل میشود که تبدیلهای رایج نوع را انجام میدهند. استفاده از آنها میتواند به طور قابل توجهی کدهای تکراری (boilerplate) را کاهش داده و قابلیت نگهداری را بهبود بخشد.
1. Partial و Required
نوع کاربردی Partial<T> نوعی را میسازد که تمام ویژگیهای T در آن اختیاری هستند. این مورد به ویژه برای عملیات بهروزرسانی در REST APIها مفید است، جایی که ارسال تنها فیلدهایی که میخواهید تغییر دهید کافی است.
interface User {
id: string;
email: string;
name: string;
}
// Only email and name are optional
type UpdateUserDto = Partial<User>;
const update: UpdateUserDto = { email: 'new@example.com' };
در مقابل، Required<T> تمام ویژگیها را اجباری میکند که میتواند هنگام کار با اشیایی که ممکن است به دلیل منطق شرطی پیچیده، تا حدی تایپ شده باشند، مفید باشد.
2. Pick و Omit
هنگامی که به زیرمجموعهای از یک رابط نیاز دارید، Pick<T, K> به شما امکان میدهد کلیدهای خاصی را انتخاب کنید. به طور مشابه، Omit<T, K> نوعی را با حذف کلیدهای مشخصشده ایجاد میکند.
// Create a type that excludes sensitive password data
type PublicUser = Omit<User, 'password' | 'ssn'>;
انواع نگاشتشده: ساخت پویای نوع
انواع نگاشتشده به شما امکان میدهند انواع جدیدی را با تکرار روی کلیدهای یک نوع موجود ایجاد کنید. این ویژگی برای ایجاد تغییراتی در ساختارهای موجود، مانند readonly کردن تمام ویژگیها یا null کردن آنها، قدرتمند است.
// Makes all properties of T readonly
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
// Makes all properties of T optional
type Nullable<T> = {
[P in keyof T]: T[P] | null;
};
interface Config {
host: string;
port: number;
}
const readOnlyConfig: Readonly<Config> = { host: 'localhost', port: 3000 };
readOnlyConfig.host = 'remote'; // Error: Cannot assign to 'host' because it is a read-only property.
این ویژگی اغلب به صورت داخلی در کتابخانه استاندارد TypeScript استفاده میشود و برای ساخت انواع کاربردی پیشرفتهای که به صورت پویا با ساختارهای ورودی سازگار میشوند، ضروری است.
انواع شرطی و انواع رشتهای قالببندی شده
اگرچه کمی پیچیدهتر هستند، انواع شرطی به شما امکان میدهند بررسیهای نوع را در داخل سیستم نوع انجام دهید. آنها از سینتکس عملگر سهتایی پیروی میکنند: T extends U ? X : Y.
type IsString<T> = T extends string ? "It's a string" : "Not a string";
type Result1 = IsString<"hello">; // "It's a string"
type Result2 = IsString<123>; // "Not a string"
انواع رشتهای قالببندی شده (Template Literal Types) که در TypeScript 4.1 معرفی شدند، امکان دستکاری رشتههای حرفی را فراهم میکنند و اجازه میدهند مدیریت رویدادهای ایمن از نظر نوع و تعریف مسیرهای پیچیدهتری انجام شود.
نتیجهگیری
تسلط بر انواع پیشرفته TypeScript تنها به معنای نوشتن کد هوشمندانه نیست؛ بلکه به معنای بهبود تجربه توسعهدهنده و کاهش خطاهای زمان اجرا است. با بهرهبرداری از جنریکها، انواع کاربردی، انواع نگاشتشده و انواع شرطی، میتوانید برنامههایی بسازید که نه تنها ایمن از نظر نوع هستند، بلکه بسیار بیانگر و قابل نگهداری نیز میباشند. همانطور که به کار با TypeScript ادامه میدهید، خود را به چالش بکشید تا انواع اتحاد ساده را به ساختارهای جنریک قویتر بازسازی کنید. منحنی یادگیری اولیه در بلندمدت به شدت جبران میشود و منجر به کدبیسهایی میشود که درک، اشکالزدایی و مقیاسپذیری آنها آسانتر است.