7 ترفند مهم تایپ اسکریپتی
زمان مطالعه: 12 دقیقه
۱۴۰۲/۶/۱۲

7 ترفند مهم تایپ اسکریپتی

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

1- Interface Types

 به کمک enum  ها، میتوان یک لیست با کلید های ثابت و مشخص داشت و نوع ورودی ها و پارامتر ها را بر آن اساس مشخص کرد. همچنین میتوانیم برای ویژگی های داخل type ها و interface ها از enum ها و دیگر ابزار ها نیز استفاده کنیم؛ در کد زیر خواهید دید که چگونه با ترکیب انواع تایپ ها در نوع ورودی متد، در رفتار آن تاثیر خواهیم بود:

enum CounterActionType {
  Increment = "INCREMENT",
  IncrementBy = "INCREMENT_BY",
}
// رفتار اینکرمنت اکشن که فقط قرار است یک مقداری به علاوه ی ۱ شود
interface IncrementAction {
  type: CounterActionType.Increment;
}
// رفتار اینکرمنت بای که فقط قرار است یک مقداری به علاوه ی پیلود شود
interface IncrementByAction {
  type: CounterActionType.IncrementBy;
  payload: number;
}
// رفتار ها را در قالب یک تایپ جدید نگهداری میکنیم
type CounterAction = IncrementAction | IncrementByAction;

function reducer(state: number, action: CounterAction) {
  switch (action.type) {
    case CounterActionType.Increment:
      // در اینجا تایپ اسکریپت میداند که از نوع اینکرمنت اکشن میباشد و دیگر پیلود ندارد
      return state + 1;
    case CounterActionType.IncrementBy:
      // تایپ اسکریپت میداند که در اینجا یک پیلودی در نوع اینکرمنت بای وجود دارد
      return state + action.payload;
    default:
      return state;
  }
}

همانگونه که دیدید، در تایپ اسکریپت میتوان نوع کارکرد یا رفتار و عمل متد را با تایپ ها مشخص نمود.

۲- Literal Types

اغلب نیاز داریم در متغیر، مجموعه ای دیتا و یا مقادیری مشخص را نگهداری نماییم، پس بهترین گزینه برای این مورد استفاده کردن از Literal Type ها خواهد بود که میزان خطای توسعه دهنده را پایین نگاه می دارد.

type Status = "idle" | "online" | "offline" | "invisible";

این پایان کار نیست! ما میتوانیم از Literal Type ها برای اعداد نیز استفاده کنیم : 

type Review = 1 | 2 | 3 | 4 | 5;

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

// اول به اعداد معنا میدیم
const reviewMap = {
  terrible: 1,
  average: 2,
  good: 3,
  great: 4,
  incredible: 5,
} as const;

// بعد به عنوان تایپ از ان استفاده خواهیم کرد

type Review = typeof reviewMap[keyof typeof reviewMap];

همچنین میتوان از enum ها نیز استفاده کرد:

enum Review {
  TERRIBLE = 1,
  AVERAGE = 2,
  GOOD = 3,
  GREAT = 4,
  INCREDIBLE = 5,
}

۳- Type Guards

گارد ها در تایپ اسکریت روشی برای محدود کردن نوع متغیر هستند، در این روش ما میتوانیم مقدار متغیر یا  ورودی متد را محدود به نوع خاصی از دیتا کنیم؛ به مثال زیر توجه کنید:

function isNumber(value: any): value is number {
  return typeof value === "number";
}

const validateAge = (age: any) => {
  if (isNumber(age)) {
    // validation logic
    // ...
  } else {
    console.error("The age must be a number");
  }
};

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

const validateAge = (age: number) => {
  // ...
};

۴- Index Signature

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

enum PaticipationStatus {
  Joined = "JOINED",
  Left = "LEFT",
  Pending = "PENDING",
}

// تمامی کلید های آبجکت از نوع رشته و مقادیر آنها از نوع تایپ بالا میباشد
interface ParticipantData {
  [id: string]: PaticipationStatus;
}

