آموزش مقدماتی تایپ اسکریپت [TypeScript]
![آموزش مقدماتی تایپ اسکریپت [TypeScript]](https://rahkarino.ir/wp-content/uploads/2023/03/rahkarino-featuredImg-new.jpg)
درود بر همراهان گرامی آکادمی راهکارینو، ضمن عرض تبریک سال نو (1402) با آموزش “زبان برنامه نویسی تایپ اسکریپت” در خدمت شما هستیم.
برای شروع کار با TypeScript پیش نیازهایی وجود دارد که در ادامه به آنها می پردازیم.
شروع کار با تایپ اسکریپت:
اگر Node.js روی سیستم عاملتان نصب نیست با مراجعه به وب سایت Nodejs.org آخرین نسخه آن را نصب کنید. در زمان نگارش این مقاله، آخرین ورژن 19.8.1 می باشد.
سپس باید یک ادیتور مناسب انتخاب کنید. سعی کنید ویژوال استودیو کد را با مراجعه به وب سایت VisualStudio روی سیستم خود نصب کنید. زیرا هم رایگان است و هم بسیار سبک و کاربرپسند.
سپس نوبت نصب خود تایپ اسکریپت می رسد. برای اینکه Typescript را به شکل گلوبال و سراسری روی سیستم خود نصب کنید از فلگ g- استفاده کنید.
npm i -g typescript
برای اینکه مطمئن شویم تایپ اسکریپت بدرستی نصب شده و نسخه آن را چک کنیم دستور زیر را در ترمینال اجرا کنید.
tsc -v
حالا یک فولدر، مثلا با نام ts-course بسازید و در آن یک فایل بنام index.ts ایجاد کنید. در این فایل دستور ساده زیر را برای شروع کار بنویسید:
let age:number = 10;
جهت اجرای آن و کامپایل کدهای آن به ES5 دستور زیر را در ترمینال ویژوال استودیو کد اجرا کنید:
tsc index.ts
اکنون باید یک فایل بنام index.js در کنار فایل قبلی index.ts ساخته شده باشد که می توانید کد جاوا اسکریپت کامپایل شده را مشاهده کنید.
ایجاد فایل tsconfig:
برای اینکه بتوانیم کانفیگ تایپ اسکریپت را تغییر دهیم و تنظیمات دلخواه خود را اعمال کنیم، نیاز است ابتدا فایل tsconfig را با استفاده از دستور زیر ایجاد کنیم.
tsc --init
اکنون فایل tsconfig.json در فولدر اصلی پروژه ساخته شده است:
فایل tsconfig.json :
بخش target :
تنظیمات مهم:
در یک پروژه واقعی، سورس کد برنامه معمولا داخل یک فولدر بنام src قرار دارند. پس طبق تصویر زیر، گزینه rootDir را برابر src/. تعریف کنید:
در ادامه باید مسیر فایل های کامپایل شده را مشخص کنیم. یعنی فایل index.js در کدام فولدر ایجاد شود. به این منظور در چند خط پایین تر در فایل tsconfig آپشن outDir را برابر dist/. تعریف می کنیم. همچنین اگر بخواهیم حجم فایل نهایی کاهش یابد می توانیم کانفیگ removeComments را فعال کنیم. کانفیگ مهم دیگر noEmitOnError می باشد. کاربرد آن اینست که اگر در فایل typescript خطایی وجود داشته باشد، این فایل کامپایل نخواهد شد و یک فایل js در فولدر dist نمی سازد. پس این خط را نیز فعال کنید:
حال اگر ترمینال را باز کرده و دستور tsc را اجرا کنیم، تمام فایل های تایپ اسکریپت داخل فولدر پروژه ما کامپایل می شوند و فایل js مربوطه در فولدر dist قرار خواهند گرفت.
دیباگ کردن تایپ اسکریپت:
وقتی برنامه دچار خطا می شود نیاز است آن را debug کنیم. برای اینکار در فایل tsconfig آپشن sourceMap را از بخش Emit فعال کنید (از حالت کامنت خارج کنید)
با انجام این کانفیگ وقتی یکبار دیگر برنامه را کامپایل کنید علاوه بر فایل index.js در فولدر dist یک فایل دیگر بنام index.js.map نیز ساخته می شود.
برای تست دیباگ، مطابق تصویر زیر دو دستور به فایل index.ts اضافه کرده و برای خط اول (Line 1) یک break-point قرار دهید (نقطه قرمز)
سپس به پنل Debug در vsCode رفته و مطابق تصویر زیر روی لینک create a launch.json file کلیک کنید.
و در لیستی که نمایش داده می شود گزینه Nodejs را انتخاب کنید. اکنون فایل launch.json باز می شود که شامل چند کانفیگ است. برای تکمیل تنظیمات دیباگ در ویژوال استودیو کد، دستور زیر را در این فایل بعد از گزینه program تعریف کنید:
"preLaunchTask": "tsc: build - tsconfig.json"
محتوای نهایی این فایل مطابق تصویر زیر خواهد بود:
اکنون در پنل دیباگ یک دکمه می بینید بنامه Launch Program یا دکمه F5 پس یکی از آنها را زده و دیباگ برنامه را شروع کنید. برای دیباگ خط به خط برنامه دکمه step over را بزنید.
انواع داده (Data-Type) در تایپ اسکریپت:
عبارتند از: any, array, tuple, enum, function, object
انواع داده Built-in در جاوا اسکریپت و تایپ اسکریپت:
در تعریف متغیر، مطابق دستورات زیر، می توانیم نوع داده را مشخص کنیم:
let sales: number = 123_456; let is_published: boolean = false; let course: string = "TypeScript";
اما اگر نوع متغیر را تعیین نکنیم، تایپ اسکریپت طبق نوع داده ای که مقداردهی کرده ایم آن را تعریف می کند. یعنی اگر داخل متغیر عبارت false را بریزیم، نوع آن بولین خواهد بود.
نوع داده any:
اگر نوع داده را تعیین نکنیم و همچنین هیچ مقدار اولیه ای برای آن تعریف نکنیم، نوع آن any می شود.
اگر تابعی تعریف کنیم و نوع آرگومان ورودی آن را تعریف نکنیم، مطابق تصویر زیر خطا می دهد:
اما اگر نوع آن را any تعریف کنیم این خطا برطرف می شود. روش دیگر برای رفع این خطا اینست که به فایل tsconfig و بخش Type Checking مراجعه کنیم و گزینه noImplicitAny را از کامنت خارج کنیم. و مقدار آن را برابر false قرار دهیم:
"noImplicitAny": false
نکته: روش فوق پیشنهاد نمی شود زیرا تا جای ممکن نباید در تایپ اسکریپت از نوع any استفاده کنیم.
نوع داده آرایه (array):
در جاوا اسکریپت تعریف آرایه از انواع مختلف داده ای امکان پذیر است. پس در تایپ اسکریپت هم می توان اینکار را انجام داد. اما اگر فقط بخواهیم آرایه ای از اعداد را داشته باشیم باید به شکل زیر تعریف کنیم:
let arr: number[] = [1, 2, 3];
حال اگر در آرایه فوق یک آیتم رشته ای، مثلا “4” را اضافه کنیم، خطا می دهد.
یکی از امکانات جالبی که تعریف آرایه های تک نوعی به ما می دهد اینست که می توان در ویژوال استودیو از متدهای مرتبط با آن نوع داده استفاده کرد. مثلا در تصویر زیر مشاهده می کنیم که برای آرایه عددی arr فقط متدهای مربوط به کار با آرایه ها نمایش می یابند.
و در تصویر زیر، برای آرایه رشته ای characters فقط متدهایی که با رشته ها کار می کنند با زدن دکمه های ctrl+space نمایش می یابند (code completion):
نوع داده ای tuple:
نوع Tuple یک آرایه است با تعداد آیتم ثابت و با نوع داده ای مشخص. مثلا در کد زیر گفتیم یک آرایه می خواهیم که شامل دو آیتم است و آیتم اول آن از نوع عددی و آیتم دوم آن از نوع رشته ای می باشد.
let brand: [number, string] = [1, "Rahkarino"];
امکان code-completion در اینجا نیز کاربرد دارد. یعنی اگر بنویسیم brand[0] و دات بزنیم، متدهای مربوط به اعداد نمایش می یابند. هر چند می توان تعداد آیتم های نامحدودی برای آرایه tuple تعریف کرد اما توصیه می شود که tuple فقط دو آیتم داشته باشد. بصورت key, value
نوع داده ای enum:
یکی از انواع داده ای پرکاربرد در زبان تایپ اسکریپت، و همچنین سایر زبان ها مانند سی شارپ یا جاوا، Enum می باشد. برای تعریف چندین constant به کار می رود. فرض کنید می خواهیم سایز لباس را در یک متغیر ذخیره کنیم. به دو روش زیر می توان اینکار را انجام داد:
// method 1: const small = 1; const medium = 2; const large = 3; // method 2: enum Size { Small = 1, Medium = 2, Large = 3, }
مسلما روش دوم یعنی استفاده از enum روش مدرن تر و بهینه تری است. دقت کنید که بطور پیش فرض اعداد یک به بعد برای آیتم های enum در نظر گرفته می شود. اما می توان بجای عدد از رشته هم استفاده کرد مثلا Small = ‘s’
برای استفاده از enum نیز می توان به شکل زیر عمل کرد:
let mySize = Size.Medium;
به تصویر زیر توجه کنید:
برای اینکه خروجی enum را در کنسول مشاهده کنیم، می توان دستور console.log(mySize) را نوشت و برای اجرا باید tsc را در ترمینال اجرا کرد تا فایل index.js ساخته شود و دستور زیر را برای اجرای این فایل اجرا کرد:
node dist/index.js
خروجی m را در ترمینال خواهیم داشت:
نکته: اگر قبل از کلمه enum در هنگام تعریف متغیر Size از کلمه کلیدی const استفاده کنید، کد خروجی کامپایل شده کوتاه تر و بهینه تری خواهید داشت.
نوع داده ای function:
در هنگام تعریف توابع یکی از موارد مهمی که باید سعی کنید رعایت کنید تعریف نوع خروجی تابع می باشد. به خصوص وقتی می خواهید api پیاده سازی کنید. در کد زیر، نوع ورودی تابع calculateTax عددی است و نوع خروجی آن نیز عدد است:
function calculateTax(income: number): number { return 0; }
در کد بالا از آرگومان ورودی income در بدنه تابع استفاده نشده است. در تایپ اسکریپت این امکان وجود دارد که اگر از متغیر یا آرگومانی استفاده نکرده باشیم به ما اخطار بدهد. برای فعالسازی این قابلیت، فایل tsconfig را باز کرده و گزینه زیر را از کامنت خارج کنید:
"noUnusedParameters": true
اکنون اخطار زیر را خواهیم دید:
نکته: اگر بخواهیم به تایپ اسکریپت بگوییم وقتی که یک تابع مقدار بازگشتی مشخص ندارد به ما اخطار دهد، باید گزینه زیر را در فایل tsconfig از کامنت خارج کنیم:
"noImplicitReturns": true
نکته: تنظیمات دیگر tsconfig در زمینه کار با توابع اینست که می توان به تایپ اسکریپت گفت وقتی که یک متغیر لوکال داخل اسکوپ یک تابع تعریف می کنیم و از آن در بدنه همان تابع استفاده نمی کنیم به ما اخطار بدهد. برای اینکار کافیست گزینه زیر را از کامنت در بیاوریم:
"noUnusedLocals": true
نکته: این قابلیت در typescript وجود دارد که بتوان یک یا چند آرگومان ورودی تابع را اختیاری کرد. یعنی بتوان هنگام صدا زدن تابع، آنها را ارسال کرد یا خیر. برای اینکار دو راه داریم.
- استفاده از علامت سوال بصورت arg1?:number
- تعریف مقدار پیش فرض برای آرگومان ورودی مانند arg1 = 2022
روش دوم پیشنهاد می شود. زیرا اگر از روش اول استفاده کنیم و آرگومان را ارسال نکنیم احتمال اینکه به خطا بخوریم زیاد است. اما در روش دوم در صورتی که آرگومان اختیاری ارسال نشود یک مقدار پیش فرض دارد.
نوع داده ای object:
در این نوع داده ای می توان مقادیر key-value داشت. مثلا در کد زیر، آیدی شخص و نام او را داریم:
const person: { readonly id: number; name: string } = { id: 0, name: "Ehsan", }; person.name = "Ali";
با تعریف کلمه کلیدی readonly گفتیم که آیدی غیر قابل تغییر است.
تعریف متد در آبجکت:
در کد زیر یک متد بنام greet برای آبجکت person تعریف کردیم که یک رشته بعنوان ورودی می گیرد و یک پیغام در خروجی چاپ می کند (نوع خروجی آن void است)
const person: { readonly id: number; name: string; greet: (name: string) => void; } = { id: 0, name: "Ehsan", greet: (name: string) => console.log(`Hello ${name}`), }; person.greet("New Year");
پس از اجرا، خروجی زیر را در ترمینال خواهیم داشت:
تایپ های پیشرفته در تایپ اسکریپت:
در ادامه این پست آموزشی، تایپ های پیشرفته تایپ اسکریپت را بررسی میکنیم. مانند:
type aliases, unions & intersections, type narrowing, nullable types, unknown types, never type
Type aliases:
در آخرین مثال مقاله قبل، کد زیر، اگر بخواهیم یک آبجکت دیگر از نوع Person بسازیم، با همان متدها و پراپرتی ها، مجبوریم نوع آنها را مجددا تعریف کنیم که اصلا بهینه نیست و پیشنهاد نمی شود.
const person: { readonly id: number; name: string; greet: (name: string) => void; } = { id: 0, name: "Ehsan", greet: (name: string) => console.log(`Hello ${name}`), };
روش اصولی استفاده از Type Aliases می باشد. یعنی توسط کلمه کلیدی type انواع داده موردنظر را تعریف می کنیم و هر جا که خواستیم از آن استفاده می کنیم. به این شکل:
type Person = { readonly id: number; name: string; greet: (name: string) => void; }; const person: Person = { id: 0, name: "Ehsan", greet: (name: string) => console.log(`Hello ${name}`), };
Union Types:
امکان تعریف دو یا چند نوع داده را فراهم می کند. توسط علامت |
در مثال زیر، نوع ورودی تابع می تواند یا رشته باشد یا عدد.
function kgToLbs(weight: number | string): number { return 0; }
ایرادی که کد فوق دارد اینست که نمی توان از متدهای مختص هر یک از انواع رشته یا عدد استفاده کرد. چون نمی دانیم قرار است به عنوان آرگومان ورودی عدد ارسال شود یا رشته. برای رفع این مشکل باید از type narrowing استفاده کنیم. بدین شکل که تایپ آرگومان ورودی را بررسی می کنیم و در هر بلاک شرطی، از متدهای مربوطه استفاده می کنیم:
function kgToLbs(weight: number | string): number { if (typeof weight === "number") return weight * 2.2; else return parseInt(weight) * 2.2; }
Intersection:
حالت قبل یعنی union بصورت “یا” عمل می کرد. مثلا رشته یا عدد. اما در مواقعی که می خواهیم بصورت “و” عمل کنیم، مثلا هم نوع A و هم نوع B باید از intersection با علامت & استفاده کنیم. به مثال زیر دقت کنید:
type Draggable = { drag: () => void; }; type Resizable = { resize: () => void; }; type UIWidget = Draggable & Resizable; let textBox: UIWidget = { drag: () => {}, resize: () => {}, };
در کد فوق، نوع داده UIWidget از ترکیب دو نوع Draggable و Resizable ساخته می شود. پس وقتی textBox را تعریف می کنیم باید هر دو متد drag و resize را برای آن مقداردهی کنیم.
Literal Types:
وقتی می خواهیم نوع داده را به دو یا چند مورد خاص محدود کنیم، در واقع از literal types ها استفاده کرده ایم. مثلا فرض کنید یک نوع داده ای عددی داریم که فقط می تواند اعداد 50 و 100 را شامل شود. کد زیر را در نظر بگیرید:
type Quantity = 50 | 100; let quantity: Quantity = 100; type Metric = "cm" | "inch"; let width: Metric = "cm";
Nullable Types:
در جاوااسکریپت می توان هنگام فراخوانی توابع، بجای ارسال نوع رشته، null ارسال کرد. این قضیه می تواند منجر به بروز خطا در برنامه شود. اما در تایپ اسکریپت بطور پیش فرض نمی توان اینکار را انجام داد. اما اگر می خواهید تنظیمات اولیه تایپ اسکریپت را تغییر دهید و امکان استفاده از nullable type ها را داشته باشید باید در فایل tsconfig و در بخش type-checking گزینه strictNullChecks را از کامنت خارج کرده و آن را برابر false قرار دهید. دقت کنید که انجام اینکار توصیه نمی شود. مگر اینکه خودتان در برنامه بصورت دستی با دستورات شرطی (مانند if) null بودن داده را بررسی کنید تا باعث بروز خطا نشود. به مثال زیر توجه کنید:
function greet(name: string | null) { if (name) { console.log(name.toUpperCase()); } } greet(null);
Optional Chaining:
وقتی با انواع داده null یا undefined کار می کنیم باید بطور کامل و با وسواس زیاد آنها را در برنامه خود چک کنیم. زیرا اغلب ارورهای برنامه بخاطر این نوع داده ها می باشد. همانطور که در مثال قبل گفتیم می توان null یا undefined بودن یک متغیر را در if بررسی کرد اما یک روش راحتتر برای اینکار استفاده از علامت سوال یا optional chaining می باشد. به مثال زیر توجه کنید:
type Customer = { birthday?: Date; }; function getCustomer(id: number): Customer | null { return id === 0 ? null : { birthday: new Date() }; } let customer = getCustomer(0); console.log(customer?.birthday?.getFullYear());
روش فوق را می توان برای دسترسی به آیتم های یک آرایه نیز اعمال کرد. به منظور دسترسی به یک آیتم از آرایه، هم می توان توسط دستور if عملیات null checking را انجام داد و هم از optional chaining استفاده کرد. به این صورت: myArr?.[0]
همچنین از علامت سوال می توان هنگام صدا زدن یک تابع استفاده کرد. یعنی یک تابع در صورتی فراخوانی شود که برابر null یا undefined نباشد. به این صورت: ().?func
Nullish Coalescing:
در مواقعی که می خواهیم null یا undefined بودن یک داده را بررسی کنیم، به کار می رود و با دو علامت سوال یعنی ?? مشخص می شود. فرض کنید در برنامه خود می خواهیم speed را بررسی کنیم. اگر از دستور speed || 30 برای مقداردهی سرعت استفاده کنیم، وقتی سرعت صفر است مقدار 30 در نظر گرفته می شود زیرا عدد صفر یک falsy value است. اما اگر ما بخواهیم صفر هم قبول کنیم این روش کاربرد ندارد. پس باید از عملگر nullish coalescing استفاده کنیم. یعنی speed ?? 30 در اینحالت، اگر speed برابر null یا undefined باشد مقدار 30 برای آن تعریف می شود.
Type Assertion:
در حالت پیش فرض اگر بخواهیم یک رفرنس از المان در DOM را در متغیری ذخیره کنیم (توسط متد document.getElementById(“phone”)) نوع آن HTMLInputElement | null خواهد بود. زیرا تایپ اسکریپت فرض می کند که احتمال دارد این المان در dom پیدا نشود. اما اگر شما به عنوان توسعه دهنده برنامه مطمئن هستید که این المان در صفحه وجود دارد می توانید از قابالیت type-assertion استفاده کنید. یعنی به تایپ اسکریپت بگویید که من در این مورد از تو بیشتر میدونم !!!
به مثال زیر دقت کنید:
let phone = document.getElementById("phone") as HTMLInputElement; console.log(phone.value);
در این مثال، بعد از نوشتن phone و زدن کلیدهای ctrl+space می توان به متدهای مربوط به المان های DOM دسترسی داشت. بصورت code-completion
نکته: در استفاده از این قابلیت باید احتیاط کرد و فقط زمانی از as استفاده کنیم که مطمئن هستیم آن المان در DOM قرار دارد.
Unknown Types:
در نگاه اول شاید بنظر برسد نوع unknown با any یکی است. اما در واقع متفاوت هستند. استفاده از نوع unknown اولویت دارد. یعنی در مواقعی که نمی دانید یک داده چه تایپی دارد بهتر است از unknown استفاده کنید. زیرا کامپایلر تایپ اسکریپت به توسعه دهنده اجازه نمی دهد که هر جور دلش خواست با آن داده رفتار کند و مثلا هر متدی را روی آن فراخوانی کند. در واقع شما را مجبور می کند که در یک بلاک شرطی مثلا if نوع داده را بررسی کنید و طبق آن متدهای مربوطه را روی آن اعمال کنید. به عنوان مثال:
function render(document: unknown) { if (typeof document === "string") { console.log(document.toLowerCase()); } if (document instanceof CustomType) { // do something... } }
نکته: وقتی می خواهیم در یک بلاک شرطی نوع متغیری را بررسی کنیم، اگر نوع موردنظر ما primitive باشد، مانند string و number باید از کلمه کلیدی typeof استفاده کرد اما اگر یک تایپ شخصی است که خودمان تعریف کرده ایم باید از instanceof استفاده کنیم.
خب دوستان عزیز، در اینجا به پایان مقاله آموزشی تایپ اسکریپت می رسیم. لطفا اگر سوال یا پیشنهادی در زمینه این پست آموزشی دارید از طریق فرم ارسال دیدگاه پایین مقاله با ما در ارتباط باشید.
در مقاله بعد، درباره جنریک تایپ ها (Generics) در تایپ اسکریپت (TypeScript) صحبت خواهیم کرد.
ارادتمند شما، احسان صفری
دیدگاهتان را بنویسید