وبلاگ
سیستم نوعبندی پیشرفته در تایپ اسکریپت: درک عمیقتر از Typeها
فهرست مطالب
“تسلط به برنامهنویسی پایتون با هوش مصنوعی: آموزش کدنویسی هوشمند با ChatGPT”
"تسلط به برنامهنویسی پایتون با هوش مصنوعی: آموزش کدنویسی هوشمند با ChatGPT"
"با شرکت در این دوره جامع و کاربردی، به راحتی مهارتهای برنامهنویسی پایتون را از سطح مبتدی تا پیشرفته با کمک هوش مصنوعی ChatGPT بیاموزید. این دوره، با بیش از 6 ساعت محتوای آموزشی، شما را قادر میسازد تا به سرعت الگوریتمهای پیچیده را درک کرده و اپلیکیشنهای هوشمند ایجاد کنید. مناسب برای تمامی سطوح با زیرنویس فارسی حرفهای و امکان دانلود و تماشای آنلاین."
ویژگیهای کلیدی:
بدون نیاز به تجربه قبلی برنامهنویسی
زیرنویس فارسی با ترجمه حرفهای
۳۰ ٪ تخفیف ویژه برای دانشجویان و دانش آموزان
0 تا 100 عطرسازی + (30 فرمولاسیون اختصاصی حامی صنعت)
دوره آموزش Flutter و برنامه نویسی Dart [پروژه محور]
دوره جامع آموزش برنامهنویسی پایتون + هک اخلاقی [با همکاری شاهک]
دوره جامع آموزش فرمولاسیون لوازم آرایشی
دوره جامع علم داده، یادگیری ماشین، یادگیری عمیق و NLP
دوره فوق فشرده مکالمه زبان انگلیسی (ویژه بزرگسالان)
شمع سازی و عودسازی با محوریت رایحه درمانی
صابون سازی (دستساز و صنعتی)
صفر تا صد طراحی دارو
متخصص طب سنتی و گیاهان دارویی
متخصص کنترل کیفی شرکت دارویی
مقدمه: چرا سیستم نوعبندی تایپاسکریپت؟
در دنیای پیچیده توسعه نرمافزار مدرن، مدیریت پیچیدگی و اطمینان از صحت کد از اهمیت بالایی برخوردار است. جاوااسکریپت، به عنوان زبان اصلی وب، با ماهیت پویا و انعطافپذیر خود، سرعت توسعه را افزایش میدهد، اما در پروژههای بزرگ و تیمهای توسعه، میتواند منجر به خطاهای زمان اجرا و مشکلات نگهداری شود. در اینجاست که تایپاسکریپت (TypeScript) وارد میشود؛ یک سوپراست از جاوااسکریپت که با افزودن یک سیستم نوعبندی استاتیک قدرتمند، امکان شناسایی خطاها را در زمان توسعه (compile-time) فراهم میآورد، نه در زمان اجرا (runtime).
سیستم نوعبندی تایپاسکریپت فراتر از Typeهای ابتدایی مانند string
، number
و boolean
عمل میکند. این سیستم، با ارائه قابلیتهای پیشرفتهای نظیر Generic ها، Conditional Types، Mapped Types و غیره، به توسعهدهندگان اجازه میدهد تا Typeهای بسیار دقیق و انعطافپذیری تعریف کنند که رفتار و ساختار دادهها را با جزئیات بینظیری مدلسازی میکنند. این قابلیتها نه تنها به بهبود کیفیت و پایداری کد کمک میکنند، بلکه تجربه توسعهدهنده (Developer Experience – DX) را با ارائه تکمیل خودکار هوشمند (IntelliSense)، Refactoring امنتر و مستندسازی ضمنی، به طور چشمگیری ارتقا میبخشند. درک عمیق این مفاهیم پیشرفته، کلید باز کردن پتانسیل کامل تایپاسکریپت و نوشتن کدهای قویتر، قابل نگهداریتر و مقیاسپذیرتر است. در این مقاله، ما به بررسی عمیقتر این ویژگیهای قدرتمند میپردازیم و نشان میدهیم چگونه میتوان از آنها برای حل چالشهای پیچیده در طراحی نرمافزار استفاده کرد.
هدف ما فراتر از یک معرفی ساده است؛ ما قصد داریم با ارائه مثالهای عملی و تحلیل دقیق، شما را با ظرافتهای هر مفهوم آشنا کنیم تا بتوانید از آنها در پروژههای خود به بهترین شکل بهره ببرید. از تفاوتهای ظریف بین Type Aliases و Interfaces گرفته تا قدرت Transformational Mapped Types و انعطافپذیری Conditional Types، هر بخش به دقت مورد بررسی قرار خواهد گرفت تا دید جامعی از تواناییهای سیستم نوعبندی تایپاسکریپت به دست آورید.
مبانی پیشرفته: Type Aliases در مقابل Interfaces و مفهوم Structural Typing
در تایپاسکریپت، دو راه اصلی برای تعریف Typeهای شیگرا وجود دارد: Type Aliases
و Interfaces
. اگرچه در بسیاری از موارد میتوان از هر دو به جای یکدیگر استفاده کرد، اما تفاوتهای ظریف و کاربردهای خاصی دارند که درک آنها برای انتخاب صحیح و بهینهسازی کد ضروری است.
تفاوتهای کلیدی و موارد استفاده
Interface
یک قرارداد برای شکل شیء تعریف میکند. این رویکرد به ویژه برای تعریف ساختار اشیایی که قصد دارید به عنوان پارامتر به توابع ارسال کنید یا از توابع بازگردانید، یا برای تعریف کلاسهایی که میخواهند یک قرارداد خاص را پیادهسازی کنند، مفید است. Interfaces قابلیت Declaration Merging دارند، به این معنی که اگر دو Interface با یک نام در Scope یکسان تعریف شوند، تایپاسکریپت آنها را با هم ادغام میکند و یک Interface واحد با تمام Propertyهای هر دو ایجاد میکند. این ویژگی برای افزودن Propertyهای جدید به Interfaceهای موجود از کتابخانههای خارجی یا برای Modular کردن تعاریف Typeها در فایلهای مختلف بسیار مفید است.
interface User {
id: number;
name: string;
}
interface User {
email: string; // Declaration Merging
}
const user: User = {
id: 1,
name: "Alice",
email: "alice@example.com"
};
از سوی دیگر، Type Alias
یک نام جدید برای هر Type موجود ایجاد میکند. این Type میتواند یک شیء، یک Union Type، یک Tuple، یک Generic Type یا حتی یک Primitive Type باشد. Type Aliases بسیار انعطافپذیرتر از Interfaces هستند و میتوانند برای تعریف هر Type دلخواه استفاده شوند. با این حال، Type Aliases از Declaration Merging پشتیبانی نمیکنند. اگر دو Type Alias با یک نام تعریف کنید، تایپاسکریپت خطا میدهد.
type UserAlias = {
id: number;
name: string;
};
type ID = number;
type StringOrNumber = string | number;
type Point = [number, number]; // Tuple Type
const userId: ID = 123;
const coord: Point = [10, 20];
انتخاب بین Interface و Type Alias اغلب به نوع Type و هدف شما بستگی دارد. به طور کلی، برای تعریف شکل اشیاء و کلاسها، Interfaces ترجیح داده میشوند، به خصوص اگر نیاز به Declaration Merging داشته باشید یا قصد داشته باشید که Type شما توسط کلاسها پیادهسازی شود (با کلمه کلیدی implements
). برای Typeهای پیچیدهتر مانند Union Types، Tuple Types یا برای ایجاد نامهای مستعار برای Typeهای موجود، Type Aliases انتخاب بهتری هستند. بسیاری از توسعهدهندگان از یک قانون سرانگشتی پیروی میکنند: اگر شیء است، از Interface استفاده کنید؛ در غیر این صورت، از Type Alias استفاده کنید.
مفهوم Structural Typing و پیامدهای آن
تایپاسکریپت از یک سیستم نوعبندی ساختاری (Structural Typing) استفاده میکند، که گاهی اوقات “duck typing” نیز نامیده میشود (“If it walks like a duck and quacks like a duck, then it is a duck”). این بدان معناست که سازگاری Typeها بر اساس ساختار آنها تعیین میشود، نه بر اساس نام آنها. اگر یک Type A تمام Propertyهای یک Type B را داشته باشد، پس A با B سازگار است، حتی اگر هیچ رابطه ارثبری صریحی بین آنها وجود نداشته باشد یا Typeها نامهای متفاوتی داشته باشند.
interface Point2D {
x: number;
y: number;
}
interface Point3D {
x: number;
y: number;
z: number;
}
const point2D: Point2D = { x: 10, y: 20 };
const point3D: Point3D = { x: 10, y: 20, z: 30 };
function logPoint(p: Point2D) {
console.log(`x: ${p.x}, y: ${p.y}`);
}
logPoint(point2D); // OK
logPoint(point3D); // OK! Point3D has x and y, so it's structurally compatible with Point2D.
این ویژگی قدرت زیادی به تایپاسکریپت میدهد، زیرا امکان کدنویسی انعطافپذیرتر و Modular تر را فراهم میکند. شما نیازی ندارید که به صراحت یک Interface را پیادهسازی کنید تا با آن سازگار باشید؛ فقط کافی است ساختار صحیحی داشته باشید. با این حال، این ویژگی میتواند منجر به خطاهای غیرمنتظرهای شود اگر انتظار نوعبندی اسمی (Nominal Typing) را داشته باشید (که در آن سازگاری بر اساس نام Type تعیین میشود، نه ساختار آن). برای مثال، اگر دو Interface با ساختار یکسان اما معنای متفاوت داشته باشید، تایپاسکریپت آنها را سازگار میداند.
interface Product {
name: string;
price: number;
}
interface Service {
name: string;
price: number;
}
const laptop: Product = { name: "MacBook", price: 2000 };
const consulting: Service = { name: "SEO Consultation", price: 500 };
function calculateTax(item: Product) {
return item.price * 0.10;
}
console.log(calculateTax(consulting)); // OK, even though 'consulting' is a Service.
در این مثال، Service
به طور ساختاری با Product
سازگار است، حتی اگر از نظر مفهومی متفاوت باشند. برای جلوگیری از چنین ابهاماتی، میتوانید از تکنیکهای نوعبندی اسمیمانند استفاده از “برندهای” منحصر به فرد (Unique Brand Properties) در Typeها استفاده کنید:
interface Product {
_brand: 'Product'; // Unique brand
name: string;
price: number;
}
interface Service {
_brand: 'Service'; // Unique brand
name: string;
price: number;
}
const laptop: Product = { _brand: 'Product', name: "MacBook", price: 2000 };
const consulting: Service = { _brand: 'Service', name: "SEO Consultation", price: 500 };
function calculateTaxStrict(item: Product) {
return item.price * 0.10;
}
// console.log(calculateTaxStrict(consulting)); // Error: Property '_brand' is missing in type 'Service' but required in type 'Product'.
این رویکرد به شما کمک میکند تا کنترل بیشتری بر سازگاری Typeها داشته باشید، به ویژه در سناریوهایی که معنای Type اهمیت بیشتری از ساختار صرف آن دارد.
همچنین، موارد readonly
و Optional Properties (?
) نقش مهمی در تعریف دقیقتر Typeها ایفا میکنند. استفاده از readonly
برای Propertyهایی که نباید پس از مقداردهی اولیه تغییر کنند، ایمنی کد را افزایش میدهد. Optional Properties نیز برای تعریف Propertyهایی که ممکن است در یک شیء وجود داشته باشند یا نداشته باشند، استفاده میشوند و انعطافپذیری بیشتری در مدلسازی دادهها فراهم میکنند.
interface Config {
readonly id: string;
port?: number; // Optional property
debug: boolean;
}
const myConfig: Config = {
id: "abc-123",
debug: true
};
// myConfig.id = "def-456"; // Error: Cannot assign to 'id' because it is a read-only property.
console.log(myConfig.port); // undefined, but no error.
قدرت Generic ها: Typeهای پارامتری برای قابلیت استفاده مجدد
Generic ها یکی از قدرتمندترین ویژگیهای سیستم نوعبندی تایپاسکریپت هستند که به شما امکان میدهند اجزای قابل استفاده مجدد ایجاد کنید که قادر به کار با انواع مختلفی از دادهها هستند، بدون اینکه ایمنی Type را از دست بدهند. آنها به شما اجازه میدهند Typeها را به عنوان پارامتر به توابع، کلاسها و Interfaces ارسال کنید، درست مانند اینکه توابع مقادیر را به عنوان پارامتر دریافت میکنند.
مقدمهای بر Generic ها و نیاز به آنها
تصور کنید یک تابع دارید که یک آرایه را دریافت میکند و اولین عنصر آن را برمیگرداند. بدون Generic ها، ممکن است مجبور شوید از any
استفاده کنید، که اطلاعات Type را از بین میبرد:
function getFirstElementAny(arr: any[]): any {
return arr[0];
}
const str = getFirstElementAny(["hello", "world"]); // str is 'any'
const num = getFirstElementAny([1, 2, 3]); // num is 'any'
// No Type safety if we then try to use `str` as a number.
یا برای هر Type خاص یک تابع جداگانه بنویسید، که منجر به تکرار کد میشود:
function getFirstString(arr: string[]): string {
return arr[0];
}
function getFirstNumber(arr: number[]): number {
return arr[0];
}
اینجاست که Generic ها وارد میشوند. با Generic ها، میتوانید یک تابع واحد بنویسید که برای هر Typeی کار کند و در عین حال اطلاعات Type را حفظ کند:
function getFirstElement<T>(arr: T[]): T {
return arr[0];
}
const str = getFirstElement(["hello", "world"]); // str is inferred as string
const num = getFirstElement([1, 2, 3]); // num is inferred as number
const bool = getFirstElement([true, false]); // bool is inferred as boolean
<T>
یک پارامتر Type است که به آن “Type Variable” میگویند. وقتی getFirstElement
را فراخوانی میکنید، تایپاسکریپت T
را بر اساس آرگومانهای ارسالی استنتاج میکند. این قابلیت استفاده مجدد را بدون قربانی کردن ایمنی Type فراهم میکند.
extends
constraint در Generic ها
گاهی اوقات شما نیاز دارید که Type پارامتر Generic خود را محدود کنید. به عنوان مثال، ممکن است بخواهید تضمین کنید که Type T
دارای Property خاصی است. برای این کار از کلمه کلیدی extends
استفاده میشود.
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // Now we know 'arg' has a .length property
return arg;
}
loggingIdentity("hello"); // OK, string has a length property
loggingIdentity([1, 2, 3]); // OK, array has a length property
// loggingIdentity(3); // Error: Argument of type 'number' is not assignable to parameter of type 'Lengthwise'.
این محدودیتها (Constraints) به شما اجازه میدهند تا عملیاتهای خاصی را بر روی پارامترهای Generic انجام دهید، در حالی که Type Safety را حفظ میکنید. Generic ها نه تنها برای توابع، بلکه برای Interfaces، کلاسها و Type Aliases نیز استفاده میشوند.
Generic Interfaces, Generic Classes, Generic Functions
Generic ها میتوانند در تمام بخشهای Type System تایپاسکریپت به کار روند:
- Generic Functions: همانطور که در مثال
getFirstElement
دیدیم، توابع Generic رایجترین کاربرد Generic ها هستند.
function identity<T>(arg: T): T {
return arg;
}
interface GenericIdentityFn<T> {
(arg: T): T;
}
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: GenericIdentityFn<number> = identity;
console.log(myIdentity(10)); // 10, Type is number
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
constructor(zero: T, addFn: (x: T, y: T) => T) {
this.zeroValue = zero;
this.add = addFn;
}
sum(arr: T[]): T {
return arr.reduce((acc, curr) => this.add(acc, curr), this.zeroValue);
}
}
let myGenericNumber = new GenericNumber<number>(0, (x, y) => x + y);
console.log(myGenericNumber.sum([1, 2, 3])); // 6
let stringConcatenator = new GenericNumber<string>("", (x, y) => x + y);
console.log(stringConcatenator.sum(["hello", " ", "world"])); // "hello world"
keyof
و typeof
در ترکیب با Generic ها
keyof
یک Type Operator است که یک Type از تمام Property Nameهای یک Type شیء را به عنوان Literal String Union Type برمیگرداند. این بسیار مفید است وقتی با Generic ها ترکیب میشود تا بتوانیم به صورت ایمن به Propertyهای یک شیء دسترسی پیدا کنیم.
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
const person = { name: "Alice", age: 30 };
let personName = getProperty(person, "name"); // personName is inferred as string
let personAge = getProperty(person, "age"); // personAge is inferred as number
// let invalidProp = getProperty(person, "address"); // Error: Argument of type '"address"' is not assignable to parameter of type '"name" | "age"'.
typeof
Type Operator یک Type از یک متغیر یا Property را برمیگرداند. ترکیب آن با keyof
میتواند برای ساخت Typeهای پویا بسیار قدرتمند باشد.
const COLORS = {
red: "#FF0000",
green: "#00FF00",
blue: "#0000FF",
} as const; // 'as const' makes properties readonly literal types.
type ColorName = keyof typeof COLORS; // "red" | "green" | "blue"
type ColorValue = typeof COLORS[keyof typeof COLORS]; // "#FF0000" | "#00FF00" | "#0000FF"
function getColorHex(colorName: ColorName): ColorValue {
return COLORS[colorName];
}
console.log(getColorHex("red")); // "#FF0000"
// console.log(getColorHex("yellow")); // Error: Argument of type '"yellow"' is not assignable to parameter of type '"red" | "green" | "blue"'.
Generic ها قلب Type System پیشرفته تایپاسکریپت هستند و درک عمیق آنها برای نوشتن کدهای قابل استفاده مجدد، امن و منعطف ضروری است. آنها پایه و اساس بسیاری از Utility Typeها و الگوهای پیشرفته دیگر را تشکیل میدهند که در ادامه به آنها خواهیم پرداخت.
پیمایش پیچیدگی: Conditional Types و قابلیتهای استخراج نوع (Infer)
Conditional Types (Typeهای شرطی) یکی از پیچیدهترین و در عین حال قدرتمندترین ویژگیهای سیستم نوعبندی تایپاسکریپت هستند. آنها به شما اجازه میدهند که Type یک Type را بر اساس یک شرط تعیین کنید، که اغلب شامل یک Type Check است. این قابلیت، سطح بیسابقهای از انعطافپذیری و Expressiveness را در تعریف Typeهای پویا و وابسته به دادهها فراهم میآورد.
سینتکس و منطق T extends U ? X : Y
سینتکس یک Conditional Type شبیه به یک Ternary Operator در جاوااسکریپت است: SomeType extends OtherType ? TrueType : FalseType
.
SomeType extends OtherType
: این یک Type Check است که بررسی میکند آیاSomeType
قابل انتساب بهOtherType
است یا خیر.TrueType
: اگر شرط True باشد، این Type انتخاب میشود.FalseType
: اگر شرط False باشد، این Type انتخاب میشود.
type IsString<T> = T extends string ? "Yes" : "No";
type A = IsString<string>; // Type A is "Yes"
type B = IsString<number>; // Type B is "No"
type C = IsString<any>; // Type C is "Yes" | "No" (distributive conditional type)
type D = IsString<string | number>; // Type D is "Yes" | "No" (distributive conditional type)
نکته مهم در مورد Conditional Types، رفتار توزیعی (Distributive) آنهاست. وقتی یک Conditional Type بر روی یک Union Type اعمال میشود، Conditional Type به صورت جداگانه بر روی هر عضو Union اعمال میشود و سپس نتایج به یک Union Type جدید تبدیل میشوند. این رفتار اغلب برای ساخت Utility Typeهایی مانند Exclude
و Extract
استفاده میشود.
type Exclude<T, U> = T extends U ? never : T;
// If T is assignable to U, it's excluded (becomes never), otherwise it's kept.
type NonNullableStrings = Exclude<"a" | "b" | undefined | null, undefined | null>;
// Equivalent to:
// ("a" extends (undefined | null) ? never : "a") |
// ("b" extends (undefined | null) ? never : "b") |
// (undefined extends (undefined | null) ? never : undefined) |
// (null extends (undefined | null) ? never : null)
// Result: "a" | "b" | never | never => "a" | "b"
کلمه کلیدی infer
و مثالهای پیچیده آن
کلمه کلیدی infer
در Conditional Types برای استخراج (Infer) یک Type از یک Type دیگر استفاده میشود و آن را در یک Type Variable جدید “catch” میکند. این قابلیت به طرز باورنکردنی قدرتمند است و به شما امکان میدهد Typeهای بسیار پیچیده را تجزیه و تحلیل کنید.
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
function greet(name: string): string {
return `Hello, ${name}`;
}
type GreetReturnType = ReturnType<typeof greet>; // GreetReturnType is string
در این مثال، infer R
به تایپاسکریپت میگوید: “اگر T
یک تابع است که هر تعداد آرگومان را میگیرد و چیزی را برمیگرداند، آن چیزی که برمیگرداند را به عنوان R
استخراج کن.” اگر شرط مطابقت داشته باشد، R
Type بازگشتی تابع خواهد بود؛ در غیر این صورت، any
خواهد بود.
مثالهای پیشرفتهتر با infer
:
- استخراج نوع عناصر آرایه:
type ElementType<T> = T extends (infer U)[] ? U : T;
type ArrayElement = ElementType<number[]>; // ArrayElement is number
type NonArrayType = ElementType<string>; // NonArrayType is string
type UnpackPromise<T> = T extends Promise<infer U> ? U : T;
type PromiseValue = UnpackPromise<Promise<number>>; // PromiseValue is number
type NonPromiseValue = UnpackPromise<string>; // NonPromiseValue is string
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
type MyFunctionParams = Parameters<(a: number, b: string) => void>; // MyFunctionParams is [number, string]
استفاده از infer
به شما اجازه میدهد تا الگوهای پیچیدهای را در Typeها مطابقت دهید و بخشهای خاصی از آن الگوها را استخراج کنید. این به ویژه برای کتابخانههایی که نیاز به کار با ساختارهای دادهای انعطافپذیر دارند، یا برای تحلیل و تبدیل Typeهای موجود، بسیار ارزشمند است.
Conditional Types، به همراه infer
، پایه و اساس بسیاری از Utility Typeهای پیشرفته تایپاسکریپت را تشکیل میدهند. درک چگونگی عملکرد آنها به شما امکان میدهد تا Typeهای سفارشی خود را برای سناریوهای پیچیدهتر بنویسید و از توانایی تایپاسکریپت در استنتاج Typeهای پویا به بهترین شکل استفاده کنید.
نقشهبرداری از Typeها: Mapped Types برای تحولات ساختاری
Mapped Types (Typeهای نقشهبرداری شده) در تایپاسکریپت ابزاری قدرتمند برای تبدیل Typeهای شیء موجود به Typeهای شیء جدید بر اساس یک الگوی تکراری هستند. آنها به شما اجازه میدهند تا Propertyهای یک Type را پیمایش کرده و Propertyهای متناظر در یک Type جدید را تغییر دهید. این قابلیت به ویژه برای ایجاد Utility Typeهایی که Propertyهای یک شیء را Optional، Readonly یا تغییر Type میدهند، بسیار مفید است.
سینتکس [P in K]
سینتکس اصلی Mapped Type به شکل [P in K]
است، که در آن K
یک Union Type از کلیدها (Property Nameها) و P
یک Type Variable است که هر کلید را در Union Type K
پیمایش میکند. معمولاً K
از keyof SomeType
به دست میآید.
type MyMappedType<T> = {
[P in keyof T]: T[P]; // This just recreates T, for demonstration
};
interface User {
id: number;
name: string;
email?: string;
}
type CopiedUser = MyMappedType<User>;
// CopiedUser is { id: number; name: string; email?: string; }
Modifiers: +?
, -?
, +readonly
, -readonly
Mapped Types همچنین امکان اضافه کردن یا حذف کردن Modifierها را از Propertyها فراهم میکنند. این Modifierها شامل ?
(Optionality) و readonly
هستند. شما میتوانید با استفاده از +
برای اضافه کردن و -
برای حذف کردن آنها را کنترل کنید. اگر هیچ +
یا -
استفاده نشود، Modifierها از Type اصلی به ارث میرسند.
+?
(اضافه کردن Optionality): همه Propertyها را Optional میکند.-?
(حذف Optionality): همه Propertyها را Required میکند.+readonly
(اضافه کردن Readonly): همه Propertyها را Readonly میکند.-readonly
(حذف Readonly): Readonly بودن Propertyها را حذف میکند (آنها را Mutable میکند).
interface Person {
readonly name: string;
age: number;
address?: string;
}
// Make all properties optional:
type Partial<T> = {
[P in keyof T]?: T[P]; // Shorthand for [P in keyof T]: T[P] | undefined;
};
type MyPartialPerson = Partial<Person>;
// MyPartialPerson is { name?: string; age?: number; address?: string; }
// Make all properties required:
type Required<T> = {
[P in keyof T]-?: T[P];
};
type MyRequiredPerson = Required<Person>;
// MyRequiredPerson is { name: string; age: number; address: string; }
// Make all properties readonly:
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type MyReadonlyPerson = Readonly<Person>;
// MyReadonlyPerson is { readonly name: string; readonly age: number; readonly address?: string; }
// Make all properties mutable (remove readonly):
type Mutable<T> = {
-readonly [P in keyof T]: T[P];
};
type MyMutablePerson = Mutable<Person>;
// MyMutablePerson is { name: string; age: number; address?: string; }
as
remapping key names
در تایپاسکریپت 4.1 به بعد، قابلیت as
به Mapped Types اضافه شد که به شما اجازه میدهد نام کلیدهای جدید را تغییر نام دهید یا آنها را فیلتر کنید. این قابلیت بسیار قدرتمند است و امکان تبدیلهای پیچیدهتر Type را فراهم میکند.
type Getters<T> = {
[P in keyof T as `get${Capitalize<string & P>}`]: () => T[P];
};
interface UserData {
name: string;
age: number;
}
type UserGetters = Getters<UserData>;
/*
UserGetters is:
{
getName: () => string;
getAge: () => number;
}
*/
در این مثال، `get${Capitalize<string & P>}`
یک Template Literal Type است که هر کلید را به یک نام جدید تبدیل میکند (مثلاً name
به getName
). Capitalize<string & P>
یک Utility Type داخلی است که حرف اول یک رشته را بزرگ میکند.
همچنین میتوانید از as
برای فیلتر کردن Propertyها استفاده کنید:
type OnlyStrings<T> = {
[P in keyof T as T[P] extends string ? P : never]: T[P];
};
interface MixedType {
name: string;
age: number;
email: string;
isAdmin: boolean;
}
type StringProperties = OnlyStrings<MixedType>;
/*
StringProperties is:
{
name: string;
email: string;
}
*/
در اینجا، T[P] extends string ? P : never
یک Conditional Type است که اگر Type یک Property رشته باشد، کلید آن را حفظ میکند و در غیر این صورت آن را به never
تبدیل میکند. Propertyهایی که به never
نگاشت میشوند، به طور موثر از Type نتیجه حذف میشوند.
کاربردها در تبدیل Property ها (مثل Pick
, Omit
)
بسیاری از Utility Typeهای داخلی تایپاسکریپت مانند Pick
و Omit
، خود از Mapped Types و Conditional Types استفاده میکنند.
Pick<T, K>
: یک Type جدید را با انتخاب زیرمجموعهای از PropertyهایK
از TypeT
میسازد.
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
interface Product {
id: string;
name: string;
price: number;
description: string;
}
type ProductSummary = Pick<Product, "id" | "name">;
// ProductSummary is { id: string; name: string; }
Omit<T, K>
: یک Type جدید را با حذف زیرمجموعهای از Propertyهای K
از Type T
میسازد. (این با Exclude
و Mapped Types پیادهسازی میشود)type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
type ProductDetails = Omit<Product, "id" | "description">;
// ProductDetails is { name: string; price: number; }
Mapped Types ابزاری فوقالعاده قدرتمند برای دستکاری Typeها و ایجاد Typeهای پویا بر اساس Typeهای موجود هستند. آنها به شما اجازه میدهند تا Typeهای خود را Modular کنید و از تکرار کد جلوگیری نمایید، در حالی که Type Safety را در سراسر برنامه شما تضمین میکنند.
پیوستن به هم: Union و Intersection Types و مدیریت آنها
Union Types (Typeهای اجتماع) و Intersection Types (Typeهای اشتراک) دو مفهوم بنیادین در تایپاسکریپت هستند که به شما امکان میدهند تا Typeها را ترکیب کنید تا Typeهای جدید و پیچیدهتر ایجاد کنید. این قابلیتها برای مدلسازی سناریوهای دادهای که شامل چندین شکل احتمالی یا ترکیبی از چندین رفتار هستند، ضروریاند.
Union Types (`A | B`): کار با Typeهای مختلف، Discriminated Unions
یک Union Type، با استفاده از علامت |
، نشان میدهد که یک متغیر میتواند یکی از چندین Type ممکن باشد. این برای سناریوهایی که یک مقدار میتواند از چندین Type مجاز باشد، بسیار مفید است.
type StringOrNumber = string | number;
let value: StringOrNumber;
value = "hello"; // OK
value = 123; // OK
// value = true; // Error: Type 'boolean' is not assignable to type 'StringOrNumber'.
وقتی با یک Union Type کار میکنید، تایپاسکریپت تنها به Propertyها یا متدهایی اجازه دسترسی میدهد که در تمام اعضای Union مشترک باشند. برای دسترسی به اعضای غیر مشترک، نیاز به Narrowing (محدود کردن) Type دارید.
function printId(id: string | number) {
console.log(id.toString()); // OK, toString() exists on both string and number
// console.log(id.toUpperCase()); // Error: Property 'toUpperCase' does not exist on type 'string | number'.
// Property 'toUpperCase' does not exist on type 'number'.
if (typeof id === "string") {
console.log(id.toUpperCase()); // OK, now 'id' is narrowed to 'string'
}
}
Discriminated Unions
یکی از قویترین کاربردهای Union Types، Discriminated Unions است. این الگو شامل Union Typeهایی از اشیاء است که هر کدام دارای یک Property مشترک (discriminant property) با یک Literal Type منحصر به فرد هستند. این Property مشترک به تایپاسکریپت اجازه میدهد تا به طور هوشمندانه Type را Narrowing کند.
interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
sideLength: number;
}
interface Triangle {
kind: "triangle";
base: number;
height: number;
}
type Shape = Circle | Square | Triangle;
function getArea(shape: Shape): number {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "square":
return shape.sideLength ** 2;
case "triangle":
return 0.5 * shape.base * shape.height;
default:
// Exhaustive checking for switch statement (explained below)
const _exhaustiveCheck: never = shape;
return _exhaustiveCheck;
}
}
const myCircle: Circle = { kind: "circle", radius: 10 };
console.log(getArea(myCircle)); // 314.159...
در مثال بالا، kind
یک Discriminant Property است. تایپاسکریپت از آن برای فهمیدن اینکه در هر case
از switch
، shape
دقیقاً کدام Type از Union است، استفاده میکند.
Intersection Types (`A & B`): ترکیب Typeها، ایجاد Typeهای جدید
یک Intersection Type، با استفاده از علامت &
، دو یا چند Type را ترکیب میکند تا یک Type جدید ایجاد کند که تمام Propertyهای Typeهای اصلی را دارد. این به معنای “هم A و هم B” است.
interface HasID {
id: number;
}
interface HasName {
name: string;
}
type PersonWithIDAndName = HasID & HasName;
const person: PersonWithIDAndName = {
id: 1,
name: "Alice"
};
// type PersonWithIDAndName is { id: number; name: string; }
اگر Typeهای ترکیب شده Propertyهای مشترکی با Typeهای ناسازگار داشته باشند، Property مشترک به never
تبدیل میشود.
interface ConflictingTypeA {
value: string;
}
interface ConflictingTypeB {
value: number;
}
type Conflict = ConflictingTypeA & ConflictingTypeB; // Conflict is { value: never; }
// const conflict: Conflict = { value: "hello" }; // Error: Type 'string' is not assignable to type 'never'.
// const conflict2: Conflict = { value: 123 }; // Error: Type 'number' is not assignable to type 'never'.
Intersection Types اغلب برای ترکیب Mixins یا برای افزودن قابلیتهای خاص به یک Type موجود بدون نیاز به ارثبری کلاس استفاده میشوند.
interface Loggable {
log(message: string): void;
}
class UserProfile {
constructor(public username: string, public email: string) {}
}
type EnhancedUser = UserProfile & Loggable;
function createEnhancedUser(username: string, email: string): EnhancedUser {
return Object.assign(new UserProfile(username, email), {
log: (message: string) => console.log(`[${username}]: ${message}`)
});
}
const admin = createEnhancedUser("admin", "admin@example.com");
admin.log("User logged in.");
console.log(admin.username);
Exhaustive Checking با never
never
Type در تایپاسکریپت نشاندهنده Typeی است که هرگز نباید رخ دهد. این Type معمولاً برای نشان دادن توابعی استفاده میشود که هرگز باز نمیگردند (مثلاً توابعی که یک خطا را پرتاب میکنند یا یک حلقه بینهایت اجرا میکنند). در Conditional Types، never
برای حذف Propertyها استفاده میشود.
یکی از کاربردهای مهم never
، در ترکیب با Discriminated Unions، برای اطمینان از پوشش کامل تمام حالات ممکن در یک switch
یا if/else if
است. این الگو به “Exhaustive Checking” معروف است.
function getAreaSafe(shape: Shape): number {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "square":
return shape.sideLength ** 2;
// case "triangle": // If we comment this out...
// return 0.5 * shape.base * shape.height;
default:
const _exhaustiveCheck: never = shape; // If a new Shape type is added, 'shape' will not be 'never' here
return _exhaustiveCheck; // TypeScript will show an error here
}
}
اگر یک case
جدید به Union Type Shape
اضافه کنید (مثلاً interface Pentagon { kind: "pentagon"; ... }
) اما فراموش کنید که آن را در تابع getAreaSafe
مدیریت کنید، تایپاسکریپت در خط const _exhaustiveCheck: never = shape;
خطا میدهد. این به شما اطمینان میدهد که تمام حالات ممکن Type را مدیریت کردهاید و از خطاهای زمان اجرا جلوگیری میکند.
Type Guards و Type Predicates برایNarrowing Type
Type Guards توابع یا عباراتی هستند که تایپاسکریپت از آنها برای Narrowing Type یک متغیر در یک Scope خاص استفاده میکند. رایجترین Type Guards شامل typeof
و instanceof
هستند.
function logValue(x: string | Date) {
if (typeof x === "string") {
console.log(x.toUpperCase()); // x is string
} else if (x instanceof Date) {
console.log(x.toDateString()); // x is Date
}
}
User-Defined Type Guards (Type Predicates):
شما میتوانید Type Guardهای خود را با استفاده از Type Predicateها تعریف کنید. یک Type Predicate به شکل parameterName is Type
نوشته میشود و تایپاسکریپت را متقاعد میکند که اگر تابع true
برگرداند، پارامتر مورد نظر از Type مشخص شده است.
interface Bird {
fly(): void;
layEggs(): void;
}
interface Fish {
swim(): void;
layEggs(): void;
}
function isFish(pet: Bird | Fish): pet is Fish {
return (pet as Fish).swim !== undefined;
}
function getPetMoves(pet: Bird | Fish) {
if (isFish(pet)) {
pet.swim(); // pet is narrowed to Fish
} else {
pet.fly(); // pet is narrowed to Bird
}
}
in
Operator نیز میتواند به عنوان یک Type Guard عمل کند:
function checkAnimal(animal: { swim?: () => void; fly?: () => void }) {
if ("swim" in animal) {
animal.swim(); // animal is inferred to have swim()
} else if ("fly" in animal) {
animal.fly(); // animal is inferred to have fly()
}
}
Union و Intersection Types، به همراه Type Guardها و Exhaustive Checking، ابزارهایی قدرتمند برای مدلسازی دادههای پیچیده و اطمینان از صحت منطق برنامه شما در تایپاسکریپت هستند.
فراتر از نوعبندی: Template Literal Types و Const Assertions
تایپاسکریپت به طور مداوم با ویژگیهای جدیدی تکامل مییابد که Type System آن را حتی قدرتمندتر و Expressiveتر میکنند. دو مورد از این ویژگیهای نسبتاً جدیدتر، Template Literal Types و Const Assertions هستند که امکان Type Checking دقیقتر و ایجاد Typeهای پویا را فراهم میکنند.
Template Literal Types: ترکیب رشتهها با Typeها
Template Literal Types، که در تایپاسکریپت 4.1 معرفی شدند، به شما امکان میدهند تا Typeهای رشتهای جدیدی را با استفاده از ترکیب Literal String Typeها با Typeهای دیگر در یک قالب (Template) ایجاد کنید، درست مانند Template Literals در جاوااسکریپت. این ویژگی به ویژه برای کار با String-based APIs، تولید نام رویدادها، یا ساخت URLها مفید است.
type EventName<T extends string> = `${T}Changed` | `${T}Created` | `${T}Deleted`;
type UserEvents = EventName<"user">; // "userChanged" | "userCreated" | "userDeleted"
type ProductEvents = EventName<"product">; // "productChanged" | "productCreated" | "productDeleted"
function emitEvent(eventName: UserEvents) {
// ...
}
emitEvent("userCreated"); // OK
// emitEvent("userUpdated"); // Error: Type '"userUpdated"' is not assignable to type '"userChanged" | "userCreated" | "userDeleted"'.
همچنین میتوانید از Union Types در داخل Template Literal Types استفاده کنید تا تمام ترکیبهای ممکن را ایجاد کنید:
type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE";
type Endpoint = "users" | "products";
type APIPath = `/${Endpoint}/${number | "all"}` | `/status`;
type RequestPath = `${HTTPMethod} ${APIPath}`;
type UserRequests = RequestPath;
/*
UserRequests is:
"GET /users/1" | "GET /users/all" | "GET /products/1" | "GET /products/all" | "GET /status" |
"POST /users/1" | "POST /users/all" | "POST /products/1" | "POST /products/all" | "POST /status" |
// ... and so on for PUT and DELETE
*/
function handleRequest(path: RequestPath) {
// ...
}
handleRequest("GET /users/all"); // OK
handleRequest("POST /products/123"); // OK
// handleRequest("PATCH /users/all"); // Error: Type '"PATCH /users/all"' is not assignable to type 'RequestPath'.
Template Literal Types با Utility Typeهای داخلی مانند Uppercase<StringType>
، Lowercase<StringType>
، Capitalize<StringType>
و Uncapitalize<StringType>
نیز ترکیب میشوند تا قابلیتهای تغییر رشته را ارائه دهند:
type Key = "name" | "age";
type SetterMethodNames = `set${Capitalize<Key>}`;
// SetterMethodNames is "setName" | "setAge"
این ویژگیها به شما امکان میدهند تا Typeهای بسیار دقیق و خودکار ایجاد کنید که به طور خودکار به تغییرات در Literal Stringها واکنش نشان میدهند، و خطاهای زمان اجرا مربوط به نامهای اشتباه را کاهش میدهند.
Const Assertions (`as const`): تبدیل متغیرها به Literal Typeها
as const
یک Type Assertion است که در تایپاسکریپت 3.4 معرفی شد. وقتی as const
را به یک Literal (یا یک آرایه یا شیء حاوی Literalها) اعمال میکنید، تایپاسکریپت Type آن را به دقیقترین و محدودترین شکل ممکن استنتاج میکند. این بدان معناست که:
- Literal Primitiveها (مانند رشتهها، اعداد، بولینها) به Literal Typeهای خودشان تبدیل میشوند، نه به Typeهای گستردهتر (
string
،number
،boolean
). - Propertyهای شیء به
readonly
تبدیل میشوند. - عناصر آرایه به
readonly tuple
تبدیل میشوند.
بدون as const
:
const COLORS_NORMAL = {
red: "#FF0000",
green: "#00FF00",
};
type ColorsNormalType = typeof COLORS_NORMAL;
/*
ColorsNormalType is:
{
red: string;
green: string;
}
*/
// COLORS_NORMAL.red = "#FF0000"; // OK, property is mutable
// COLORS_NORMAL.red = "any other string"; // Also OK
با as const
:
const COLORS_CONST = {
red: "#FF0000",
green: "#00FF00",
} as const;
type ColorsConstType = typeof COLORS_CONST;
/*
ColorsConstType is:
{
readonly red: "#FF0000";
readonly green: "#00FF00";
}
*/
// COLORS_CONST.red = "#FF0000"; // Error: Cannot assign to 'red' because it is a read-only property.
// COLORS_CONST.red = "any other string"; // Error: Type '"any other string"' is not assignable to type '"#FF0000"'.
as const
به ویژه برای تعریف مجموعهای از ثابتها یا تنظیمات که Typeهای آنها باید به صورت Literal و غیرقابل تغییر باشند، بسیار مفید است. این کار امکان Type Checking دقیقتر را فراهم میکند و از خطاهای ناشی از تغییر تصادفی مقادیر جلوگیری میکند.
پیامدهای as const
بر Type Inference
as const
به طور چشمگیری بر Type Inference تأثیر میگذارد. به جای استنتاج Typeهای گسترده (widening)، تایپاسکریپت Typeهای دقیق (narrowing) را استنتاج میکند. این به شما اجازه میدهد تا Typeهای بسیار دقیق و محدودتری را بر اساس دادههای زمان کامپایل ایجاد کنید.
const roles = ["admin", "editor", "viewer"]; // Type is string[]
// roles.push("guest"); // OK
const immutableRoles = ["admin", "editor", "viewer"] as const; // Type is readonly ["admin", "editor", "viewer"]
// immutableRoles.push("guest"); // Error: Property 'push' does not exist on type 'readonly ["admin", "editor", "viewer"]'.
type Role = typeof immutableRoles[number]; // "admin" | "editor" | "viewer"
function assignRole(user: string, role: Role) {
console.log(`Assigning ${role} to ${user}`);
}
assignRole("John", "admin"); // OK
// assignRole("Jane", "guest"); // Error: Argument of type '"guest"' is not assignable to parameter of type '"admin" | "editor" | "viewer"'.
در مثال بالا، بدون as const
، roles
Type string[]
را میگرفت که اجازه افزودن هر رشتهای را میداد. اما با as const
، immutableRoles
به یک Tuple از Literal Typeها تبدیل میشود که دقیقاً مقادیر موجود را نشان میدهد و اجازه تغییر یا افزودن عناصر غیرمجاز را نمیدهد. سپس میتوانیم یک Union Type از این Literalها را برای استفاده در Type Checking دیگر استخراج کنیم.
Template Literal Types و Const Assertions ابزارهایی پیشرفته هستند که قابلیتهای Type System تایپاسکریپت را به طور قابل توجهی گسترش میدهند و به توسعهدهندگان اجازه میدهند تا Typeهای دقیقتر و ایمنتری را در سناریوهای پیچیدهتر و پویا تعریف کنند.
سایر ابزارهای پیشرفته و نکات مهم
سیستم نوعبندی تایپاسکریپت مجموعهای غنی از Utility Typeهای داخلی و مفاهیم پیشرفته دیگر را ارائه میدهد که برای سناریوهای خاص توسعه بسیار مفید هستند.
Utility Types داخلی تایپاسکریپت
تایپاسکریپت مجموعهای از Utility Typeهای از پیش تعریف شده را برای انجام عملیاتهای رایج بر روی Typeها فراهم میکند. قبلاً به برخی از آنها مانند Partial
، Required
، Readonly
، Pick
و Omit
اشاره شد. در ادامه به معرفی و توضیح برخی دیگر میپردازیم:
-
Exclude<T, U>
: Typeهایی را ازT
حذف میکند که قابل انتساب بهU
هستند.type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c" type T1 = Exclude<"a" | "b" | "c", "a" | "b">; // "c" type T2 = Exclude<string | number | (() => void), Function>; // string | number
-
Extract<T, U>
: Typeهایی را ازT
استخراج میکند که قابل انتساب بهU
هستند.type T0 = Extract<"a" | "b" | "c", "a" | "f">; // "a" type T1 = Extract<string | number | (() => void), Function>; // () => void
-
NonNullable<T>
:null
وundefined
را ازT
حذف میکند.type T0 = NonNullable<string | number | undefined>; // string | number type T1 = NonNullable<string[] | null | undefined>; // string[]
-
Parameters<T>
: Typeهای پارامترهای یک تابع TypeT
را به عنوان یک Tuple Type برمیگرداند.declare function f1(arg: { a: number; b: string }): void; type T0 = Parameters<() => string>; // [] type T1 = Parameters<(s: string, n: number) => void>; // [string, number] type T2 = Parameters<typeof f1>; // [{ a: number, b: string }]
-
ReturnType<T>
: Type بازگشتی یک تابع TypeT
را برمیگرداند.declare function f1(): { a: number; b: string }; type T0 = ReturnType<() => string>; // string type T1 = ReturnType<(s: string) => void>; // void type T2 = ReturnType<typeof f1>; // { a: number, b: string }
-
Awaited<T>
: Type را از Promiseها باز میکند.type P1 = Promise<string>; type R1 = Awaited<P1>; // string type P2 = Promise<Promise<number>>; type R2 = Awaited<P2>; // number (unwraps recursively)
این Utility Typeها کارهای تکراری را خودکار میکنند و به توسعهدهندگان اجازه میدهند تا بر روی منطق کسب و کار تمرکز کنند تا بر روی جزئیات Type Mapping.
unknown
vs any
و موارد استفاده از unknown
any
و unknown
هر دو میتوانند هر مقداری را در خود جای دهند، اما تفاوت حیاتی در ایمنی Type آنهاست. any
در واقع Type Checking را برای متغیر مربوطه غیرفعال میکند و هر عملیاتی روی آن مجاز است، حتی اگر Typeهای واقعی در زمان اجرا متفاوت باشند. این میتواند منجر به خطاهای زمان اجرا شود.
let valueAny: any = "hello";
valueAny.foo(); // No error, but will throw at runtime if foo() doesn't exist
در مقابل، unknown
ایمنتر است. قبل از انجام هر عملیاتی بر روی یک متغیر از نوع unknown
، باید Type آن را Narrowing کنید (با استفاده از Type Guardها). این تضمین میکند که کد شما Type Safe باقی میماند.
let valueUnknown: unknown = "hello";
// valueUnknown.foo(); // Error: Object is of type 'unknown'.
if (typeof valueUnknown === "string") {
console.log(valueUnknown.toUpperCase()); // OK, valueUnknown is now narrowed to string
}
unknown
بهترین انتخاب برای مقادیر ناشناختهای است که از خارج از برنامه شما میآیند (مانند ورودی API یا خواندن فایل)، زیرا شما را مجبور میکند که Type آن را بررسی کنید و به طور ایمن با آن رفتار کنید. any
باید فقط در موارد بسیار نادر و زمانی که هیچ راه دیگری برای Type کردن کد وجود ندارد، استفاده شود.
never
Type و کاربردهای آن
همانطور که قبلاً اشاره شد، never
Type نشان دهنده Typeی است که هرگز نباید رخ دهد. کاربردهای اصلی آن عبارتند از:
- توابعی که هرگز باز نمیگردند: توابعی که همیشه یک خطا پرتاب میکنند یا وارد یک حلقه بینهایت میشوند.
function error(message: string): never {
throw new Error(message);
}
function infiniteLoop(): never {
while (true) {}
}
Exclude
دیدیم، never
برای حذف اعضا از Union Typeها استفاده میشود.switch
یا if/else
.Non-null Assertion Operator (`!`) و Type Assertion (`as` keyword)
-
Non-null Assertion Operator (`!`): این اپراتور به تایپاسکریپت میگوید که “من میدانم این مقدار
null
یاundefined
نیست، حتی اگر تایپاسکریپت فکر کند ممکن است باشد.” از آن فقط زمانی استفاده کنید که 100% مطمئن هستید، زیرا اگر اشتباه کنید، یک خطای زمان اجرا رخ خواهد داد.function greetUser(name: string | null) { const userName: string = name!; // Assert that name is not null console.log(`Hello, ${userName.toUpperCase()}`); } // const element = document.getElementById("my-element"); // const text = element!.textContent; // Use with caution! What if element is null?
-
Type Assertion (`as` keyword): این به کامپایلر تایپاسکریپت میگوید که “من میدانم Type این مقدار چیست، پس به من اعتماد کن.” این یک عملیات زمان اجرا نیست و فقط برای Type Checking در زمان کامپایل استفاده میشود.
let someValue: any = "this is a string"; let strLength: number = (someValue as string).length; // Assert as string interface Foo { bar: number; baz: string; } const obj = { bar: 123, baz: "hello", extra: true }; const foo: Foo = obj as Foo; // Assert that obj conforms to Foo // Note: This won't check for missing properties, only extra. // If you remove 'bar' or 'baz' from 'obj', this assertion would still pass at compile-time but fail at runtime.
از Type Assertion با احتیاط استفاده کنید. اغلب بهتر است از Type Guardها یا Type Inference برای اجازه دادن به تایپاسکریپت برای فهمیدن Type استفاده کنید.
Tuple Types در عمق
Tuple Types آرایههایی با تعداد مشخصی از عناصر با Typeهای مشخص در هر موقعیت هستند. آنها برای مدلسازی ساختارهای دادهای که شبیه آرایه هستند اما دارای معنای خاصی برای هر عنصر هستند، مفیدند.
type Rgb = [number, number, number];
const red: Rgb = [255, 0, 0];
// const invalidColor: Rgb = [255, 0, 0, 1]; // Error: Type 'number' is not assignable to type 'undefined'.
Tupleها میتوانند شامل عناصر Optional (با ?
) و Rest Elements (با ...
) باشند:
type OptionalTuple = [number, string?];
const t1: OptionalTuple = [10]; // OK
const t2: OptionalTuple = [10, "hello"]; // OK
type RestTuple = [number, ...string[]];
const r1: RestTuple = [1, "a", "b", "c"]; // OK
const r2: RestTuple = [1]; // OK
Enums (نکات پیشرفته)
Enums (Enumerations) به شما امکان میدهند تا مجموعهای از ثابتهای نامگذاری شده را تعریف کنید. آنها میتوانند عددی (Numeric) یا رشتهای (String) باشند. اگرچه در تایپاسکریپت رایج هستند، استفاده از Literal Union Types اغلب توصیه میشود، زیرا Type Safeتر هستند و نیازی به کد جاوااسکریپت زمان اجرا ندارند.
// Numeric Enum (default)
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right // 3
}
// String Enum
enum HttpMethod {
GET = "GET",
POST = "POST",
PUT = "PUT_METHOD",
}
// Literal Union Type (often preferred for better Type Safety and smaller bundle size)
type UserRole = "admin" | "editor" | "viewer";
Enums به خصوص در زمانی که نیاز به Mapping بین مقادیر عددی و نامها دارید (مثلاً با مقادیر APIهای قدیمی) مفیدند، اما برای استفادههای جدید، Literal Union Types معمولاً ارجحیت دارند.
نتیجهگیری
سیستم نوعبندی پیشرفته تایپاسکریپت فراتر از Type Checking ساده است؛ این یک ابزار قدرتمند برای طراحی نرمافزار، مدلسازی دادهها و بهبود کیفیت کد در طول چرخه توسعه است. با درک و بهکارگیری مفاهیمی مانند Generic ها، Conditional Types، Mapped Types، Template Literal Types و Const Assertions، توسعهدهندگان میتوانند کدهای JavaScript را با سطحی از دقت و امنیت بنویسند که قبلاً غیرقابل تصور بود.
قابلیتهایی مانند Discriminated Unions و Exhaustive Checking به شما اطمینان میدهند که تمام حالات ممکن دادهها را مدیریت کردهاید، در حالی که Utility Types داخلی و Type Guardهای سفارشیسازی شده فرآیند Narrowing Type و دستکاری Type را ساده میکنند. هدف نهایی، نوشتن کدی است که نه تنها کار میکند، بلکه قابل نگهداری، مقیاسپذیر و در برابر خطاها مقاوم باشد. تسلط بر این ویژگیهای پیشرفته تایپاسکریپت، شما را به یک توسعهدهنده کارآمدتر و مؤثرتر تبدیل خواهد کرد که قادر به ساخت برنامههای کاربردی پیچیده و پایدار است.
تایپاسکریپت به طور مداوم در حال تکامل است و ویژگیهای جدیدی به آن اضافه میشود که آن را حتی قدرتمندتر میکند. با ادامه یادگیری و کاوش در عمق این سیستم نوعبندی، میتوانید از پتانسیل کامل آن برای بهبود فرآیند توسعه و خروجی نهایی خود بهرهمند شوید.
“تسلط به برنامهنویسی پایتون با هوش مصنوعی: آموزش کدنویسی هوشمند با ChatGPT”
"تسلط به برنامهنویسی پایتون با هوش مصنوعی: آموزش کدنویسی هوشمند با ChatGPT"
"با شرکت در این دوره جامع و کاربردی، به راحتی مهارتهای برنامهنویسی پایتون را از سطح مبتدی تا پیشرفته با کمک هوش مصنوعی ChatGPT بیاموزید. این دوره، با بیش از 6 ساعت محتوای آموزشی، شما را قادر میسازد تا به سرعت الگوریتمهای پیچیده را درک کرده و اپلیکیشنهای هوشمند ایجاد کنید. مناسب برای تمامی سطوح با زیرنویس فارسی حرفهای و امکان دانلود و تماشای آنلاین."
ویژگیهای کلیدی:
بدون نیاز به تجربه قبلی برنامهنویسی
زیرنویس فارسی با ترجمه حرفهای
۳۰ ٪ تخفیف ویژه برای دانشجویان و دانش آموزان