انواع شرطی (Conditional Types)
«نوع شرطی (Conditional Type)» یعنی تصمیم گیری روی نوع ها. مثل if-else اما در سطحِ تایپ. بنابراین کد ایمن تر می شود و خطاها زودتر دیده می شوند. تصور کن از روی کارنامه، مسیر رشته را حدس می زنیم.
نوشتار پایه انواع شرطی
الگو این است: T extends U ? X : Y. اگر T قابل انتصاب به U باشد، X می شود؛ وگرنه Y.
type IsString<T> = T extends string ? true : false;
type Result1 = IsString<string>;
type Result2 = IsString<number>;
type Result3 = IsString<'hello'>;
let a: IsString<string>;
let b: IsString<number>;
انواع شرطی و اتحادها
روی اتحادها، شرطی ها «پخش» می شوند. یعنی برای هر عضو جدا محاسبه می شوند.
type ToArray<T> = T extends any ? T[] : never;
type StringOrNumberArray = ToArray<string | number>;
type ExtractString<T> = T extends string ? T : never;
type StringsOnly = ExtractString<string | number | boolean | 'hello'>;
استفاده از infer برای کشف نوع
infer یعنی «از داخل نوع، بخشش را بیرون بکش». مثل حدس زدن نمره هر درس از معدل.
type ReturnTypeOf<T> = T extends (...args: any[]) => infer R ? R : never;
function greet(): string {
return 'Hello, world!';
}
function getNumber(): number {
return 42;
}
type GreetReturnType = ReturnTypeOf<typeof greet>;
type NumberReturnType = ReturnTypeOf<typeof getNumber>;
type ElementType<T> = T extends (infer U)[] ? U : never;
type NumberArrayElement = ElementType<number[]>;
type StringArrayElement = ElementType<string[]>;
نوع های شرطیِ از پیش آماده
کتابخانه استاندارد، ابزارهای آماده دارد. سریع و مطمئن.
type OnlyStrings = Extract<string | number | boolean, string>;
type NoStrings = Exclude<string | number | boolean, string>;
type NotNull = NonNullable<string | null | undefined>;
type Params = Parameters<(a: string, b: number) => void>;
type Ret = ReturnType<() => string>;
الگوهای پیشرفته
بازکردن تو در توی Promise
گاهی Promise ها تودرتو هستند. با شرطی بازشان کن.
type UnwrapPromise<T> = T extends Promise<infer U> ? UnwrapPromise<U> : T;
type A = UnwrapPromise<Promise<string>>;
type B = UnwrapPromise<Promise<Promise<number>>>;
type C = UnwrapPromise<boolean>;
زنجیره if-else در سطح نوع
چند شرط را پشت سر هم بگذار. مثل مشاوره انتخاب رشته.
type TypeName<T> =
T extends string ? 'string' :
T extends number ? 'number' :
T extends boolean ? 'boolean' :
T extends undefined ? 'undefined' :
T extends Function ? 'function' :
'object';
type T0 = TypeName<string>;
type T1 = TypeName<42>;
type T2 = TypeName<true>;
type T3 = TypeName<() => void>;
type T4 = TypeName<Date[]>;
function processValue<T>(value: T): T extends string
? string
: T extends number
? number
: T extends boolean
? boolean
: never {
if (typeof value === 'string') {
return value.toUpperCase() as any;
} else if (typeof value === 'number') {
return (value * 2) as any;
} else if (typeof value === 'boolean') {
return (!value) as any;
} else {
throw new Error('Unsupported type');
}
}
const stringResult: string = processValue('hello');
const numberResult: number = processValue(10);
const boolResult: boolean = processValue(true);
نکته: برای تمرین عملی بیشتر، نمونه «کاربرد واقعی» را هم اجرا کن.
گام های عملی
- مسئله نوعی را دقیق بنویس.
- الگوی
T extends U ? X : Yرا بساز. - اگر لازم شد، از
inferبرای استخراج نوع استفاده کن. - روی اتحادها، نتیجه پخش شدن را بررسی کن.
- در پایان، با ابزارهای آماده مثل
Extractمقایسه کن.
جمع بندی سریع
- انواع شرطی تایپ اسکریپت، منطق if برای نوع هاست.
inferبخش های پنهان نوع را بیرون می کشد.- روی اتحادها، شرطی ها جداگانه اعمال می شوند.
- از ابزارهای آماده برای سرعت بهره ببر.
- پیچیدگی زیاد، سرعت کامپایل را کم می کند.
برای ادامه مسیر، صفحه انواع شرطی تایپ اسکریپت و همچنین نگهبان های نوع و انواع نگاشتی را ببین.