const participants: ParticipantData = {
  id1: PaticipationStatus.Joined,
  id2: PaticipationStatus.Left,
  id3: ParticipationStatus.Pending,
  // ...
};

۵- Generics Types

این نوع تایپ یک ابزار قدرتمند برای استفاده ی بیشتر از متد با رفتار های متفاوت میباشد؛ ما میتوانیم یک type را به متد یا کلاس خود inject (تزریق) کنیم و نسبت به تایپ تزریق شده، رفتار متد تغییر خواهد کرد و متغیر ها یا المان های داخل متد یا کلاس با آن تایپ برابر خواهد شد:

class KeyValuePair <T, U> {
  private key: T;
  private val: U;

  setKeyValue(key: T, val: U): void {
    this.key = key;
    this.val = val;
  }

  display(): void {
    console.log(`Key = ${this.key}, val = ${this.val}`);
  }
}

let kvp1 = new KeyValuePair <number, string>();
kvp1.setKeyValue(1, "Steve");
kvp1.display(); //Output: Key = 1, Val = Steve 

let kvp2 = new KayValuePair <string, string>();
kvp2.SetKeyValue("CEO", "Bill");
kvp2.display(); //Output: Key = CEO, Val = Bill

مثال فوق برای استفاده از generic تایپ ها در کلاس ها است، در مثال زیر از جنریک ها در فانکشن ها یا توابع استفاده می کنیم:

function getArray<T>(items : T[] ) : T[] {
  return new Array<T>().concat(items);
}

let myNumArr = getArray<number>([100, 200, 300]);
let myStrArr = getArray<string>(["Hello", "World"]);

myNumArr.push(400); // OK
myStrArr.push("Hello TypeScript"); // OK

myNumArr.push("Hi"); // Compiler Error
myStrArr.push(500); // Compiler Error

6- Immutable Types

تایپ های Immutable (غیر قابل تغییر)، تایپ هایی هستند که به توسعه دهنده این اطمینان را خواهند داد که در طول فرایند توسعه، مقادیر به طور تصادفی تغییر نخواهند کرد. این مورد برای نگهداری داده های حساس که نمیخواهیم تایپ آن تغییر یابد مفید خواهد بود:

const ErrorMessages = {
  InvalidEmail: "Invalid email",
  InvalidPassword: "Invalid password",
  // ...
} as const;

// This will throw an error
ErrorMessages.InvalidEmail = "New error message";

۷- Utility Types

گاه در برخی از تایپ های بزرگ، نیازمند اختیاری یا اجباری کردن تعدادی از موارد هستیم، و یا در interface ها و type های پیچیده، نیاز به استفاده برخی از موارد در قالب یک تایپ دیگر داریم. در این هنگام utility type ها چاره کار هستند!:

partial

این نوع تایپ همه چیز را اختیاری میکند:

interface Person {
  name: string;
  age: number;
  email: string;
}

// Define a new type called 'PartialPerson' that is a partial version of 'Person'
type PartialPerson = Partial < Person > ;

// Same as:
// interface Person {
//   name?: string | undefined;
//   age?: number | undefined;
//   email?: string | undefined;
// }

Required

نقطه مقابل partial ، تایپ required میباشد که همه چیز را ضروری خواهد کرد:

interface Person {
  name?: string | undefined;
  age?: number | undefined;
  email?: string | undefined;
 }
 
 // Define a new type called 'RequiredPerson' that is a required version of 'Person'
 type RequiredPerson = Required<Person>;
 
 // Same as:
 // interface Person {
 //   name: string;
 //   age: number;
 //   email: string;
 // }

Omit

این ابزار برای ایجاد یک تایپ جدید از تایپی دیگر میباشد که میتواند یک یا چند ویژگی آن را حذف کرده و تایپ جدید را ایجاد کند:

interface User {
  id: number;
  name: string;
  email: string;
  age: number;
}

type UserWithoutEmail = Omit <User, 'email'> ;

// same as: 
// interface User {
//   id: string;
//   name: string;
//   age: number;
// }

 همچنین میتوانیم چند ویژگی را با کمک union حذف کنیم:

