Interface در تایپ اسکریپت – از ساخت تا استفاده + مثال و کد
![Interface در تایپ اسکریپت – از ساخت تا استفاده + مثال و کد](https://blog.faradars.org/wp-content/webp-express/webp-images/doc-root/wp-content/uploads/2024/01/js-interface10-1.jpeg.webp)
«تایپ اسکریپت» (TypeScript) در قلمرو جاوا اسکریپت محبوبیت قابلتوجهی به دست آورده است و ابزارهایی را برای تیمهای برنامه نویسی فراهم میکند تا با ترکیب ویژگیهای تعیین نوع پویا و ایستا، بهرهوری خود را افزایش دهند. این ویژگیها کاربران را قادر میسازد تا کد واضحتر و توصیفیتری ایجاد کنند که این قابلیت تشخیص و حل زودهنگام خطا را تسهیل میکند. یکی از این ویژگیهای مهم، ویژگی Interface در تایپ اسکریپت است که به عنوان نوعی ابزار کمکی به برنامه نویسان بسیار کمک خواهد کرد. در این مطلب از «مجله فرادرس»، «رابط یا واسط» (اینترفیس | Interface) در تایپ اسکریپت را مورد بررسی و بازبینی قرار خواهیم داد و در سناریوهایی کاملاً عملی به توضیح آن خواهیم پرداخت. کاربران در پایان مطالعه این مطلب درک نسبتاً صحیح و مناسبی از مفهوم Interface در TypeScript به دست خواهند آورد.
مفهوم Interface در تایپ اسکریپت
در زبان برنامه نویسی جاوا اسکریپت، توسعهدهندگان سطح قابلتوجهی از انعطافپذیری را در مورد نوع مقادیر اختصاص داده شده به متغیرها دارند. متغیر که در ابتدا اعلام و مقداری از نوع عدد صحیح به آن اختصاص داده شده است، میتواند متعاقباً با مقدار متفاوت، مانند نوعی تابع، در طول اجرای برنامه تخصیص داده شود. این انعطافپذیری یکی از ویژگیهای تعیین نوع پویا در جاوا اسکریپت است که به متغیرها اجازه میدهد تا نوع خود را به صورت پویا در طول زمان اجرا تغییر دهند.
همانطور که در مثال زیر نشان داده شده است، انواع متغیرها در جاوا اسکریپت از پیش تعیین نشدهاند:
1var a = 2
2a = function () {
3 console.log("I was initialized with a value of an integer, but now I'm a function")
4}
برای درک بهتر این موضوع، سناریوی ساخت خودروی تسلا مدل S بدون مشخصات از پیش تعریف شده را در قالب مثالی نظر بگیرید. ۱۰ مهندس به طور مستقل نمونههای اولیه را میسازند که هر کدام دارای مجموعهای از مشخصات خاص خود هستند. به عنوان مثال، یک نمونه اولیه ممکن است جزئیات شارژ خودرو را نمایش دهد، در حالی که نمونه دیگر دارای سیستم نظارت بر تایر و غیره است.
![یک خودروی تسلا در حال حرکت](https://blog.faradars.org/wp-content/uploads/2024/01/A-Tesla-car-on-the-road.jpg)
اگر مشخصات از قبل تعیین شده بود، مهندسان میتوانستند به مجموعهای از دستورالعملها پایبند باشند و فرآیند پیادهسازی را سادهتر کنند. این به موازات چالشی است که کاربران هنگام ساخت موجودیتهای پیچیده در جاوا اسکریپت با آن مواجه میشوند.
1function buildTeslaModelS (teslaObj) {
2 // Implementation Details
3}
4
5buildTeslaModelS({
6 length: 196,
7 width: 86,
8 measureTirePressure: function () {
9 },
10 wheelbase: 116
11})
12
13buildTeslaModelS({
14 length: "196",
15 width: "86",
16 wheelbase: "116",
17 measureRemCharging: function () {
18 }
19})
تابع buildTeslaModelSماشین تسلا مدلSرا بر اساس پارامترهای teslaObjارائهشده تولید میکند. با این حال، فرضیاتی را در مورد پارامترهای ورودی ایجاد خواهد کرد و انتظار دارد که طول، عرض و فاصله محوری ویژگیهای اعداد صحیح باشد. در نتیجه، زمانی که این مفروضات برآورده نمیشوند، مانند زمانی که مقادیر به صورت رشته ارائه میشوند، خطاهای زمان اجرا رخ میدهد. علاوه بر این، تابع buildTeslaModelSفاقد آگاهی از ویژگی measureRemChargingاست که منجر به خطا میشود. buildTeslaModelSفرض میکند که measureRemChargingبرای همه مدلها اجباری است و در صورت عدم وجود این ویژگی، خطای زمان اجرا ایجاد میشود.
برای افزایش این انعطافپذیری، مکانیزمی برای انتقال ساختار مورد انتظار پارامتر teslaObjبه تابع buildTeslaModelSموردنیاز است. این امکان بررسی اعتبار سنجی زمان کامپایل را برای ویژگیهای اجباری و انواع آنها فراهم میکند و از اجرای قویتر و مقاومتر در برابر خطا اطمینان میدهد؛ اما مثال بالا چه کمکی بی درک مفهوم Interface در تایپ اسکریپت خواهد کرد؟ در ادامه به این مسئله خواهیم پرداخت.
![یک لپ تاپ روی میز](https://blog.faradars.org/wp-content/uploads/2024/01/Close-up-of-a-laptop-on-an-empty-desk-displaying-faded-lines-of-programming-codes.jpg)
درک مفهوم Interface در تایپ اسکریپت از مثال بالا
اینترفیس در TypeScript نوعی ابزار ارزشمند برای ارائه ساختار و وضوح در کد است. اساساً، رابط به عنوان نوعی طرح اولیه عمل میکند و بدون دیکته کردن و پیچیدگی خاصی مشخص خواهد کرد که موجودیت باید الزاماتی را برآورده سازد. مثال بالا با نشان دادن نوعی چالش رایج در توسعه جاوا اسکریپت، به ایجاد زمینه برای درک مفهوم اینترفیس در TypeScript کمک میکند. در جاوا اسکریپت، انعطافپذیری انواع متغیر میتواند منجر به مشکلاتی شود، بهویژه هنگام ساخت موجودیتهای پیچیده مانند خودروی تسلا مدل Sبدون مشخصات از پیش تعریف شده. مثال ساخت ماشین تسلا نشان میدهد که بدون ساختار یا قرارداد مشخصی که ویژگیهای مورد انتظار و انواع آنها را تعریف میکند، توسعهدهندگان ممکن است با خطاهای زمان اجرا مواجه شده و در حفظ ثبات در بین پیادهسازیها با مشکلاتی روبهرو شوند.
در زمینه مثال تسلا مدل S، تایپ اسکریپت به کاربران اجازه میدهد تا نوعی رابط ایجاد کنند که ویژگیها و عملکردهای ضروری را که باید در هر پیادهسازی وجود داشته باشد، مشخص میکند. این رابط یا همان اینترفیس به عنوان نوعی قرارداد عمل کرده و جنبههای ضروری را بدون پرداختن به جزئیات اجرای آنها شرح میدهد. رابط کاربری تسلا مدل اس در اینجا آمده است:
1interface TeslaModelS {
2 length: number;
3 width: number;
4 wheelbase: number;
5 seatingCapacity: number;
6 getTyrePressure: () => number;
7 getRemCharging: () => number;
8}
این رابط بهوضوح ویژگیها را فهرست میکند، انواع آنها را مشخص کرده و توابعی مانند getTyrePressureو getRemChargingرا تعیین خواهد کرد. همچنین این رابط انواع بازگشت آنها را با جزئیات توضیح میدهد. با پایبندی به این رابط، توسعهدهندگان میتوانند پیادهسازیهای سازگار و قابلاعتمادی از تسلا مدل Sایجاد کنند و کیفیت کد را ارتقاء داده و احتمال خطا را کاهش دهند. همچنین در مثال فوق اکنون، هنگام استفاده از این رابط، تایپ اسکریپت بررسیهای زمان کامپایل را برای اطمینان از مطابقت اشیا ارسال شده به توابعی مانند buildTeslaModelSبا ساختار تعریف شده ارائه میدهد. این ویژگی به شناسایی خطاهای احتمالی در مراحل اولیه توسعه کمک میکند و ثبات کد را ارتقا میدهد.
نحوه استفاده از رابط در تایپ اسکریپت
برای استفاده از Interface در تایپ اسکریپت، وجود نوعی کلاس یا تابع میتواند این رابط را برای مشخص کردن پیادهسازی خاصی آماده کند. بیاید اجرای تابعی را در نظر بگیریم که به رابط TeslaModelSپایبند است و تا حدود زیادی درک رفتار و نحوه ساخت اینترفیس را برای ما مشخص خواهد کرد:
1function buildTeslaModelS(teslaObj: TeslaModelS) {
2}
تلاش برای فراخوانی این تابع با شیئی که با اینترفیس هماهنگ نیست، نوعی خطای کامپایلر تایپ اسکریپت را ایجاد میکند، همانطور که در زیر نشان داده شده است:
1buildTeslaModelS({
2 length: "196",
3 width: "86",
4 wheelbase: "116",
5 measureRemCharging: function () {
6 }
7})
کامپایلر به دو دلیل زیر خطا ایجاد میکند:
- طول، عرض و «فاصله بین دو محور» (wheelbase) انتظار میرود در ساخت ماشین از نوع عددی باشند اما به صورت رشتهها ارائه میشوند.
- ویژگی measureRemChargingدر رابط تعریف نشده است. این ویژگی باید getRemChargingنامیده شود و نوعی عدد صحیح بازگرداند تا از انطباق با قرارداد رابط اطمینان حاصل کند.
برای انطباق با رابط و ساخت تسلا مدل S، تابع باید به صورت زیر اجرا شود:
1function buildTeslaModelS (teslaObj: TeslaModelS) {
2}
3
4buildTeslaModelS({
5 length: 196,
6 width: 86,
7 wheelbase: 116,
8 seatingCapacity: 4,
9 getTyrePressure: function () {
10 let tyrePressure = 20 // Evaluated after doing a few complex computations!
11 return tyrePressure
12 },
13 getRemCharging: function () {
14 let remCharging = 20 // Evaluated after doing a few complex computations!
15 return remCharging
16 }
17})
این پیادهسازی دقیقاً با انتظارات ذکر شده در رابط مطابقت دارد و اطمینان میدهد که شی ارائه شده به ساختار و عملکرد مشخص شده پایبند است.
تعیین Duck Typing در تایپ اسکریپت چیست؟
«تعیین نوع اردکی» (Duck Typing) که به عنوان زیر مجموعه یا بخشی از تعیین نوع ساختاری نیز شناخته میشود، نقش مهمی در بررسی نوع در تایپ اسکریپت ایفا میکند. این نوع سیستم بهجای نوع یا کلاس صریح، بر ارزیابی ساختار و شکل شی تمرکز دارد. در تایپ اسکریپت، تعیین نوع اردکی بررسی میکند که آیا شی یا مقدار، دارای ویژگیها و متدهای لازم در طول زمان کامپایل است یا خیر. این کار بدون تکیهبر سلسلهمراتب کلاسها یا رابطهای از پیش تعریف شده، انجام میگیرد.
بیایید اهمیت تعیین نوع اردکی را به وسیله Interface در تایپ اسکریپت با مثالی گویا بررسی کنیم:
1interface Animal {
2 name: string;
3 makeSound(): void;
4}
5
6class Dog implements Animal {
7 name: string;
8
9 constructor(name: string) {
10 this.name = name;
11 }
12
13 makeSound(): void {
14 console.log('Woof!');
15 }
16}
17
18class Cat implements Animal {
19 name: string;
20
21 constructor(name: string) {
22 this.name = name;
23 }
24
25 makeSound(): void {
26 console.log('Meow!');
27 }
28}
29
30function playWithAnimal(animal: Animal): void {
31 console.log(`Playing with ${animal.name}`);
32 animal.makeSound();
33}
34
35const dog = new Dog('Max');
36const cat = new Cat('Mittens');
37
38playWithAnimal(dog); // Output: Playing with Max
39playWithAnimal(cat); // Output: Playing with Mittens
در مثال بالا، اینترفیس یا همان رابط Animal الزامات ضروری نوعی حیوان را مشخص میکند و ویژگی nameو متد makeSoundرا برای آن مشخص خواهد کرد. کلاسهای Dogو Catاین رابط را پیادهسازی میکنند و مطمئن میشوند که دارای ویژگیها و متدهای لازم مطابق با توابعی هستند که آرگومان Animalرا میپذیرند. تابع playWithAnimalبا بررسی نکردن کلاس خاص آرگومانAnimal، تعیین نوع اردکی را مثال میزند و بررسی میکند که آیا شی دارای ویژگی nameو متد makeSoundاست. این رویکرد امکان تعریف اشیا را بدون مواجهه با خطا فراهم میکند.
![یک اردک در مقابل یک لپ تاپ بر روی یک میز](https://blog.faradars.org/wp-content/uploads/2024/01/Close-up-of-a-duck-working-with-a-laptop-on-an-empty-desk.jpg)
در حالی که تعیین نوع اردکی روشی قدرتمند محسوب میشود، مهم است که توجه داشته باشیم که جایگزین بررسی نوع صریح نمیشود. بررسی نوع صریح مزایای ارزشمندی از جمله تشخیص زودهنگام خطاهای نوع در طول کامپایل و بهبود خوانایی کد ارائه میدهد.
تفاوت اینترفیس و انواع در تایپ اسکریپت
در حالی که انواع و اینترفیس در تایپ اسکریپت هدف مشترک تعریف اشکال و ساختارها را دارند اما اهداف متفاوتی را نیز دنبال میکنند و هر کدام برای سناریوهای مختلف مناسب هستند. در اینجا مروری مختصر از تمایزات کلیدی بین رابطها و انواع در هنگام تعریف اشکال آورده شده است:
ویژگیها و کاربردهای Interface در تایپ اسکریپت شامل فهرست موارد زیر است:
- هدف اولیه: در درجه اول برای تعریف قرارداد برای اشیا استفاده میشوند.
- جزئیات پیادهسازی: رابطها صرفاً بر تعریف ساختار بدون ترکیب جزئیات پیادهسازی تمرکز میکنند.
- انعطافپذیری: آنها در نمایش ساختارهای مختلف تطبیقپذیری کمتری دارند.
- اجرای قرارداد: قراردادها را برای ویژگیهای شی سختگیرانه اجرا میکند.
ویژگیها و کاربردهای نوع در تایپ اسکریپت شامل فهرست موارد زیر است:
- هدف اولیه: برای نمایش ساختارهای مختلف، از جمله اشکال، استفاده میشود اما به قراردادهای شی محدود نمیشود.
- جزئیات پیادهسازی: میتوانند شامل جزئیات پیادهسازی همراه با تعریف شکل باشند.
- انعطافپذیری: از آنچه میتوانند نشان دهند، انعطافپذیرتر هستند.
- اجرای قرارداد: قرارداد سختگیرانهای را اجرا نمیکنند اما میتوانند اطلاعات نوع را ارائه دهند.
اینترفیس در تایپ اسکریپت برای توصیف اشکال اشیا، اجرای قراردادها و اطمینان از ثبات در ساختارهای کلاس مناسب هستند. از سوی دیگر، انواع در مدیریت تعیین نوع پیچیده، مدیریت دادههای غیر شی و تعریف نوع برای کتابخانههای خارجی برتری دارند. انتخاب بین اینترفیس و انواع به نیازهای خاص پروژه و ماهیت دادهها یا ساختارهایی که با آنها سروکار داریم بستگی دارد. استفاده از هر دو در پایگاه کد تایپ اسکریپت به کاربر این امکان را میدهد که از نقاط قوت آنها در بخشهای مختلف برنامه خود استفاده کند.
توابع Interface در تایپ اسکریپت
توابع اینترفیس در تایپ اسکریپت که بهعنوان انواع تابع نیز شناخته میشوند، راهی برای تعریف ساختار توابع در تایپ اسکریپت ارائه میدهند. توابع اینترفیس به ویژه زمانی مفید هستند که کاربر بخواهد به طور صریح انواع پارامترهایی را که تابع باید بپذیرد و نوع آن را باید بازگرداند، مشخص کند. در اینجا مثالی از نحوه تعریف و استفاده از نوعی تابع اینترفیس در تایپ اسکریپت آورده شده است:
1interface Calculator {
2 (a: number, b: number): number;
3}
4
5const add: Calculator = (a, b) => a + b;
6const subtract: Calculator = (a, b) => a - b;
7
8console.log(add(5, 3)); // 8
9console.log(subtract(10, 4)); // 6
در مثال بالا، رابط ساختار تابعی را مشخص میکند که دو پارامتر از نوع عدد را میگیرد و عددی را بازمیگرداند. این رابط به عنوان طرحی برای توابعی مانند addو subtractعمل میکند و از مطابقت آنها با امضای مشخصشده اطمینان میدهد. رابطهای تابع نقش مهمی در اجرای اشکال تابع سازگار، بهبود وضوح کد و تسهیل تشخیص زودهنگام خطاهای مربوط به تعیین نوع در مرحله توسعه ایفا میکنند.
نحوه تعریف ویژگی های اختیاری در رابط ها
در رابطه با مفهوم Interface در تایپ اسکریپت، اغلب از موجودیتها میخواهیم که به ویژگیهای خاصی پایبند باشند. با این حال، موقعیتهایی وجود دارد که همه ویژگیها اجباری نیستند و اینجاست که ویژگیهای اختیاری وارد عمل میشوند. برای نشان دادن نوعی ویژگی اختیاری در رابط یا اینترفیس، از علامت یا نماد ?استفاده میشود. برای درک بهتر این موضوع مثالی از رابط TeslaModelSرا در نظر بگیرید:
1interface TeslaModelS {
2 length: number;
3 width: number;
4 wheelbase: number;
5 seatingCapacity: number;
6 getTyrePressure?: () => number;
7 getRemCharging: () => number;
8}
در مثال فوق ?بعد از getTyrePressureنشان میدهد که این ویژگی اختیاری است. کاربرانی که این رابط را پیادهسازی میکنند موظف به ارائه این قابلیت در همه موارد نیستند و کامپایلر تایپ اسکریپت در صورت حذف آن خطایی ایجاد نمیکند. علاوه بر این، کامپایلر ویژگیهای اضافی را که در رابط تعریف نشده است، بررسی میکند. برای مثال، اگر پارامتر teslaObjحاوی نوعی ویژگی اضافی مانند turningCircleباشد که در رابط TeslaModelSمشخص نشده است، کامپایلر نوعی خطا ایجاد میکند، مانند مثال زیر:
1buildTeslaModelS({
2 length: 196,
3 width: 86,
4 wheelbase: 116,
5 getTyrePressure: function () {
6 let tyrePressure = 20 // Evaluated after doing a few complex computations!
7 return tyrePressure
8 },
9 getRemCharging: function () {
10 let remCharging = 20 // Evaluated after doing a few complex computations!
11 return remCharging
12 },
13 turningCircle: 10
14})
خطای کامپایلر به صورت زیر است:
Argument of type { length: number; width: number; wheelbase: number; getTyrePressure: () => number; getRemCharging: () => number; turningCircle: number; } is not assignable to parameter of type TeslaModelS. Object literal may only specify known properties, and turningCircle does not exist in type TeslaModelS.
خطای کامپایلر فوق تأکید میکند که ویژگی turningCircleبخشی از نوع TeslaModelSنیست و باعث میشود که این ویژگی به رابط مشخصشده پایبند باشد.
ویژگی های فقط خواندنی در رابط ها
در رابطها، ویژگیهای فقط خواندنی آنهایی هستند که پس از مقداردهی اولیه، بدون تغییر باقی میمانند. به عنوان مثال، ویژگیهایی مانند طول، عرض، فاصله بین دو محور و ظرفیت صندلی از ساخت ماشین را در نظر بگیرید که باید مقداری ثابت را در طول چرخه عمر خود حفظ کنند. برای اعمال این تغییرناپذیری، باید رابط را بر این اساس ویژگی فقط خواندنی مانند مثال زیر تنظیم کرد:
1interface TeslaModelS {
2 readonly length: number;
3 readonly width: number;
4 readonly wheelbase: number;
5 readonly seatingCapacity: number;
6 getTyrePressure?: () => number;
7 getRemCharging: () => number;
8}
در مثال بالا، کلمه کلیدی readonlyبه ویژگیها اعمال میشود که نشان میدهد این مقادیر پس از اختصاص مقدار اولیه قابل تغییر نیستند. این کار یکپارچگی این ویژگیها را در طول استفاده از رابط تضمین میکند.
![یک برنامه نویس جاوا اسکریپت](https://blog.faradars.org/wp-content/uploads/2023/12/js-interface4.jpeg)
ویژگی های قابل ایندکس در اینترفیس
ویژگیهای قابل نمایهسازی یا قابل ایندکس در اینترفیس به نوعی ویژگی اطلاق میشود که با استفاده از اعداد یا رشتههای منحصربهفرد امکان نمایهسازی در آنها را فراهم میکنند. به عنوان مثال، تعریف نوع به نام CustomArray زیر مدنظر است:
1interface CustomArray { [index: number]: string } let cars: CustomArray = ['Hatchback', 'Sedan', 'Land Rover', 'Tesla Model S'] console.log('Element at position 1', cars[1]) // 'Sedan'
در مثال بالا، نوع CustomArrayامکان نمایهسازی یا همان فهرست بندی با اعداد را فراهم میکند و نوعی تخصصی ایجاد خواهد کرد که از ویژگیها و عملکردهای خاص پشتیبانی میکند و در مواقعی که عملیات سفارشی مورد نیاز است مفید واقع میشود. حال سناریویی عملیتر را بررسی میکنیم. فرض بر این است مجموعهای از خودروهای تسلا مدل Sموجود هستند. میتوان از ویژگیهای قابل ایندکس برای سازماندهی و بررسی کارآمد هر مدل استفاده کرد. رابطهای TeslaModelSMapو TeslaModelSReviewبه صورت زیر برای این هدف معرفی شدهاند:
1interface TeslaModelSMap {
2 engineer: string,
3 model: TeslaModelS,
4 readonly rating: number
5}
6interface TeslaModelSReview {
7 [id: number]: TeslaModelSMap
8}
9
10const TeslaModelSReviewQueue: TeslaModelSReview = [
11 {
12 engineer: 'John',
13 model: modelByJohn1, // modelByJohn1 is of type `TeslaModelS`
14 rating: 2
15 },
16 {
17 engineer: 'Ray',
18 model: modelByRay1, // modelByRay1 is of type `TeslaModelS`
19 rating: 3
20 },
21 {
22 engineer: 'John',
23 model: modelByJohn2, // modelByJohn2 is of type `TeslaModelS`
24 rating: 4
25 },
26 // ... other 97 models
27]
در مثال فوق، رابط TeslaModelSReviewاز نوعی اندیس عددی برای مرتبط کردن ویژگیهایی مانند engineer، modelو ratingبا هر مدل تسلا استفاده میکند. این قابلیت امکان سازماندهی کارآمد و بازیابی اطلاعات را در طول فرآیند بررسی فراهم میکند. برای بهبود فرآیند بررسی، اندیسهای TeslaModelSReviewمیتوانند فقط خواندنی باشند تا از تغییرات ناخواسته جلوگیری شود. این کار با تنظیم رابط به صورت زیر حاصل میشود:
1interface TeslaModelSReview {
2 readonly [id: number]: TeslaModelS
3}
کد فوق تضمین میکند که تا زمانی که مدلها در حال بررسی هستند، اندیسها تغییرناپذیر باقی میمانند.
نحوه تعریف انواع تابع در اینترفیس
Interface در تایپ اسکریپت همچنین میتواند برای تعریف ساختار توابع استفاده شود. قبلاً، مشاهده کردیم که توابعی مانند getTyrePressureو getRemChargingبه عنوان ویژگیهایی در رابط TeslaModelSتعریف شدهاند. با این حال، رویکردی جایگزین برای این کار ایجاد نوعی رابط متمایز به طور خاص برای توابع است.
مثال زیر این مفهوم را بیان میکند:
1interface Order {
2 (customerId: number, modelId: number): boolean
3}
4
5let orderFn: Order = function (cId, mId) {
6 // processing the order
7 return true // processed successfully!
8}
در مثال بالا، رابط Orderساختار نوعی تابع را مشخص میکند و تعیین خواهد کرد که تابع باید دو پارامتر از نوع عدد (customerId و modelId)را بپذیرد و مقدار بولی را بازگرداند.
قابل ذکر است، هنگام تعریف تابع orderFn، دیگر نیازی به اعلام صریح انواع پارامترها نیست. کامپایلر تایپ اسکریپت به طور یکپارچه آرگومانهای موجود در تعریف تابع را با آرگومانهای مشخصشده در رابط مرتبط میکند. این کار شامل نگاشت cIdبه customerIdو تعیین انواع مربوطه آنها است که هر دو به عنوان نوع عددی استنتاج میشوند. به طور مشابه، نوع بازگشتی تابع orderFnبه طور خودکار از تعریف رابط استنتاج میشود. این رویکرد ساده، خوانایی و قابلیت نگهداری کد را افزایش میدهد.
![یک دختر برنامه نویس در حال کار با زبان تایپ اسکریپت](https://blog.faradars.org/wp-content/uploads/2023/12/js-interface7.jpeg)
نحوه استفاده از اینترفیس با کلاس ها
در این بخش از آموزش Interface در تایپ اسکریپت از مجله فرادرس به بررسی چگونگی استفاده از رابطها با کلاسهای جاوا اسکریپت خواهیم پرداخت. برای نمونه در زیر کلاسی ایجاد شده است که رابط TeslaModelSرا پیادهسازی میکند:
1class TeslaModelSPrototype implements TeslaModelS {
2 length: number;
3 width: number;
4 wheelbase: number;
5 seatingCapacity: number;
6 private tempCache: string;
7
8 constructor(l: number, w: number, wb: number, sc: number) {
9 this.length = l;
10 this.width = w;
11 this.wheelbase = wb;
12 this.seatingCapacity = sc;
13 }
14
15 getTyrePressure() {
16 let tyrePressure = 20 // Evaluated after doing a few complex computations!
17 return tyrePressure
18 }
19
20 getRemCharging() {
21 let remCharging = 20 // Evaluated after doing a few complex computations!
22 return remCharging
23 }
24}
در مثال بالا، کلاس TeslaModelSPrototypeبه صراحت ویژگیهای تعریف شده در رابط TeslaModelSرا پیادهسازی میکند. این شامل اعضای خصوصی مانند tempCacheاست که بخشی از اینترفیس نیستند و نشان میدهد که رابطها فقط ویژگیهای عمومی کلاس را مشخص میکنند. همچنین برای نمونهسازی شی از این کلاس و استفاده از قابلیتهای آن مثال زیر راداریم:
1let teslaObj = new TeslaModelSPrototype(196, 86, 116, 4);
2console.log('Tyre Pressure', teslaObj.getTyrePressure());
مثال فوق نشان میدهد که چگونه اینترفیسها بهعنوان نقشهای برای ساختارهای کلاس عمل میکنند و اطمینان میدهند که کلاس به واسط مشخصشده پایبند است. ویژگی خصوصی tempCacheبر پایبندی رابط تأثیری نمیگذارد زیرا رابطها فقط بر ویژگیهای عمومی تمرکز میکنند.
انواع مختلف متغیرها در کلاس
در کلاس، سه نوع متمایز از متغیرها وجود دارد که به صورت موارد زیر هستند:
متغیرهای محلی:
- متغیرهای محلی در داخل تابع یا بلوک تعریف میشوند و فقط در طول اجرای آن تابع یا بلوک وجود دارند.
- هر بار که تابع اجرا میشود، نمونههای جدیدی از متغیرهای محلی در حافظه ایجاد میشود.
متغیرهای نمونه:
- متغیرهای نمونه اعضای کلاس هستند و برای ذخیره ویژگیهای اشیای کلاس خدمت میکنند.
- هر شی ایجاد شده از کلاس دارای کپی منحصربهفرد خود از متغیرهای نمونه است.
متغیرهای استاتیک:
- همچنین به عنوان متغیرهای کلاس شناخته میشوند. متغیرهای استاتیک با کل کلاس مرتبط هستند.
- تمام اشیا نمونهسازی شده از کلاس نسخهای از متغیرهای استاتیک را به اشتراک میگذارند.
توجه به این نکته مهم است که هنگام برخورد با اینترفیس در تایپ اسکریپت، تمرکز منحصراً بر روی بخش نمونه کلاس است. به عنوان مثال، تابع سازنده تحت بخش استاتیک قرار میگیرد و در مثال ساخت ماشین تسلا، رابطTeslaModelS مشخصات سازنده یا هیچ جنبه ایستا کلاس را ارائه نمیدهد.
گسترش اینترفیس در تایپ اسکریپت
رابطها در تایپ اسکریپت میتوانند رابطهای دیگر را گسترش دهند و امکان ترکیب ویژگیهای رابط توسعه یافته را فراهم کنند. این رویکرد باعث ایجاد اجزای کوچک و قابلاستفاده مجدد میشود. مثال زیر را در نظر بگیرید، جایی که رابطهای متمایز اجزای مختلف مدل تسلا را مدیریت میکنند:
1interface Wheel {
2 wheelBase: number;
3 controls: Array<string>,
4 material: string;
5}
6
7interface Charger {
8 adapter: string;
9 connector: string;
10 location: string;
11}
12
13interface TeslaModelS extends Wheel, Charger {
14 // ... All other properties
15}
در مثال فوق، رابط TeslaModelSهر دو رابط Wheelو Chargerرا گسترش میدهد و ویژگیهای مربوطه را به ارث میبرد. این رویکرد ماژولار تضمین میکند که اجزای مختلف از به وسیله رابطهای جداگانه مدیریت میشوند و سازماندهی کد و قابلیت نگهداری را افزایش میدهد.
نام مستعار نوع چه تفاوتی با رابط ها دارد؟
نام مستعار نوع در تایپ اسکریپت راهی برای اختصاص نامی به ترکیبی از انواع مختلف فراهم میکند. به عنوان مثال، نوع مستعار میتواند برای نشان دادن نوعی داده ایجاد شود که این داده میتواند رشته یا تهی باشد. مانند مثال زیر:
1type StringOrNull = string | null;
نام مستعار نوع و رابط اغلب بهجای یکدیگر استفاده میشوند. برای مثال، ساختار شی TeslaModelSرا میتوان با استفاده از نوع مستعار به صورت زیر تعریف کرد:
1type TeslaModelS {
2 length: number;
3 width: number;
4 wheelbase: number;
5 seatingCapacity: number;
6 getTyrePressure: () => number;
7 getRemCharging: () => number;
8}
مشابه رابطها، نام مستعار نوع میتواند انواع و رابطهای دیگر را با استفاده از عملگر intersectionگسترش دهد. آنها همچنین میتوانند به وسیله نوعی کلاس نیز پیادهسازی شوند. نام مستعار نوع معمولاً هنگام ترکیب انواع مختلف استفاده میشود، همانطور که در مثال تابع renderObjectنشان داده شده است:
1function renderObject (objShape: Square | Rectangle | Triangle) {\
2 // ...
3}
در مثال بالا، «Square»، «Rectangle» و «Triangle» انواع هستند و عملگر «|» به objShapeاجازه میدهد از نوع «Square»، «Rectangle» و «Triangle» باشد. این اتحاد اشکال را نمیتوان با استفاده از اینترفیس بیان کرد.
از سوی دیگر، Interface در تایپ اسکریپت برای تعریف قرارداد در مورد شکل شی مورد استفاده قرار میگیرند. در نتیجه، آنها برای نشان دادن مجموعهای از اشکال چندگانه مناسب نیستند. علاوه بر این، کلاس نمیتواند نوعی را پیادهسازی کند که ترکیبی از اشکال را توصیف میکند و این ویژگی تفاوت عملکردی کلیدی بین رابطها و نامهای مستعار نوع را برجسته میکند. علاوه بر این، اگر دو اینترفیس نامی واحد داشته باشند، در رابط یا اینترفیسی واحد با ویژگیهای هر دو ادغام میشوند. با این حال، تلاش برای تعریف چندین نوع با نام یکسان منجر به خطای کامپایلر میشود.
انواع ترکیبی در رابط ها
در جاوا اسکریپت، توابع به عنوان اشیا در نظر گرفته میشوند و افزودن ویژگیها به توابع مجاز است. این قابلیت معمولاً در الگوهایی مشاهده میشود که تابع به عنوان شی با ویژگیهای اضافی عمل میکند. در تایپ اسکریپت، گرفتن این الگو با استفاده از رابطها شامل ایجاد انواع ترکیبی است که مثال زیر این مفهوم را بیان میکند:
1function manufactureCar (type) {
2 const model = function getModel (type) {
3 console.log('inside getModel function')
4 // get the model of type as mentioned in the argument
5 }
6 model.getCustomerDetails = function () {
7 console.log('inside customer details function')
8 // get the details of the customer who has purchased this model
9 }
10 model.price = 100000
11 model.trackDelivery = function () {
12 console.log('inside trackDelivery function')
13 // track the delivery of the model
14 }
15 return model
16}
17
18let tesla = manufactureCar('tesla')
19tesla() // tesla is a function
20tesla.getCustomerDetails() // getCustomerDetails is a property defined on function
برای بیان این الگو با استفاده از اینترفیس در تایپ اسکریپت، نوع داده ترکیبی به نام CarDeliveryایجاد میشود که قطعه کد آن به صورت زیر است:
1interface CarDelivery {
2 (string): TeslaModelS,
3 getCustomerDetails (): string,
4 price: number,
5 trackDelivery (): string
6}
7
8function manufactureCar (type: string): CarDelivery {
9 const model = <CarDelivery> function (type: string) {
10 // get the model of type as mentioned in the argument
11 }
12 model.getCustomerDetails = function () {
13 // get the details of the customer who has purchased this model
14 return 'customer details'
15 }
16 model.price = 100000
17 model.trackDelivery = function () {
18 // track the delivery of the model
19 return 'tracking address'
20 }
21 return model
22}
23let tesla = manufactureCar('tesla')
24tesla() // tesla is a function
25tesla.getCustomerDetails() // getCustomerDetails is a property defined on function
تشریح مثال فوق به صورت زیر است:
- رابط CarDelivery: رابط CarDeliveryبرای توصیف ساختار مورد انتظار شی تعریف شده است. این رابط به عنوان نوعی نقشه عمل کرده و ویژگیها و انواع آنها را که یک شی باید داشته باشد را مشخص میکند.
- شی بازگردانی شده به وسیله تابعmanufactureCar: انتظار میرود که تابع manufactureCarشی را بازگردانی کند که به ساختار مشخص شده به وسیله رابط CarDeliveryمتکی است. این شی برای نشان دادن مدلی از یک ماشین در نظر گرفته شده است.
- خواص مشخص شده: رابط CarDeliveryسه ویژگی را مشخص میکند:getCustomerDetails، price و trackDelivery. این ویژگیها رفتار یا ویژگیهای مورد انتظار شی برگشتی را تعریف میکنند.
- نوع هیبریدی یا ترکیبی: شی حاصل از manufactureCarبه عنوان یک نوع ترکیبی توصیف میشود. این بدان معنی است که شی دارای ویژگیهای تابع و مجموعهای از ویژگیها است.
- ویژگیهای قابل فراخوانی: اصطلاح callableبه توانایی شی برای فراخوانی به عنوان تابع، اشاره دارد.
به طور خلاصه، رابط CarDeliveryتضمین میکند که شیای که به وسیله manufactureCarبازگردانی میشود، از ساختار خاصی پیروی کرده و جنبههای فراخوانی و ویژگیها را در نوعی داده ترکیبی، ادغام میکند.
![شخصی در حال برنامه نویسی با زبان جاوا اسکریپت و تایپ اسکریپت](https://blog.faradars.org/wp-content/uploads/2023/12/js-interface9.jpeg)
نحوه استفاده از ژنریک در رابط های تایپ اسکریپت
«Generics» در تایپ اسکریپت راهی برای ایجاد مؤلفههای همهکاره ارائه میدهد که میتوانند بر روی انواع دادههای مختلف کار کنند. به جای محدود کردن تابعی برای پذیرش نوع خاصی از داده، ژنریک به آن اجازه میدهد تا در صورت نیاز با انواع مختلف سازگار شود. بیایید مثالی را با استفاده از اجرای پشته در نظر بگیریم:
1interface StackSpec<T> {
2 (elements: Array<T>): void
3}
4
5function Stack<T> (elements) {
6 this.elements = elements
7 this.head = elements.length - 1
8
9 this.push = function (number): void {
10 this.elements[this.head] = number
11 this.head++
12 }
13
14 this.pop = function <T>(): T {
15 this.elements.splice(-1, 1)
16 this.head--
17 return this.elements[this.head]
18 }
19
20 this.getElements = function (): Array<T> {
21 return this.elements
22 }
23}
24
25let stacksOfStr: StackSpec<string> = Stack
26let cars = new stacksOfStr(['Hatchback', 'Sedan', 'Land Rover'])
27cars.push('Tesla Model S')
28
29console.log('Cars', cars.getElements()) // ['Hatchback', 'Sedan', 'Land Rover', 'Tesla Model S']
در مثال بالا، رابط StackSpecبرای مدیریت هر نوع دادهای طراحی شده است که با نوع عمومی «T» مشخص میشود. سپس تابع Stackبا قابلیتهای عمومی پیادهسازی شده و به آن اجازه میدهد با آرایهای از عناصر از هر نوع کار کند. متدهای پشته، مانند Pushبرای افزودن عنصر و Popبرای حذف عنصر بالا، برای نوع عمومی «T» طراحی شدهاند. این انعطافپذیری اجازه میدهد تا از همان پیادهسازی پشته برای انواع دادههای مختلف استفاده مجدد شود که نشاندهنده قدرت ژنریک در ایجاد اجزای سازگار و قابلاستفاده مجدد است.
همچنین در مثال بالا ما با موفقیت پشتهای از رشتهها به نام stacksOfStrایجاد کردیم که برای مدیریت رشتهها با جایگزین کردن نوع عمومی «T» با «رشته» پیکربندی شده است. این قابلیت به ما اجازه میدهد تا از همان پیادهسازی پشته برای انواع دادههای مختلف، مانند اعداد یا هر نوع دیگری، دوباره استفاده کنیم. برای نشان دادن بهتر این موضوع در مثال پایین نوعی پشته که به طور خاص برای مدلهای ماشین تسلای مثالهای بالا طراحی شده است را در نظر بگیریم که قطعه کد آن به صورت زیر است:
1let stacksOfTesla: StackSpec<TeslaModelS> = Stack
2let teslaModels = [
3 {
4 engineer: 'John',
5 modelId: 1,
6 length: 112,
7 //...
8 },
9 // ...
10]
11let teslaStack = new stacksOfTesla(teslaModels)
12console.log(teslaStack) // prints the value of `teslaModels`
در مثال بالا، پشتهای به نام teslaStackبا استفاده از Stackبا نوع عمومی که روی TeslaModelSتنظیم شده، نمونهسازی شده است. این پشته به کاربر اجازه میدهد تا آرایهای از اشیا مدل تسلا را مدیریت کند. توجه به این نکته مهم است که همان پیادهسازی پشته به طور یکپارچه برای مدیریت آرایه از نوع خاص اعمال میشود و اثربخشی ترکیب ژنریکها با رابطها در تایپ اسکریپت را نشان میدهد. این ترکیب قدرتمند امکان ایجاد اجزای انعطافپذیر و قابلاستفاده مجدد را فراهم میکند که میتوانند با انواع دادهها سازگار شوند.
نحوه کامپایل کردن اینترفیس در تایپ اسکریپت چگونه است؟
تایپ اسکریپت، اگرچه در مدیریت پیچیدگیهای جاوا اسکریپت بسیار خوب عمل میکند اما در اجرای مرورگر با نوعی چالش مواجه است زیرا مرورگرها ذاتاً جاوا اسکریپت را درک میکنند نه تایپ اسکریپت. بنابراین، کد تایپ اسکریپت باید در جاوا اسکریپت کامپایل شود.
کامپایلر تایپ اسکریپت کلاس TeslaModelSPrototypeارائه شده را به کد جاوا اسکریپت زیر تبدیل میکند که مثال زیر برای بیان این هدف است:
1var TeslaModelSPrototype = /** @class */ (function () {
2 function TeslaModelSPrototype(l, w, wb, sc) {
3 this.length = l;
4 this.width = w;
5 this.wheelbase = wb;
6 this.seatingCapacity = sc;
7 }
8 TeslaModelSPrototype.prototype.getTyrePressure = function () {
9 var tyrePressure = 20; // Evaluated after doing a few complex computations!
10 return tyrePressure;
11 };
12 TeslaModelSPrototype.prototype.getRemCharging = function () {
13 var remCharging = 20; // Evaluated after doing a few complex computations!
14 return remCharging;
15 };
16 return TeslaModelSPrototype;
17}());
18var teslaObj = new TeslaModelSPrototype(196, 86, 116, 4);
19console.log('Tyre Pressure', teslaObj.getTyrePressure());
در مثال بالا، متغیرهای نمونه مانند «length»، «width»، «wheelbase» و «seatingCapacity» در تابع «TeslaModelSPrototype» مقداردهی اولیه میشوند. در همین حال، متدهای getTyrePressureو getRemChargingبر روی نمونه اولیه تابع TeslaModelSPrototypeتعریف شدهاند. این کد بهدست آمده جاوا اسکریپت کدی استاندارد است و میتواند به طور یکپارچه در محیط مرورگر اجرا شود.
دلایل استفاده از اینترفیس در تایپ اسکریپت چیست؟
اینترفیسها به کارایی موتورهای جاوا اسکریپت کمک میکنند. برای بررسی این موضوع، بیایید کارکردهای داخلی موتورهای جاوا اسکریپت را با تمرکز بر «V8»، موتور مورد استفاده کروم، بررسی کنیم. وقتی صحبت از ذخیره اشیا میشود، Interface در تایپ اسکریپت فقط در زمان کامپایل نقش دارند. بررسی کد تولید شده به وسیله کامپایلر تایپ اسکریپت عدم وجود رابط را آشکار میکند. در عوض، ویژگیهای رابط TeslaModelSجای خود را در سازنده TeslaModelSPrototypeپیدا میکنند. به طور همزمان، انواع تابع به نمونه اولیه تابع TeslaModelSPrototypeاضافه میشود. نکته مهم این است که موتورهای جاوا اسکریپت از رابطها غافل هستند.
برای درک بهتر این موضوع سناریوی ایجاد خودروهای متعدد TeslaModelSPrototypeمد نظر است. با وجود اینکه هر شی از نوع TeslaModelSاست، موتور جاوا اسکریپت چندین کپی از ساختار رابط برای هر شی تولید نمیکند. در عوض، برای اشیایی از نوع TeslaModelSنوعی شکل منفرد ایجاد میکند و هر شی به سادگی مقادیر ویژگیهای مربوطه را ذخیره کرده و با رابط TeslaModelSهمسو میشود.
این رویکرد دارای مزیت عملکرد قابلتوجهی برای موتورهای جاوا اسکریپت است. این موتور به جای کپی کردن اشکال مشابه برای هزاران شی، استفاده از حافظه را با حفظ نوعی شکل یکپارچه برای اشیای متصل به رابط TeslaModelS بهینه میکند. در مواردی که اشیاء دارای اشکال متمایز هستند، موتور مجبور است برای هر شی اشکال متفاوتی ایجاد و آن را مدیریت کند. اینترفیس در جاوا اسکریپت نقشی اساسی در حفظ یکنواختی اشکال برای اشیا با ویژگیهای مشترک دارد و به بهبود عملکرد کمک میکند.
اینترفیس های قابل فراخوانی در تایپ اسکریپت
اینترفیسهای قابل فراخوانی مکانیزمی را در تایپ اسکریپت فراهم میکنند تا از استفاده صحیح از توابع با ثبت مسائل احتمالی مربوط به آرگومانهای نادرست یا مقادیر برگشتی اطمینان حاصل کند. مثال زیر در این رابطه مهم است:
1const generateANumber: Function = (factor: number, genFunc: Function) => {
2return genFunc(factor)
3}
4console.log(generateANumber(5, (a:number) => a)) // 5
5console.log(generateANumber(5, () => "Cheese")) //Cheese
در کد بالا، نوعی مشکل احتمالی وجود دارد. ما قصد داریم که genFuncتابعی باشد که عددی را میگیرد و عددی را بازمیگرداند اما تنظیمات فعلی این قانون را اجرا نمیکند. برای مثال، ارسال تابعی که هیچ آرگومان نمیگیرد و رشتهای را بازمیگرداند، هیچ نوع خطای ایجاد نمیکند. برای پرداختن به این مشکل، میتوان از رابطهای قابل فراخوانی برای تعریف تابع مورد انتظار استفاده کرد که مثال زیر این موضوع را بیان میکند:
1// Callable Interface
2interface NumberGenerator {
3 (num: number): number
4}
5interface GenANum {
6 (num: number, gen: NumberGenerator): number
7}
8const generateANumber: GenANum = (factor: number, genFunc: NumberGenerator) => {
9 return genFunc(factor)
10}
11console.log(generateANumber(5, (a:number) => a)) // 5
12console.log(generateANumber(5, () => "Cheese")) // Type Error
اکنون با معرفی اینترفیسها («NumberGenerator» و «GenANum»)، به صراحت تعریف تابع «generateANumber» و آرگومان «genFunc» آن قابل انجام هستند. اگر تابع نادرست ارسال شود، مانند () => "Cheese"، خطای نوع را دریافت میکند و ایمنی نوع کلی کد را افزایش میدهد. اینترفیسهای قابل فراخوانی نقش مهمی در اطمینان از تعیین صحیح توابع دارند.
نحوه استفاده از رابط ها با React
بیایید مثالی ساده از استفاده از رابطهای React و تایپ اسکریپت برای نمایش فهرستی از Pokemonایجاد کنیم. مؤلفه اصلی App، یک PokemonList را در کانتینر divکه به وسیله شناسه ریشه شناسایی شده است، ارائه میکند:
1import React, { Fragment } from 'react';
2import { render } from 'react-dom';
3import PokemonList from './pokemon-list';
4import './style.css';
5
6const App = () => {
7 return (
8 <Fragment>
9 <h2>Pokemon List</h2>
10 <PokemonList />
11 </Fragment>
12 )
13}
14
15render(<App />, document.getElementById('root'));
در تنظیمات بالا، مؤلفه Appمنطق رندر PokemonListرا کپسوله میکند. اجرای مؤلفه PokemonListبه صورت زیر است:
1import React, { Component } from 'react';
2import { PokemonListModel } from './pokemon-model';
3
4interface PokemonProps {}
5interface PokemonState {
6 pokemonList: PokemonListModel | null;
7}
8
9class PokemonList extends Component<PokemonProps, PokemonState> {
10
11 constructor (props) {
12 super(props);
13 this.state = {
14 pokemonList: null
15 }
16 }
17
18 getPokemonList = () => {
19 fetch ('https://pokeapi.co/api/v2/pokemon/?limit=50')
20 .then (response => {
21 return response.json();
22 })
23 .then (response => {
24 this.setState({ pokemonList: response });
25 })
26 }
27
28 render () {
29 let { pokemonList } = this.state;
30 return (
31 <div className='pokemon-list'>
32 {
33 pokemonList && pokemonList.results.map (pokemon => {
34 return (
35 <div className='row' key={pokemon.name}>
36 <span>{pokemon.name}</span>
37 </div>
38 )
39 })
40 }
41 </div>
42 )
43 }
44
45 componentDidMount () {
46 this.getPokemonList()
47 }
48}
49
50export default PokemonList
کامپوننت PokemonListلیستی از Pokemonرا از API منبع باز Pokemonبازیابی کرده و نتایج API را در وضعیت کامپوننت ذخیره میکند. این کامپوننت از رابطهای PokemonPropsو PokemonState برای تعریف ویژگیها و وضعیت خود استفاده میکند. همچنین رابط PokemonListModelساختار شی را که از API Pokemonبازگردانی شده است را ترسیم خواهد کرد. حال نوبت به بررسی رابط PokemonListModelبوده که قطعه کد آن به صورت زیر است:
1export interface PokemonListModel {
2 count: number;
3 next: string | null;
4 previous: string | null;
5 results: Array
6}
7
8interface Pokemon {
9 name: string;
10 url: string;
11}
در رابط فوق، ویژگی resultsاز نوع «Array«Pokemon»» است که در آن Pokemonرابط دیگری محسوب میشود که ساختار تک تک اشیا Pokemonرا تعریف میکند. نمایشی از برنامه Pokemonبه صورت تصویر زیر است:
![یک پروژه ری اکت جی اس ساده که از تایپ اسکریپت در آن استفاده شده است](https://blog.faradars.org/wp-content/uploads/2023/12/interface-.png)
استفاده از رابط ها در Angular
در این بخش از آموزش Interface در تایپ اسکریپت به بررسی قابلیت استفاده از اینترفیسها با «انگولار» (Angular) خواهیم پرداخت. در Angular که بر اساس تایپ اسکریپت ساخته شده است، اینترفیسها نقش مهمی دارند. بررسی پایگاه کد زیر برای درک نحوه رفتار این اینترفیسها در انگولار خالی از لطف نیست.
1export interface Post {
2 title: string
3 content: string
4}
در مثال بالا ابتدا محتوای فایل /src/app/types.tsمد نظر است. این فایل به عنوان نوعی مکان مرکزی برای تعریف اینترفیسها و انواعی که در سراسر برنامه مورد استفاده قرار میگیرد عمل میکند: در این مثال، رابطی به نام Postوجود دارد که دو ویژگی title و contentرا مشخص میکند. هدف تسهیل بازیابی و افزودن پستها در برنامه است.
برای درک بهتر کار اینترفیسها در تایپ اسکریپت کپسولهسازی مدیریت داده در فایل /src/app/post.service.tsمورد بررسی واقع خواهد شد که کد زیر برای این هدف مد نظر است:
1import { Injectable } from '@angular/core';
2import { Post } from './types';
3
4@Injectable({
5 providedIn: 'root'
6})
7export class PostService {
8 private posts: [Post]; // Private property representing an array of posts
9
10 constructor() {
11 this.posts = [{ title: "Post 0", content: "content for post 0" }];
12 }
13
14 // Method for adding a post
15 addPosts(newPost: Post) {
16 this.posts.push(newPost);
17 }
18
19 // Method for getting all posts
20 getPosts() {
21 return this.posts;
22 }
23}
در کد فوق، از رابط Postبرای تعریف ویژگی خصوصی، که آرایهای از پستها است، استفاده خواهد شد. آرایه با پست اولیه مقداردهی اولیه میشود. این سرویس دو متد زیر را ارائه میدهد:
- addPosts: پستی را به عنوان آرگومان میپذیرد و آن را به آرایه "this.posts"اضافه میکند.
- getPosts: آرایه حاوی اشیا Postرا بازمیگرداند.
میتوان در برنامه Angular بالا از نوعی سرویس ویژه به نام PostService، برای مدیریت فهرستی از پستها استفاده میشود. کلاس AppComponentکه مسئول کامپوننت اصلی برنامه است، PostServiceرا برای دسترسی به تابعهایی که ارائه میدهد، تزریق میکند. قطعه کد انجام این کار به صورت زیر است:
1import { Component } from '@angular/core';
2import { PostService } from './post.service';
3import { Post } from './types';
4@Component({
5 selector: 'app-root',
6 templateUrl: './app.component.html',
7 styleUrls: ['./app.component.css'],
8})
9export class AppComponent {
10 title = 'sampleproject';
11 postService: PostService;
12 posts: [Post]
13 // initiate PostService in component
14 constructor(postService: PostService) {
15 this.postService = postService;
16 this.posts = this.postService.getPosts()
17 }
18 addPost() {
19 // Create new post
20 const newPost: Post = {
21 title: `Post ${this.posts.length}`,
22 content: 'this is the content for this post',
23 };
24 // update list of posts in service and component
25 this.postService.addPosts(newPost)
26 this.posts = this.postService.getPosts()
27 }
28}
در AppComponent، دو ویژگی کلیدی وجود دارد:
- postService: این ویژگی نمونهای از postServiceرا که قبلاً تعریف شده است، نشان میدهد و به عنوان پلی بین کامپوننت و سرویس مدیریت داده عمل میکند.
- posts: این ویژگی آرایهای از اشیا Postرا به صورت محلی در کامپوننت نگه میدارد. این ویژگی به عنوان یک کپی از فهرست پستهای بازیابی شده از سرویس عمل میکند.
سازنده کامپوننت از تزریق وابستگی برای مقداردهی اولیه نمونه postServiceاستفاده میکند. علاوه بر این، فهرست اولیه پستها را با استفاده از متد getPostsاز سرویس دریافت کرده و ویژگی پستهای محلی را پر میکند. برای تسهیل افزودن پستهای جدید، کامپوننت شامل متد addPostاست. این متد پست جدیدی با عنوان تولید شده به صورت پویا و محتوای از پیش تعریف شده ایجاد میکند. سپس پست به وسیله متد addPostسرویس به لیست اضافه شده و بر این اساس ویژگی پست محلی بهروز میشود.
همچنین میتوان متد addPostمحلی در کامپوننت معرفی شود که امکان افزودن پستهای جدید به وسیله برنامه را فراهم میکند و در عین حال همگامسازی را با سرویس و آرایه محلی تضمین خواهد کرد. برای ارائه نوعی رابط کاربری برای این تابع، ظاهر کامپوننت را در فایل src/app/app.component.htmlتعریف میکنیم.
1<h1> Posts </h1>
2<button (click)="addPost()">Add Post</button>
3<div *ngFor="let post of posts; index as i">
4 <h2>{{post.title}}</h2>
5 <p>{{post.content}}</p>
6</div>
قالب HTML شامل موارد زیر است:
- عنصر h1 که عنوان پستها را نمایش میدهد.
- buttonکه پس از کلیک کردن، متد addPostرا فعال کرده و افزودن پستی جدید را تسهیل میکند.
عنصر div که از دستور Angular *ngFor برای پیمایش روی آرایههای Postsاز کامپوننت استفاده میکند، برای هر پست، نوعی عنصر h2برای عنوان و نوعی عنصر pبرای محتوا ایجاد میکند.
در طول این فرآیند، رابط پست نقش مهمی ایفا میکند. استفاده از آن تضمین میکند که هرگونه خطا، مانند غلط املایی در زمان کامپایلر گرفته میشود و از خطاهای احتمالی زمان اجرا در برنامه جلوگیری میکند. این رویکرد استحکام و قابلیت اطمینان کلی پایگاه کد را افزایش میدهد.
نکاتی در رابطه با Interface در تایپ اسکریپت
Interface در تایپ اسکریپت به عنوان نوعی مکانیسم قوی برای ایجاد قرارداد در تایپ اسکریپت عمل میکنند. توجه به نکات زیر در رابطه با Interfaceها بسیار حائز اهمیت است:
- مشخصات موجودیتها: رابطها مشخصات موجودیتها را مشخص میکنند و اینها میتوانند به وسیله توابع و کلاسها پیادهسازی شوند. ویژگیهای اختیاری را میتوان با ?نشان داد، در حالی که ویژگیهای فقط خواندنی از کلمه کلیدی فقط خواندنی در نام ویژگی استفاده میکنند.
- بررسیهای کامپایلر: کامپایلر تایپ اسکریپت بررسیهایی را برای ویژگیهای اضافی روی اشیا انجام میدهد و در صورتی که شی حاوی خصوصیتی باشد که در رابط تعریف نشده باشد، خطا ایجاد میکند.
- ویژگیهای قابل ایندکس: چگونگی تعریف ویژگیهای قابل ایندکس را با استفاده از رابطها بررسی میکند.
- پیادهسازی کلاس: کلاسها توانایی پیادهسازی رابطها را دارند.
- گسترش رابط: رابطها را میتوان گسترش داد و امکان وارد کردن ویژگیها از سایر رابطها را با استفاده از کلمه کلیدی extendsفراهم میکند.
- استفاده از Generics: قدرت ژنریکها در تایپ اسکریپت را میتوان با رابطها مهار کرد و ایجاد اجزای قابلاستفاده مجدد را تسهیل کرد.
- قابلیت ذخیرهسازی بهینه: رابطها با کمک به ذخیرهسازی کارآمد اشیا با اشکال مشابه، به عملکرد موتورهای جاوا اسکریپت کمک میکنند.
این مفاهیم در مجموع تطبیقپذیری و کاربرد رابطها در تایپ اسکریپت را نشان داده و مجموعه ابزار جامعی را برای ایجاد کدهای قوی و قابل نگهداری ارائه میدهند.
سخن پایانی
Interface در تایپ اسکریپت به عنوان سنگ بنای تعریف قراردادهای واضح کدنویسی، بهبود ساختار کد و ارتقای بررسی نوع قوی عمل میکند. اینترفیس به توسعهدهندگان اجازه میدهد تا شکل مورد انتظار اشیا، توابع و کلاسها را مشخص کنند. همچنین این قابلیت در تایپ اسکریپت همکاری یکپارچه را در پایگاه کد تسهیل کرده و تشخیص خطاهای احتمالی را در طول توسعه به جای زمان اجرا امکان پذیر میسازد. یکی از مزایای قابل توجه رابطها تأثیر مثبت آنها بر عملکرد موتور جاوا اسکریپت است. با بهینهسازی ذخیرهسازی اشیا با اشکال مشابه، رابطها به کارایی حافظه و سرعت کلی زمان اجرا کمک میکنند.
در مطلب فوق از مجله فرادرس مفهوم اینترفیسها در جاوا اسکریپت مورد بررسی قرار گرفت و در سناریوهای عملی متفاوتی، استفاده از اینتفریس مورد بازبینی قرار گرفت. همچنن مباحثی مانند تعیین نوع اردکی، تفاوت اینترفیس و انواع در تایپ اسکریپت، انواع تابع در اینترفیس، گسترش اینترفیس در تایپ اسکریپت، اینترفیسهای قابل فراخوانی در تایپ اسکریپت، نحوه استفاده از رابطها با React و استفاده از رابطها در Angular مورد بررسی قرار گرفتند.