ایجاد نوع از روی نوع های دیگر در تایپ اسکریپت [TypeScript]

ترکیب نوع ها در تایپ اسکریپت

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

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

جنریک ها (Generics)

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

برای نمونه به تابع زیر توجه کنید:

function first(arr: number[]): number {
  return arr[0];
}

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

function first<MyType>(arr: MyType[]): MyType {
  return arr[0];
}

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

const one = first([1,2,3]); // one: number

const two = first(['a', 'b', 'c']); // two: string

// حتی می توانیم نوع را خودمان مشخص کنیم
const three = first<boolean>([...]) // three: boolean

غیر از توابع در سایر بخش ها مثل کلاس ها و یا interface و type هم می توانیم از جنریک ها استفاده کنیم:

interface Box<T>{
  content: T
}

const strBox: Box<string> = { content: 'hello' };

const numBox: Box<number> = { content: 123 };

عملگر نوع keyof

عملگر keyof ، یک نوع آبجکت را می گیرد و یک نوع union از رشته یا اعداد دقیق بر حسب ویژگی های آن آبجکت بر می گرداند:

type Point = { x: number; y: number };
type P = keyof Point; // P = 'x' | 'y'

type ArrayLike = { [n: number]: unknown };
type A = keyof ArrayLike; // A = number

عملگر نوع typeof

با استفاده از عملگر typeof می توانیم نوع یک مقدار را در هر لحظه به دست آوریم:

let s = "hello";
let n: typeof s; // n: string

یا برای مثال:

function myFn() {
  return { x: 10, y: 3 };
}

type P = typeof myFn; // P = () => { x: number; y: number }

نوع های شرطی

با استفاده از نوع های شرطی می توانیم یک نوع را بر اساس یک شرط تعیین کنیم. معمولا به فرم condition ? trueExpression : falseExpression نوشته می شوند. به مثال زیر توجه نمایید:

interface IdLabel {
  id: number;
}
interface NameLabel {
  name: string;
}

type NameOrId<T extends number | string> = T extends number
  ? IdLabel
  : NameLabel;

در مثال بالا قسمت <T extends number | string> یک پارامتر نوع است که می تواند فقط number یا string باشد (جنریک). قسمت T extends number ? IdLabel : NameLabel یک نوع شرطی است که می گوید اگر T عدد بود، نوع مقدار خروجی IdLabel و در غیر این صورت NameLabel است.

برای نمونه در کد زیر نوع متغیر a و b بر اساس نوع ورودی تابع مشخص شده است:

function createLabel<T extends number | string>(value: T): NameOrId<T> {
  ...
}

let a = createLabel("reza"); // a: NameLabel

let b = createLabel(75); // b: IdLabel

نوع های Template Literal

نوع های Template Literal به همان فرمت رشته های Template در جاوا اسکریپت نوشته می شوند و این امکان را به ما می دهند که نوع های ترکیبی union ایجاد کنیم:

type Message = "welcome" | "goodbye";
type Lang = "fa" | "en" | "ar";

type AllMessages = `${Message}_${Lang}`;
// type AllMessages = "welcome_fa" | "welcome_en" | "welcome_ar" | "goodbye_fa" | "goodbye_en" | "goodbye_ar";

یک مثال ترکیبی

در مثال زیر با استفاده از همه موارد بالا نوع یک تابع نسبتا پیچیده را به راحتی می توانیم توصیف کنیم. این تابع یک آبجکت می گیرد و این امکان را می دهد تا برای تغییر ویژگی های آن آبجکت، listener و کال بک تعریف کنیم:

type PropEventSource<Type> = {
    on<Key extends string & keyof Type>
        (eventName: `${Key}Changed`, callback: (newValue: Type[Key]) => void ): void;
};

// به تایپ اسکریپت می گوییم چنین تابعی وجود دارد تا با تعریف آن کاری نداشته باشد
declare function makeWatchedObject<Type>(obj: Type): Type & PropEventSource<Type>;

const person = makeWatchedObject({
  firstName: "Reza",
  lastName: "Akbari",
  age: 26
});

// خطا! frstNameChanged اشتباه است
person.on("frstNameChanged", newName => {
  ...
});

// newName: string
person.on("firstNameChanged", newName => {
  ...
});

// newAge: number
person.on("ageChanged", newAge => {
  ...
});

قدم بعدی

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

برای یادگیری بهتر، علاوه بر مطالعه و تحقیق، سعی کنید تمرینات پروژه محور هم انجام دهید.

برای تست کد تایپ اسکریپت در مرورگر هم می توانید از TS Playground استفاده کنید.


کامنت ها