interface User {
  id: number;
  name: string;
  email: string;
  age: number;
}

type UserWithoutEmailAndName = Omit<User, 'email' | 'name'>;

// same as: 
// interface User {
//   id: string;
//   age: number;
// }

Pick

تایپ pick نیز نقطه مقابل omit است، که بجای حذف کردن ویژگی های اضافه، درون تایپ مرجع یک یا چند ویژگی را انتخاب کرده وبا استفاده از آن، تایپی جدید را ایجاد می کند:

interface User {
  id: number;
  name: string;
  email: string;
  age: number;
}

type UserWithEmailAndName = Pick <User, 'email' | 'name'> ;

// same as: 
// interface User {
//   name: string;
//   email: string;
// }

استفاده از ابزار ها در کنار یکدیگر: 

می توان برای ایجاد میانبر و راحتی کار، از ابزار های بالا در کنار یکدیگر نیز استفاده نمود:

interface User {
  id: number;
  name: string;
  email: string;
  age: number;
}

type PartialPick = Partial<Pick<User, 'email' | 'name'>>;

// same as: 
// interface User {
//   name?: string | undefined;
//   email?: string | undefined;
// }

 بررسی مثالی دیگر: 

interface User {
  id: number;
  name: string;
  email: string;
  age: number;
}

type OmitPartialPick = Omit<Partial<Pick<User, 'email' | 'name'>>, 'email'>;

// same as: 
// interface User {
//   name?: string | undefined;
// }

Readonly

این ابزار ویژگی readonly را به تایپ مد نظر اضافه خواهد کرد، یعنی متغیر ما بعد از مقداردهی اولیه، دیگر قابل تغییر یا مقداردهی مجدد نخواهد بود:

interface Person {
  id: number;
  name: string;
  age: number;
}

type ReadonlyPerson = Readonly<Person>;

// same as:
// interface Person {
//   readonly id: number;
//   readonly name: string;
//   readonly age: number;
// }

const person: ReadonlyPerson = {
  id: 1,
  name: 'John',
  age: 25
};

person.name = 'Mike'; // Error: Cannot assign to 'name' because it is a read-only property.

در این مقاله بطور کامل در مورد ۷ ترفند مهم تایپ اسکریپتی صحبت کردیم، همچنین با اکثر utility type ها نیز آشنا شدیم که این موارد، گزینه ها یا ابزار های متعددی را در اختیار توسعه دهندگان قرار خواهند داد.

در پایان لازم به ذکر است که یادگیری تایپ اسکریپت نه تنها خالی از لطف نیست، بلکه در برخی موارد ضروری نیز می باشد! تایپ اسکریپت پیش نیاز فریم ورک هایی مانند انگولار (Angular) و نست جی اس (Nest JS) بوده و همچنین در فریم ورک های نکست جی اس (Next JS) و ویو جی اس (Vue JS) نیز قابل استفاده و بسیار مرسوم است. تایپ اسکریپت هم در فرانت اند و هم در بک اند کاربردی خواهد بود.

 اگه از این مقاله خوشتون اومد لایک و کامنت فراموش نشه ❤️🌹

پروفایل عرفان یوسفی

نویسنده مقاله

از سال ۸۹ - ۹۰ وارد حوزه ی برنامه نویسی شدم و انواع زمینه ها و شاخه های مختلف رو کار کردم تا اینکه سال ۹۵ توی حوزه ی بک اند (نود جی اس) ماندگار شدم، تجربیات خیلی زیادی رو توی این مسیر کسب کردم. شکست ها و موفقیت هایی رو هم داشتم که همه ی این موارد رو در قالب مقاله، دوره و پادکست در اختیارتون خواهم گذاشت خلاصه که وبسایت کدنایت رو سال ۱۴۰۲ توسعه دادیم که یک پلتفرم آموزشی با گروهی از اساتید خفن هستش که قراره کلی محتوا در اختیارتون بذاریم.

دیدگاه ها و پرسش ها