درک انواع در تایپ اسکریپت — به زبان ساده
امروزه شاهد هستیم که اغلب کتابخانهها از تایپ اسکریپت پشتیبانی میکنند و از این رو به احتمال بالا علاقهمند هستید که از تایپ اسکریپت در پروژههای آتی خود استفاده کنید. دقت کنید که به این منظور نوشتن کد در فایل ts. کفایت نمیکند، زیرا کامپایلر تایپ اسکریپت سختگیری کمی دارد و بسیاری از توسعهدهندگان میل دارند که کد جاوا اسکریپت را بدون هیچ نوع «توضیح نوع» (type annotations) بنویسند و این وضعیت موجب میشود که کل مزیت استفاده از انواع در تایپ اسکریپت از دست برود. در همین راستا و در ادامه این مطلب به انواع (Types) در تایپ اسکریپت خواهیم پرداخت.
1. انواع مقدماتی
در ادامه مثالی از یک نوع مقدماتی را میبینید:
1const name: string = 'Frudo';
این مثال کاربرد نوعها را در تایپ اسکریپت به شکل زیر نشان میدهد:
1let variable_name: data_type = value
در مورد نوعهای ساده، در صورتی که انتساب متغیر در همان خط اعلان صورت بگیرد، میتوان از نوشتن data_type خودداری کرد:
1const name = 'Frudo';
کامپایلر تایپ اسکریپت نوع متغیری را که به نام Type Inference شناخته میشود خود استنباط میکند و میتوانیم از نوشتن کد غیرضروری اجتناب کنیم.
انواع مختلف متغیرهای مقدماتی در تایپ اسکریپت به شرح زیر هستند:
- بولی (درست یا نادرست)
- عدد (صحیح و اعشاری)
- رشته
- آرایه
- Any (میتواند هر نوع دادهای را شامل باشد)
در ادامه مثالی از هر یک از انواع فوق را مشاهده میکنید:
1const flag: boolean = true;// boolean
2const count: number = 10;// number
3const name: string = 'Fred';// string
4
5const names: string[] = ['Fred', 'Alice'];// Array of string
6const temp: Array<number> = [1, 4, 2.5, 100] // Array of number
7
8const a: any = 'hello'// any type
9const b: any = 5// any type
تعیین نوع داده میتواند بسیار مفید باشد، زیرا به یافتن خطاهای رایج مرتبط با انتساب کمک میکند.
هشدار: از کاربرد نوع any خودداری کنید، زیرا مانند این است که کد معمولی جاوا اسکریپت مینویسید.
1let count;// any type by default
2count = 7;// no error
3count = 'wow'// no error
4
در مثال فوق count از نوع any است که نوع پیشفرض مورد استفاده از سوی جاوا اسکریپت در زمان انتساب است. این اشتباه با استفاده از انواع زیر به راحتی قابل اجتناب است:
1let count: number;// number type
2count = 7;// no error
3count = 'wow'// compile time error
2. نوع تابع
در تایپ اسکریپت میتوانیم انواعی برای تابع نیز تعریف کنیم. این وضعیت برای تعریف تابع callback که یک الگوی رایج در جاوا اسکریپت محسوب میشود کاملاً مفید است.
ابتدا باید یک تابع تعریف کنیم و به آن یک حاشیهنویسی نوع بدهیم:
1function sum(num1: number, num2: number): number {
2 return num1 + num2;
3}
در کد فوق ما نوع عددی را به متغیرهای num1 و num2 دادهایم و نوع بازگشتی تابع نیز پس از دونقطه آخر، به صورت عددی تعیین شده است. دقت کنید که در مورد تابعهای arrow ساختار فوق متفاوت خواهد بود:
1const sum: (num1: number, num2: number) => number = (num1, num2) => num1 + num2;
ممکن است متوجه شوید که کد فوق کمی سردرگمکننده است، بنابراین آن را به دو بخش تجزیه کرده و متغیرها را به جای const با let تعریف میکنیم:
1let sum: (num1: number, num2: number) => number;
در کد فوق یک متغیر به نام sum تعریف کردهایم و نوع تابع به آن دادهایم که دو پارامتر از نوع number میگیرد و نوع بازگشتی نیز number است. سپس پیادهسازی تابع arrow را به آن انتساب میدهیم:
1sum = (num1, num2) => num1 + num2;
در کد فوق متغیر sum را با پیادهسازی تابع arrow انتساب میدهیم. یک نکته دیگر که باید اشاره کنیم این است که اگر تابع هیچ مقداری بازگشت ندهد، نوع بازگشتی آن void خواهد بود که به معنی عدم بازگشت نوع است.
1sum = (num1, num2) => num1 + nm2;
در نهایت نوع تابع را با چند مثال کاربردی جمعبندی میکنیم:
1/*
2lets create a function which checks whether a string starts with
3 a vowel and returns true or false based on it
4*/
5function isVowel(str: string): boolean {
6 const firstChar = str.charAt(0).toLowerCase();
7 const vowels = ['a', 'e', 'i', 'o', 'u'];
8 for(const v of vowels) {
9 if(v === firstChar) {
10 return true;
11 }
12 }
13 return false;
14}
15
16isVowel('hello') //false
17isVowel(34) //error
18
19/*
20lets create that same function which checks whether a string starts with
21 a vowel and returns true or false based on it but using arrow function
22*/
23
24let isVowel2: (str: string) => boolean;
25
26// this will compile
27isVowel2 = ( str: string) => {
28 const firstChar = str.charAt(0).toLowerCase();
29 const vowels = ['a', 'e', 'i', 'o', 'u'];
30 for(const v of vowels) {
31 if(v === firstChar) {
32 return true;
33 }
34 }
35 return false;
36 }
37
38// This will not compile beacuse function type is not matching
39isVowel2 = (str: number) => {
40 return true;
41}
42
43
44/*
45 let use function type in callbacks where it will be really useful
46 if string passed as parameter stats with vowel then call done function
47 else dont call it
48 */
49
50function complexTask(str: string, done: () => void) {
51 if(isVowel(str)) {
52 done();
53 }
54}
55
56
57complexTask('hello', () => console.log('Done called')) // done is not called
58
59
60complexTask('apple', () => console.log('Done called')) // done is called
3. چندتاییها
اگر تاکنون کنجکاو شدهاید که چگونه میتوان بیش از یک مقدار را در تابع بازگشت داد، باید بگوییم که باید از چندتاییها (Tuples) استفاده کنید که امکان بازگشت بیش از یک مقدار با نوع داده متفاوت را فراهم میسازد:
1const example: [int, string] = [10, 'Hi'];
چندتاییها در واقع در پسزمینه آرایههایی هستند که تایپ اسکریپت آنها را در یک نوع یکتا پوشش داده است تا بهبودهایی داشته باشند. چندتاییها میتوانند انواع دادههای متفاوتی را کنار یکدیگر نگهداری کنند و با استفاده از اندیسها همچون آرایه به آنها دسترسی داشته باشید. اما چندتاییها تفاوتهایی نیز با آرایهها دارند. هر اندیس در چندتایی میتواند تنها یک نوع داده را نگهداری کند که در زمان اعلان چندتایی تعیین شده است. برخلاف زبانهای دیگر برنامهنویسی، چندتاییها در تایپ اسکریپت «تغییرپذیر» (mutable) هستند و میتوان مقادیری را به چندتایی ارسال و از آن برداشت کرد.
1//tuple
2let example: [number, string, boolean] = [10, 'hi', false];
3
4const num1 = example[0]; //number type
5
6const str = example[1].substr(0, 1); //string type
7
8example = [23, 'hi23', true]; // it wil complie
9
10// error because 1st index should be a number
11example = ['f', 'h', true];
12
13// error
14example[5].substr(0, 1);
همچنین میتوان با دادن نوع داده خاص به یک اندیس، امکان تکمیل خودکار داشت که در آرایهها وجود ندارد. گرچه چندتاییها تغییرپذیر هستند، اما باید تا حد امکان از این کار اجتناب کرد. یکی از نمونه کاربردهای واقعی چندتاییها در React جایی است که قلابهای ()useState چندتایی بازگشت میدهند.
4. انواع تهیپذیر
جاوا اسکریپت دو نوع undefined و null عرضه میکند که نماینده مقدار خالی یا مقدار تهی هستند و این وضعیت با زبانهای جاوا و سی شارپ مشابهت دارد. این نوع داده همچنین یکی از رایجترین خطاهای زمان اجرا است که در زمان برنامهنویسی در جاوا اسکریپت با آن مواجه میشویم. تایپ اسکریپت نیز این دو نوع را دارد و میتوان آنها را به هر نوع دادهای انتساب داد.
1let str: string;
در کد فوق str مقدار undefined دارد، زیرا هرگز هیچ مقداری به آن انتساب نیافته است گرچه نوع داده آن رشته است. هر عملیاتی روی str موجب بروز خطای زمان اجرا میشود و از این رو باید با استفاده از گزاره if بررسی کنیم که آیا شامل مقداری است یا نه و سپس عملیات مورد نظر را انجام دهیم.
1if(str) {
2 str.length;
3}
تایپ اسکریپت به صورت پیشفرض خطاهای زمان اجرای undefined و null را در زمان کامپایل در نظر نمیگیرد. یک فلگ به نام strictNullChecks وجود دارد که میتوان در فایل tsconfig.json به صورت true تعیین کرد و موجب میشود که تایپ اسکریپت آنالیز گردش کد را برای کدهای نامطمئن با undefined و null انجام دهد و خطای مورد نظر را به جای زمان اجرا در زمان کامپایل ارائه کند.
1{
2 "include": ["src/**/*"],
3 "exclude": ["node_modules", "**/*.spec.ts"],
4 "compileOnSave": false,
5 "compilerOptions": {
6 ...
7 "strict": true, // All strict flags are turned on
8 "strictNullChecks": true, // Null checks for nullable types
9 ...
10 }
11}
زمانی که strictNullChecks به صورت true باشد، دستور let str: string; خطایی ایجاد میکند، زیرا نمیتوانیم undefined را به نوع رشته انتساب دهیم. برای این که بتوانیم undefined را به str انتساب دهیم، باید آن را به صورت زیر اعلان کنیم:
1let str: string | undefined;
این یک نوع «تهیپذیر» (nullable) نامیده میشود. تایپ اسکریپت امکان دسترسی مستقیم به انواع تهیپذیر را به ما نمیدهد، اما در عوض میتوانیم آنها را مورد بررسی قرار دهیم.
1let str: string | undefined = 'Hello';
2str.length; // error
3// Access like below
4if(str) {
5 str.length;
6}
این کار باعث میشود که کد ما پایداری بیشتری پیدا کند و از دسته کاملی از استثناهای زمان اجرا جلوگیری کنیم. این بررسی تهی بودن چنان قدرتمند است که وقتی تیم تایپ اسکریپت آن را به یک قابلت این زبان تبدیل کردند تا در کامپایلر استفاده شود، در کد خودشان باگهای کشف نشدهای را یافتند.
نکته: null نمیتواند به let str: string|undefined انتساب یابد، زیرا null و undefined انواع متفاوتی در تایپ اسکریپت در نظر گرفته میشوند. برای این که بتوانیم null را انتساب دهیم باید متغیر را به صورت زیر تعریف کنیم:
1let str: string | null | undefined
بدین ترتیب میتوان هر دو نوع undefined و null را به str انتساب داد. برخی اوقات هنگامی که میدانیم متغیر مفروض شامل undefined یا null نخواهد بود، میتوانیم از type assertion برای حذف الزامی null استفاده کنیم:
1let str: string | undefined = 'Hello';
2str!.length; // it will work
در این کد علامت تعجب (!) برای حذف نوع تهیپذیر از str استفاده میشود و با آن length را فراخوانی میکند. type assertion باید با دقت و تنها در مواردی که اطمینان کامل دارید متغیر مفروض تهیپذیر نخواهد بود، مورد استفاده قرار گیرد.
1/*
2This function return 1st half part of the string if it is of even length
3otherwise not return anything
4*/
5function firstHalf(str: string): string | undefined {
6 const l = str.length;
7 if(l % 2 == 0) {
8 return str.substr(0, l/2);
9 } else {
10 return undefined;
11 }
12}
13
14let str1 = firstHalf('Hello'); // Variable is assigned nullable
15
16str1.length //error
17
18str1!.length // compiles
19
20if(str1) {
21 str1.length //compiles
22}
23
24str1 = null // error
25
26str1 = undefined // compiles
27str1 = '' //compiles
28
29let str2: string | null | undefined = str1 // compiles
30
31str2 = null // compiles
5. انواع شمارشی
انواع شمارشی یا Enum-ها مانند ثابتهایی هستند که از نظر منطقی با هم دستهبندی شدهاند تا به بهبود خوانایی ثابتها از سوی انسان کمک کنند و کاربردهای مفید زیادی دارند. انواع شمارشی در زبانهای دیگر مانند جاوا و سی شارپ نیز وجود دارند و از این نظر تفاوتی با انواع شمارشی در تایپ اسکریپت ندارند. انواع شمارشی به صورت زیر تعریف میشوند:
1enum Direction {
2 North, South, East, West
3}
هر متغیر از نوع شمارشی میتواند تنها مقادیری که از سوی enum تعریف شده را داشته باشد به مثال زیر توجه کنید:
1let d: Direction = Direction.North
2d = Direction.South
3d = "East" // error
4// Checking for value
5d == Direction.South // true or false
6d == "South" // error
چنان که میبینید انواع شمارشی به ما امکان میدهند که قیود بیشتری بگذاریم و از نظر خوانایی انسانی بسیار بهتر از const هستند. انواع شمارشی به صورت پیشفرض مقادیر عددی که از 0 آغاز میشود میگیرند، اما میتوانید به صورت دستی مقادیر دیگری نیز به آنها بدهید.
1enum Direction {
2 North=1, South=6, East=5, West=2
3}
همچنین میتوانید مقادیر رشتهای به انواع شمارشی بدهید. توجه داشته باشید که انواع شمارشی تنها مقادیر عددی و رشتهای میتوانند بگیرند.
1enum Direction {
2 North='north', South='south', East='east', West='west'
3}
6. اینترفیسها
در جاوا اسکریپت غالباً اشیای زیادی به صورت درجا ساخته میشوند و در متغیر ذخیره شده و به تابعی ارسال میشوند. اینترفیسها امکان تعریف ساختن این اشیا را به دست میدهند به طوری که میتوانیم کدی را با این اطمینان بنویسیم که یک مشخصه خاص در آن شیء وجود دارد:
1interface IProduct {
2name: string;
3description: string;
4price: number;
5}
به طور کلی نام اینترفیس با I آغاز میشود تا بین کلاسها و اینترفیسها تمییز قائل شویم اما این حالت الزامی نیست. زمانی که اینترفیس تعریف شد، میتواند مانند هر نوع دیگری مورد استفاده قرار گیرد.
1let book: IProduct = {
2name: 'Typescript guide',
3description: 'Guide for beginners',
4price: 100
5};
6book = {name: 'Typescript'}; //error, all properties are not defined
7function displayPrice(product: IProduct) {
8console.log(product.price);
9}
کامپایلر تایپ اسکریپت برابری ساختاری اینترفیسها را بررسی میکند. در این حالت باید همه مشخصههای تعریف شده از سوی اینترفیس در شیء وجود داشته باشند، چون در غیر این صورت خطای زمان کامپایل تولید میشود. همچنین میتوانیم اینترفیس بینام تعریف کنیم:
1let custom: { id: number, name: string }; // anonymous interface
2custom = {id: 45, name: 'Frudo'};
3custom = {id: '45', name: 'Frudo'}; // error, id is number
اینترفیسها میتوانند بسیار قدرتمند باشند، زیرا به گروهبندی منطقی مشخصهها کمک میکنند و آن دسته از مشخصهها میتوانند از نوع تابع، شمارشی، تهیپذیر و یا حتی یک اینترفیس دیگر باشند.
1interface IProduct {
2 name: string;
3 description: string
4 price: number;
5}
6
7
8let book: IProduct = {
9 name: 'Typescript guide',
10 description: 'Guide for beginners',
11 price: 100
12};
13
14interface ICustomer {
15 name: {first: string, last: string};
16 products: IProduct[];
17 dob?: string; // Nullable type, which means it is optional interface property
18}
19
20// dob is optional
21const customer1: ICustomer = {
22 name: {first: 'Allan', last: 'Smith'},
23 products: []
24}
25
26const customer2: ICustomer = {
27 name: {first: 'Frudo', last: 'Smith'},
28 products: [{name: 'book', description:'', price: 50}],
29 dob: '7/02/1994',
30 a: '' // error, 'a' is not property of ICustomer
31}
اینترفیسها به طور عمده به عنوان مدل مانند مدلهای API برای پاسخ JSON استفاده میشوند. ایجاد مدلهای اینترفیس به تکمیل خودکار از سوی IDE، بررسی خطا و همچنین به عنوان یک مستندسازی مستقل، کمک میکند.
سخن پایانی
بدین ترتیب در این مقاله اطلاعاتی در مورد پرکاربردترین انواع در تایپ اسکریپت و روش استفاده از آنها به دست آوردیم. هر چه پروژهای بزرگتر شود، مفید بودن این وضعیت افزایش مییابد و چنان که میدانیم پروژههای واقعی در عمل گاهی بسیار بزرگ میشوند. این اندکی کار بیشتر موجب میشود که گامهای بعدی پروژه آسانتر برداشته شوند.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای جاوا اسکریپت
- مجموعه آموزشهای برنامهنویسی
- مجموعه آموزشهای دروس علوم و مهندسی کامپیوتر
- راهنمای جامع تایپ اسکریپت (Typescript) — از صفر تا صد
- قابلیتهای کمتر شناخته شده TypeScript — راهنمای کاربردی
- نکات و ترفندهای تایپ اسکریپت — راهنمای کاربردی
==