نظرات مطالب
آموزش TypeScript #2
سلام من زمانی که فایل پروژه رو ویرایش کردم به این مشکل برخوردم . امکانش هست بگید مشکل از کجاست :
Error 1 The command ""C:\Program Files (x86)\Microsoft SDKs\TypeScript\0.8.0.0\tsc" -target ES5 " exited with code 3. c:\users\IT\documents\visual studio 2012\Projects\MvcApplication6\MvcApplication6\MvcApplication6.csproj 259 5 MvcApplication6
 
مطالب
مبانی TypeScript؛ تنظیمات کامپایلر
برای کامپایل کدهای TypeScript به جاوا اسکریپت، علاوه بر پارامترهای کامپایلر، از تنظیمات فایل مخصوصی به نام tsconfig.json نیز استفاده می‌شود که این موارد را در قسمت جاری بررسی خواهیم کرد.


نحوه‌ی اعمال تنظیمات کامپایلر TypeScript

روش‌های متفاوتی جهت اعمال تنظیمات کامپایلر TypeScript وجود دارند:
الف) ذکر پارامترها و سوئیچ‌های کامپایلر خط فرمان tsc به صورت مستقیم.
ب) بعضی از ادیتور‌ها و IDEها این پارامترها را به صورت گزینه‌ها و دیالوگ‌هایی ارائه می‌دهند.
ج) استفاده از یک Build task، همانند روشی که در تنظیمات VSCode در مطلب «چرا TypeScript» مشاهده کردید.
د) ذکر تنظیمات کامپایلر، در فایل مخصوصی به نام tsconfig.json.


گزینه‌های متداول کامپایلر TypeScript

گزینه‌های کامپایلر TypeScript نسبتا قابل توجه هستند و لیست کامل و به روز آن‌ها را در هندبوک تایپ‌اسکریپت می‌توانید مشاهده کنید. در اینجا تعدادی از مهم‌ترین‌ها را بررسی خواهیم کرد:
- سوئیچ module-- جهت مشخص سازی فرمت خروجی ماژول‌های TypeScript بکار می‌رود. در مطلب بررسی ماژول‌ها عنوان شد که TypeScript قادر است ماژول‌های تعریف شده را با سایر فرمت‌های متداول جاوا اسکریپت مانند common.js و amd نیز تولید کند. سوئیچ module-- جهت تنظیم این گزینه درنظر گرفته شده‌است. خلاصه‌ای این سوئیچ نیز m-- است. این سوئیچ یکی از مقادیر commonjs, amd, system, es2015 را می‌پذیرد. اگر es2015 را مشخص کردید، نیاز است target را نیز به ES6 تنظیم کنید.
- سوئیچ moduleResolution-- نحوه‌ی یافتن ارجاعات به ماژول‌ها را مشخص می‌کند. در اینجا روش‌های node.js و کلاسیک را می‌توان قید کرد.
- سوئیچ target-- برای تعیین نگارش خروجی جاوا اسکریپت تولیدی بکار می‌رود. حالت پیش فرض آن ES3 است و ES5 و ES6 را نیز پشتیبانی می‌کند.
- گزینه‌ی watch-- کامپایلر را در حالت watch نگه می‌دارد. در این حالت تغییرات آخرین تاریخ نوشته شدن در فایل‌های ts بررسی شده و در صورت یافتن تغییری، بلافاصله خروجی js آن‌ها تهیه می‌شود.
- سوئیچ outDir-- برای مشخص کردن پوشه‌ی فایل‌های تولیدی نهایی بکار می‌رود.
- گزینه‌ی noImplicitAny-- برای ممنوع کردن نوع‌های Any متغیرها به صورت پیش فرض است و در این حالت خطای کامپایلری را مشاهده خواهید کرد. استفاده از این گزینه به این معنا نیست که دیگر نمی‌توان از نوع Any استفاده کرد؛ بلکه به این معنا است که در صورت نیاز باید آن‌را به صورت صریح قید کنید.

یک مثال:
در VSCode و در پوشه‌ی vscode. آن، در تنظیمات فایل tasks.json، چنین گزینه‌هایی را می‌توان برای کامپایلر tsc، ذکر کرد:
"args": ["--target", "ES5",
            "--outDir", "js",
            "--module", "commonjs",
            "--sourceMap",
            "--watch",
             "app.ts"],
به این ترتیب خروجی جاوا اسکریپت آن با فرمت ES 5 بوده و فایل‌های نهایی آن در پوشه‌ی js، در ریشه‌ی پروژه نوشته خواهند شد. همچنین فرمت ماژول‌های خروجی آن نیز به commonjs تنظیم شده‌است. این کامپایلر sourceMapها را جهت امکان دیباگ بهتر کدها تولید کرده و در حالت watch قرار دارد.


بررسی کاربرد فایل tsconfig.json

فایل ویژه‌ی tsconfig.json در نگارش 1.5 تایپ‌اسکریپت معرفی گردید. هدف از این فایل، ساده کردن تعریف پارامترهای کامپایلر است؛ البته الزامی به استفاده‌ی از آن وجود ندارد.
این فایل مزایای ذیل را به همراه دارد:
الف) محل قرارگیری آن، ریشه‌ی پروژه‌ی TypeScript را مشخص می‌کند.
ب) تنظیمات ذکر شده‌ی در این فایل، به تمام فایل‌های موجود در پوشه و زیر پوشه‌های محل قرارگیری آن به صورت پیش فرض اعمال می‌شوند.
هنگامیکه در تنظیمات کامپایلر tsc، نام فایل یا فایل‌های ts ایی را ذکر نمی‌کنید، این کامپایلر در ابتدا به دنبال فایل tsconfig.json می‌گردد و بر این اساس فایل‌های ts را پردازش خواهد کرد. اگر مانند مثال فوق، در انتهای پارامترها، نام فایلی را ذکر کنید، از فایل tsconfig.json صرفنظر خواهد شد.
یک نکته: برای ذکر صریح محل فایل tsconfig از پارامتر project استفاده کنید:
 tsc --project ./lib
ج) امکان ذکر گزینه‌های کامپایلر را فراهم می‌کند.
در این حالت می‌توان کامپایلر tsc را بدون پارامتری اجرا کرد و این برنامه اطلاعات مورد نیاز خود را از فایل tsconfig.json دریافت خواهد کرد. باید دقت داشت، هر سوئیچی که در خط فرمان ذکر شود، پارامترهای معادل ذکر شده‌ی در فایل tsconfig.json را بازنویسی می‌کند. بنابراین در صورت وجود این فایل، می‌توان خاصیت args مثال قبل را به یک آرایه‌ی خالی تنظیم کرد.
د) امکان مشخص سازی الحاق و عدم الحاق فایل‌های ts را به همراه دارد.
 {
     "compilerOptions": {
                      "target": "es5",
                      "outDir": "js",
                      "module":"commonjs",
                      "sourceMap":true
     },
     "files": [
             "app.ts",
             "classes.ts"
     ]
}
نمونه‌ای از محتوای این فایل JSON را در مثال فوق مشاهده می‌کنید. در خاصیت compilerOptions آن، امکان تعریف پارامترهای کامپایلر وجود دارند؛ مانند تعیین نوع جاوا اسکریپت خروجی و پوشه‌ی نهایی آن. خاصیت آرایه‌ی files آن، برای ذکر لیست فایل‌هایی است که باید به کامپایلر ارسال شوند.
کار خاصیت files الحاق و include است. اگر می‌خواهید از پوشه‌ها و یا فایل‌هایی صرفنظر شود، از خاصیت exclude استفاده کنید:
{
      "compilerOptions": {
                     "target": "es5",
                     "outDir": "js"
      },
      "exclude": [
               "node_modules",
               "lib"
      ]
}
باید دقت داشت که در اینجا تنها یکی از خواص files و یا exclude را می‌توان ذکر کرد. اگر هر دو را با هم ذکر کنید، تنها از خاصیت files استفاده می‌شود.

یک نکته
در VSCode داخل فایل tsconfig.json با فشردن ctrl+space، به یک intellisense حاوی گزینه‌های تکمیل کننده‌ی آن خواهید رسید.


ساده سازی الحاق فایل‌های تعاریف نوع‌ها

در مطلب «مبانی TypeScript؛ تهیه فایل‌های تعاریف نوع‌ها» با فایل‌های ویژه‌ی d.ts. آشنا شدیم. استفاده‌ی از این فایل‌ها به همراه ذکر اجباری reference path مرتبط در ابتدای هر فایل ts است. این‌کار اضافی را با استفاده از فایل tsconfig.json می‌توان حذف کرد:
{
         "compilerOptions": {
                  "target": "es5",
                  "outDir": "js",
                  "module": "commonjs",
                  "sourceMap": true,
                  "watch": true
         },
         "files": [
                "app.ts",
                "typings/main.d.ts"
         ]
}
در اینجا با ذکر typings/main.d.ts در قسمت files، اطلاعات موجود در این فایل d.ts. به صورت سراسری به تمام فایل‌های ts موجود در پروژه‌ی جاری اعمال می‌شود و دیگر نیازی به ذکر صریح reference path آن نیست.
مطالب
Blazor 5x - قسمت اول - معرفی
با استفاده از Blazor می‌توان برنامه‌های وب تعاملی را با کمک زبان #C تهیه کرد که پیشتر برای نوشتن آن‌ها به جاوا اسکریپت نیاز بود. به این ترتیب می‌توان برای تهیه‌ی قسمت‌های front-end و backend پروژه‌ی خود، از زبانی که به آن تسلط دارید استفاده کنید. یکی از مزایای آن امکان به اشتراک گذاری کدهای سمت سرور و کلاینت است؛ با توجه به اینکه هر دو به یک زبان تهیه می‌شوند.


وضعیت توسعه‌ی برنامه‌های وب، پیش از ارائه‌ی Blazor

عموما برای توسعه‌ی برنامه‌های وب، در سمت سرور آن‌ها از زبان‌هایی مانند C#، Java و Python و امثال آن‌ها استفاده می‌شود؛ اما این وضعیت در سمت کلاینت فرق می‌کند. در سمت کلاینت، عموما از فریم‌ورک‌ها و کتابخانه‌های جاوا اسکریپتی مانند Angular ،React ،Vue.js ،jQuery و غیره استفاده می‌شود.


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


با استفاده از Blazor می‌توان کدهای تعاملی UI را بجای استفاده از زبان جاوا اسکریپت، با کمک زبان #C تهیه کرد. به این ترتیب با استفاده از یک زبان می‌توان کدهای سمت سرور و سمت کلاینت را پیاده سازی کرد. البته شاید این سؤال مطرح شود که مرورگرها تنها قادر به درک کدهای HTML و جاوا اسکریپت هستند و نه #C، بنابراین چگونه می‌توان از زبان #C در مرورگرها نیز استفاده کرد؟ پاسخ به آن، به فناوری جدید «وب اسمبلی» بر می‌گردد. Blazor با استفاده از «وب اسمبلی» است که می‌تواند کدهای #C را درون مرورگر اجرا کند.


حالت‌های مختلف هاست و ارائه‌ی برنامه‌های مبتنی بر Blazor

برنامه‌های مبتنی بر Blazor، به دو روش مختلف قابل ارائه هستند:

الف) Blazor Server

Blazor Server، در اساس یک برنامه‌ی استاندارد ASP.NET Core است که در آن تمام قابلیت‌های سمت سرور، مانند کار با EF-Core، میسر است و امکان دسترسی به این امکانات به صورت یکپارچه‌ای در سراسر برنامه وجود دارد. در این حالت، کامپوننت‌های Blazor، بجای اجرای بر روی مرورگر کاربر، در سمت سرور اجرا می‌شوند. این تعاملات و به روز رسانی‌های UI، توسط یک اتصال دائم SignalR مدیریت می‌شوند.


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

ب) Blazor web assembly

در این حالت با استفاده از فناوری جدید «وب اسمبلی»، تمام کدهای یک برنامه‌ی مبتنی بر Blazor به کمک NET Runtime.، داخل مرورگر اجرا می‌شود. به Blazor web assembly باید همانند فریم‌ورک‌های SPA (تک صفحه‌ای وب)، مانند Angular و React نگاه کرد؛ با یک تفاوت مهم: در اینجا بجای استفاده از جاو اسکریپت برای نوشتن برنامه‌ی SPA، از #C استفاده می‌شود. اگر به تصویر فوق دقت کنید، در حالت اجرای برنامه‌های Blazor web assembly، تنها به مرورگر کاربر نیاز است و همه چیز داخل آن قرار می‌گیرد. در اینجا دیگر خبری از یک اتصال دائم SignalR با سرور وجود ندارد.
البته باید دقت داشت که از فناوری وب اسمبلی، در تمام مرورگرهای جدید پشتیبانی می‌شود؛ منهای IE 11. در این حالت مرورگر کل برنامه‌ی Blazor را دریافت می‌کند (همانند دریافت کل کدهای یک برنامه‌ی Angular و یا React) و بدون استفاده از رندر سمت سرور حالت الف، قابلیت تعامل با کاربر را دارد.
بدیهی است با توجه به اینکه Blazor web assembly مستقیما داخل مرورگر اجرا می‌شود، دیگر همانند حالت الف، امکان دسترسی مستقیم به فناوری‌ها و امکانات سمت سرور، مانند کار مستقیم با EF-Core را نخواهد داشت. برای این منظور دقیقا همانند روش کار با سایر فریم ورک‌های SPA، نیاز به تهیه‌ی یک ASP.NET Core Web API جهت تعامل با سرور خواهد بود.


مزایا و معایب حالت‌های مختلف هاست برنامه‌های Blazor

الف) Blazor Server
مزایا:
- حجم دریافتی توسط مرورگر در این حالت بسیار کم است.
- امکان دسترسی به تمام امکانات سمت سرور را دارد؛ مانند تمام کتابخانه‌های سمت سرور و همچنین امکان دیباگ آن نیز همانند سایر برنامه‌های سمت سرور است.
- بر روی مرورگرهای قدیمی نیز قابل اجرا است؛ چون بدون نیاز به فناوری جدید «وب اسمبلی» کار می‌کند.

معایب:
- رندر شدن UI آن نسبت به حالت ب، کندتر است. از این جهت که تمام تعاملات UI آن، توسط اتصال SignalR به سمت سرور ارسال شده و سپس نتیجه‌ی نهایی رندر شده، به سمت کلاینت بازگشت داده می‌شود.
- پشتیبانی از اجرای offline آن وجود ندارد. اگر اتصال SignalR موجود قطع شود، دیگر نمی‌توان از برنامه استفاده کرد.
- با توجه به نیاز به استفاده‌ی از یک اتصال دائم SignalR به ازای هر کاربر، مقیاس پذیری این نوع برنامه‌ها کمتر است. البته اگر تعداد کاربران برنامه‌های شما در یک شبکه‌ی اینترانت داخلی شرکتی محدود است، این مورد مشکل خاصی نخواهد بود. از دیدگاهی دیگر اگر تعداد کاربران برنامه‌ی شما بسیار زیاد است، استفاده از Blazor Server توصیه نمی‌شود. البته باید دقت داشت که سروری با 4GB RAM، می‌تواند 5000 کاربر همزمان SignalR را مدیریت کند.


ب) Blazor web assembly یا به اختصار Blazor WASM
مزایا:
- هیچ نوع وابستگی به سمت سرور ندارد. همینقدر که برنامه توسط مرورگر دریافت شد، قابل اجر است.
- برای هاست آن الزاما نیازی به یک سرور IIS و یا یک وب سرور ASP.NET Core نیست.
- امکان ارائه‌ی آن توسط یک CDN نیز وجود دارد.
- چون در این حالت کل برنامه توسط مرورگر دریافت می‌شود، قابلیت اجرای آفلاین را نیز پیدا می‌کند.
- برای کار، نیازی به اتصال دائم SignalR را ندارد؛ به همین جهت مقیاس پذیری آن بیشتر است.

معایب:
- حتما نیاز به استفاده‌ی از مرورگرهای جدید با پشتیبانی از web assembly را دارد؛ برای مثال نیاز به کروم نگارش 57 به بعد و فایرفاکس نگارش 52 به بعد را دارد و بر روی IE اجرا نمی‌شود.
- چون کل برنامه در این حالت توسط مرورگر دریافت می‌شود، حجم ابتدایی دریافت آن کمی بالا است.
- میدان دید و عملکرد آن همانند سایر برنامه‌های SPA، محدود است به امکاناتی که مرورگر، در اختیار برنامه قرار می‌دهد.



ایجاد پروژه‌های خالی Blazor Server و Blazor web assembly

یا می‌توانید از ویژوال استودیوی کامل و منوی افزودن پروژه‌ی آن برای اینکار استفاده کنید و یا اگر به خروجی دستور dotnet new --list مراجعه کنیم، SDK دات نت 5، به همراه دو قالب مرتبط زیر نیز هست:
C:\Users\Vahid>dotnet new --list
Templates                                         Short Name               Language          Tags
--------------------------------------------      -------------------      ------------      ----------------------
Blazor Server App                                 blazorserver             [C#]              Web/Blazor
Blazor WebAssembly App                            blazorwasm               [C#]              Web/Blazor/WebAssembly
بنابراین فقط کافی است دستور dotnet new blazorserver و یا dotnet new blazorwasm را در یک پوشه‌ی خالی اجرا کنیم تا بر اساس قالب‌های پیش‌فرض ارائه شده، بتوان پروژه‌های خالی Blazor Server و یا Blazor WebAssembly را ایجاد کرد.


در قسمت بعد، این دو پروژه‌ی خالی فوق را ایجاد کرده و ساختار آن‌ها را بررسی می‌کنیم. همچنین نکاتی را هم که در این قسمت در مورد نحوه‌ی هاست این برنامه‌ها عنوان شد، بر روی این پروژه‌ها مشاهده خواهیم کرد.
اشتراک‌ها
آموزش ساخت برنامه موبایل Uber با استفاده از React Native

Use Tailwind CSS with RN for awesome styling!

Use the Google Distance Matrix API to calculate Travel time and Distance (+ Cost!)

Use the Directions Google API for real navigation!

Use Google places API for real navigations!

Use apple & google maps for iOS & Android 

Use React Native Navigation to navigate between screens!

Use React Native Elements to elevate your app design! 

آموزش ساخت برنامه موبایل Uber با استفاده از React Native
اشتراک‌ها
بررسی بدافزار دات‌نتی Async RAT
Dissecting Async RAT — . Net based Malware — Malware Analysis

AsyncRAT is a Remote Access Tool (RAT) designed to remotely monitor and control other computers through a secure encrypted connection.
It is an open source remote administration tool, however, it could also be used maliciously because it provides functionality such as keylogger, remote desktop control, and many other functions that may cause harm to the victim’s computer.
بررسی بدافزار دات‌نتی Async RAT
اشتراک‌ها
پیش‌نمایش نسخه 15.8 Visual Studio 2017
پیش‌نمایش نسخه 15.8 Visual Studio 2017
اشتراک‌ها
سری ویدیوهای VS Code Day 2021
Join the Visual Studio Code team and community at a live event just for VS Code users. Get a glimpse of things to come and meet the team who works on VS Code every day.
 
سری ویدیوهای VS Code Day 2021
مطالب
لینک‌های هفته اول دی

وبلاگ‌ها و سایت‌های ایرانی

امنیت



ASP. Net


طراحی وب

PHP


اس‌کیوال سرور


سی شارپ


عمومی دات نت


مسایل اجتماعی و انسانی برنامه نویسی


کتاب‌های رایگان جدید


متفرقه
  • آهن بجای کروم! (یک برنامه نویس آلمانی قسمت‌هایی از مرورگر کروم را که در جهت جمع آوری اطلاعات برای گوگل بکار می‌رفته، حذف کرده و مرورگر دیگری به نام آهن را ارائه داده است!)

مطالب
مدیریت پیشرفته‌ی حالت در React با Redux و Mobx - قسمت هشتم - تنظیمات پروژه‌های React برای کار با Mobx decorators
تا اینجا دو مثالی را که از Mobx بررسی کردیم (مثال ورود متن و مثال کامپوننت شمارشگر)، به عمد به همراه decoratorهای @ دار آن نبودند. برای مثال در قسمت قبل، یک کلاس را با یک خاصیت ایجاد کردیم که روش مزین سازی خاصیت value آن را با  observable decorator، توسط متد decorate انجام دادیم و این هم یک روش کار با MobX است؛ بدون اینکه نیاز به تنظیمات خاصی را داشته باشد:
import { decorate } from "mobx";

class Count {
  value = 0;
}

decorate(Count, { value: observable });
const count = new Count();
اما اگر همان مثال بسیار ساده‌ی ورود متن را بخواهیم توسط decoratorهای @ دار MobX پیاده سازی کنیم ... پروژه‌ی استاندارد React ما کامپایل نخواهد شد که در این قسمت، روش رفع این مشکل را بررسی می‌کنیم.


بازنویسی مثال ورود متن و نمایش آن با Mobx decorators

در اینجا یک text-box، به همراه دو div در صفحه رندر خواهند شد که قرار است با ورود اطلاعاتی در text-box، یکی از آن‌ها (text-display) این اطلاعات را به صورت معمولی و دیگری (text-display-uppercase) آن‌را به صورت uppercase نمایش دهد. روش کار انجام شده هم مستقل از React است و به صورت مستقیم با استفاده از DOM API عمل شده‌است. این مثال را پیشتر در اولین قسمت بررسی MobX، ملاحظه کردید. اکنون اگر بخواهیم بجای شیءای که توسط متد observable کتابخانه‌ی MobX محصور شده‌است:
const text = observable({
  value: "Hello world!",
  get uppercase() {
    return this.value.toUpperCase();
  }
});
از یک کلاس ES6 به همراه Mobx decorators استفاده کنیم، به یک چنین پروژه‌ی جدیدی خواهیم رسید:
ابتدا یک پروژه‌ی جدید React را ایجاد می‌کنیم:
> create-react-app state-management-with-mobx-part3
> cd state-management-with-mobx-part3
> npm start
در ادامه کتابخانه‌ی mobx را نیز نصب می‌کنیم. برای این منظور پس از باز کردن پوشه‌ی اصلی برنامه توسط VSCode، دکمه‌های ctrl+` را فشرده (ctrl+back-tick) و دستور زیر را در ترمینال ظاهر شده وارد کنید:
> npm install --save mobx
در ادامه، ابتدا فایل public\index.html را جهت نمایش دو div و یک text-box، ویرایش می‌کنیم:
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>MobX Basics, part 3</title>
    <meta charset="UTF-8" />
    <link href="src/styles.css" />
  </head>

  <body>
    <main>
      <input id="text-input" />
      <p id="text-display"></p>
      <p id="text-display-uppercase"></p>
    </main>

    <script src="src/index.js"></script>
  </body>
</html>
سپس محتویات فایل src\index.js را نیز به نحو زیر تغییر می‌دهیم:
import { autorun, computed, observable } from "mobx";

const input = document.getElementById("text-input");
const textDisplay = document.getElementById("text-display");
const loudDisplay = document.getElementById("text-display-uppercase");

class Text {
  @observable value = "Hello World";
  @computed get uppercase() {
    return this.value.toUpperCase();
  }
}

const text = new Text();

input.addEventListener("keyup", event => {
  text.value = event.target.value;
});

autorun(() => {
  input.value = text.value;
  textDisplay.textContent = text.value;
  loudDisplay.textContent = text.uppercase;
});
تنها تفاوت این نگارش با نگارش قبلی آن، استفاده از کلاس Text که یک کلاس ES6 به همراه MobX Decorators است، بجای یک شیء ساده‌ی جاوا اسکریپتی می‌باشد. در اینجا خاصیت value به صورت observable@ تعریف شده و در نتیجه‌ی تغییر مقدار آن در کدهای برنامه، خاصیت محاسباتی وابسته‌ی به آن یا همان uppercase که با computed@ تزئین شده، به صورت خودکار به روز رسانی خواهد شد. متد autorun نیز به این تغییرات که حاصل فشرده شدن کلیدها هستند، واکنش نشان داده و متن دو div موجود در صفحه را به روز رسانی می‌کند.

اکنون اگر در همین حال، برنامه را با دستور npm start اجرا کنیم، با خطای زیر متوقف خواهیم شد:
./src/index.js
SyntaxError: \src\index.js: Support for the experimental syntax 'decorators-legacy' isn't currently enabled (8:3):

   6 | 
   7 | class Text {
>  8 |   @observable value = "Hello World";
     |   ^
   9 |   @computed get uppercase() {
  10 |     return this.value.toUpperCase();
  11 |   }

راه حل اول: از Decorators استفاده نکنیم!

یک راه حل مشکل فوق این است که بدون هیچ تغییری در ساختار پروژه‌ی React خود، اصلا از decorator syntax استفاده نکنیم. برای مثال اگر یک کلاس متداول MobX ای چنین شکلی را دارد:
import { observable, computed, action } from "mobx";

class Timer {
  @observable start = Date.now();
  @observable current = Date.now();

  @computed
  get elapsedTime() {
    return this.current - this.start + "milliseconds";
  }

  @action
  tick() {
    this.current = Date.now();
  }
}
می‌توان آن‌را بدون استفاده از decorator syntax، به صورت زیر نیز تعریف کرد:
import { observable, computed, action, decorate } from "mobx";

class Timer {
  start = Date.now();
  current = Date.now();

  get elapsedTime() {
    return this.current - this.start + "milliseconds";
  }

  tick() {
    this.current = Date.now();
  }
}

decorate(Timer, {
  start: observable,
  current: observable,
  elapsedTime: computed,
  tick: action
});
نمونه‌ی این روش را در قسمت قبل با تعریف شیء شمارشگر مشاهده کرده‌اید. در اینجا با توجه به اینکه Decorators در جاوا اسکریپت چیزی نیستند بجز بیان زیبای higher-order functions و higher-order functions هم توابعی هستند که توابع دیگر را با ارائه‌ی قابلیت‌های بیشتری، محصور می‌کنند، به همین جهت هر کاری را که بتوان با تزئین کننده‌ها انجام داد، همان را با توابع معمولی جاوا اسکریپتی نیز می‌توان انجام داد. اینکار را در مثال فوق توسط متد decorate مشاهده می‌کنید. این متد ابتدا نوع کلاس خاصی را دریافت کرده و سپس در پارامتر دوم آن می‌توان شیءای را تعریف کرد که خواص آن، همان خواص کلاس پارامتر اول است و مقادیر این خواص، تزئین کننده‌هایی هستند که قرار است برای آن‌ها بکار گرفته شوند. مزیت این روش بدون تغییر باقی ماندن تعریف کلاس Timer در اینجا و همچنین انجام هیچگونه تغییری در ساختار پروژه‌ی React، بدون نیاز به نصب بسته‌های کمکی اضافی است.

همچنین در این حالت بجای استفاده از کامپوننت‌های کلاسی، باید از روش بکارگیری متد observer برای محصور کردن کامپوننت تابعی تعریف شده استفاده کرد (تا دیگر نیازی به ذکر observer class@ نباشد):
const Counter = observer(({ count }) => {
  return (
   // ...
  );
});


راه حل دوم: از تایپ‌اسکریپت استفاده کنید!

create-react-app امکان ایجاد پروژه‌های React تایپ‌اسکریپتی را با ذکر سوئیچ typescript نیز دارد:
> create-react-app my-proj1 --typescript
پس از ایجاد پروژه، فایل tsconfig.json آن‌را یافته و experimentalDecorators آن‌را به true تنظیم کنید:
{
  "compilerOptions": {
    // ...
    "experimentalDecorators": true
  }
}
این تنها تغییری است که مورد نیاز می‌باشد و پس از آن برنامه‌ی React جاری، بدون مشکلی می‌تواند با decorators کار کند.


فعالسازی MobX Decorators در پروژه‌های استاندارد React مبتنی بر ES6

MobX از legacy" decorators spec" پشتیبانی می‌کند. یعنی اگر پروژه‌ای از spec جدید استفاده کند، دیگر نخواهد توانست با MobX فعلی کار کند. این هم مشکل MobX نیست. مشکل اینجا است که باید دانست کلا decorators در زبان جاوااسکریپت هنوز در مرحله‌ی آزمایشی قرار دارند و تکلیف spec نهایی و تائید شده‌ی آن مشخص نیست.
برای فعالسازی decorators در یک پروژه‌ی React استاندارد مبتنی بر ES6، شاید کمی جستجو کنید و به نتایجی مانند افزودن فایل babelrc. به ریشه‌ی پروژه و نصب افزونه‌هایی مانند babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties@ برسید. اما ... این‌ها بدون اجرای دستور npm run eject کار نمی‌کنند و اگر این دستور را اجرا کنیم، در نهایت به یک فایل package.json بسیار شلوغ خواهیم رسید (اینبار ارجاعات به Babel، Webpack و تمام ابزارهای دیگر نیز ظاهر می‌شوند). همچنین این عملیات نیز یک طرفه‌است. یعنی از این پس قرار است کنترل تمام این پشت صحنه، در اختیار ما باشد و به روز رسانی‌های بعدی create-react-app را با مشکل مواجه می‌کند. این گزینه صرفا مختص توسعه دهندگان پیشرفته‌ی React است. به همین جهت نیاز به روشی را داریم تا بتوانیم تنظیمات Webpack و کامپایلر Babel را بدون اجرای دستور npm run eject، تغییر دهیم تا در نتیجه، decorators را در آن فعال کنیم و خوشبختانه پروژه‌ی react-app-rewired دقیقا برای همین منظور طراحی شده‌است.

بنابراین ابتدا بسته‌های زیر را نصب می‌کنیم:
> npm i --save-dev customize-cra react-app-rewired
بسته‌ی react-app-rewired، امکان بازنویسی تنظیمات webpack پروژه‌ی react را بدون eject آن میسر می‌کند. customize-cra نیز با استفاده از امکانات همین بسته، نگارش‌های جدیدتر create-react-app را پشتیبانی می‌کند.

پس از نصب این پیشنیازها، فایل جدید config-overrides.js را به ریشه‌ی پروژه، جائیکه فایل package.json قرار گرفته‌است، با محتوای زیر اضافه کنید تا پشتیبانی ازlegacy" decorators spec" فعال شوند:
const {
  override,
  addDecoratorsLegacy,
  disableEsLint
} = require("customize-cra");

module.exports = override(
  // enable legacy decorators babel plugin
  addDecoratorsLegacy(),

  // disable eslint in webpack
  disableEsLint()
);
در ادامه فایل package.json را گشوده و قسمت scripts آن‌را برای استفاده‌ی از react-app-rewired، به صورت زیر بازنویسی کنید تا امکان تغییر تنظیمات webpack به صورت پویا در زمان اجرای برنامه، میسر شود:
  "scripts": {
    "start": "react-app-rewired start",
    "build": "react-app-rewired build",
    "test": "react-app-rewired test",
    "eject": "react-app-rewired eject"
  },
پس از این تغییرات، نیاز است دستور npm start را یکبار دیگر از ابتدا اجرا کنید. اکنون برنامه بدون مشکل کامپایل شده و خروجی بدون خطایی در مرورگر نمایش داده خواهد شد.


تنظیمات ESLint مخصوص کار با decorators

فایل ویژه‌ی eslintrc.json. که در ریشه‌ی پروژه قرار می‌گیرد (این فایل بدون نام است و فقط از پسوند تشکیل شده)، برای پروژه‌های MobX، باید حداقل تنظیم زیر را داشته باشد تا ESLint بتواند legacyDecorators را نیز پردازش کند:
{
  "extends": "react-app",
  "parserOptions": {
    "ecmaFeatures": {
      "legacyDecorators": true
    }
  }
}
و یا یک نمونه‌ی غنی شده‌ی فایل eslintrc.json. مخصوص برنامه‌های React به صورت زیر است:
{
    "env": {
        "node": true,
        "commonjs": true,
        "browser": true,
        "es6": true,
        "mocha": true
    },
    "settings": {
        "react": {
            "version": "detect"
        }
    },
    "parserOptions": {
        "ecmaFeatures": {
            "jsx": true,
            "legacyDecorators": true
        },
        "ecmaVersion": 2018,
        "sourceType": "module"
    },
    "plugins": [
        "babel",
        "react",
        "react-hooks",
        "react-redux",
        "no-async-without-await",
        "css-modules",
        "filenames",
        "simple-import-sort"
    ],
    "rules": {
        "no-const-assign": "warn",
        "no-this-before-super": "warn",
        "constructor-super": "warn",
        "strict": [
            "error",
            "safe"
        ],
        "no-debugger": "error",
        "brace-style": [
            "error",
            "1tbs",
            {
                "allowSingleLine": true
            }
        ],
        "no-trailing-spaces": "error",
        "keyword-spacing": "error",
        "space-before-function-paren": [
            "error",
            "never"
        ],
        "spaced-comment": [
            "error",
            "always"
        ],
        "vars-on-top": "error",
        "no-undef": "error",
        "no-undefined": "warn",
        "comma-dangle": [
            "error",
            "never"
        ],
        "quotes": [
            "error",
            "double"
        ],
        "semi": [
            "error",
            "always"
        ],
        "guard-for-in": "error",
        "no-eval": "error",
        "no-with": "error",
        "valid-typeof": "error",
        "no-unused-vars": "error",
        "no-continue": "warn",
        "no-extra-semi": "warn",
        "no-unreachable": "warn",
        "no-unused-expressions": "warn",
        "max-len": [
            "warn",
            80,
            4
        ],
        "react/prefer-es6-class": "warn",
        "react/jsx-boolean-value": "warn",
        "react-hooks/rules-of-hooks": "error",
        "react-hooks/exhaustive-deps": "warn",
        "react/prop-types": "off",
        "react-redux/mapDispatchToProps-returns-object": "off",
        "react-redux/prefer-separate-component-file": "off",
        "no-async-without-await/no-async-without-await": "warn",
        "css-modules/no-undef-class": "off",
        "filenames/match-regex": [
            "off",
            "^[a-zA-Z]+\\.*\\b(typescript|module|locale|validate|test|action|api|reducer|saga)?\\b$",
            true
        ],
        "filenames/match-exported": "off",
        "filenames/no-index": "off",
        "simple-import-sort/sort": "error"
    },
    "extends": [
        "react-app",
        "eslint:recommended",
        "plugin:react/recommended",
        "plugin:react-redux/recommended",
        "plugin:css-modules/recommended"
    ],
    "globals": {
        "Atomics": "readonly",
        "SharedArrayBuffer": "readonly",
        "process": true
    }
}
البته برای اینکه این تنظیمات کار کند، باید افزونه‌های زیر را نیز به صورت محلی در ریشه‌ی پروژه‌ی جاری نصب کنید (این مورد از ESLint 6x به بعد اجباری است و از بسته‌های global استفاده نمی‌کند):
>npm i --save-dev eslint babel-eslint eslint-config-react-app eslint-loader eslint-plugin-babel eslint-plugin-react eslint-plugin-css-modules eslint-plugin-filenames eslint-plugin-flowtype eslint-plugin-import eslint-plugin-no-async-without-await eslint-plugin-react-hooks eslint-plugin-react-redux eslint-plugin-redux-saga eslint-plugin-simple-import-sort eslint-loader typescript
پس از آن می‌توان فایل config-overrides.js را به صورت زیر نیز بر اساس تنظیمات فوق، بهبود بخشید:
const {
  override,
  addDecoratorsLegacy,
  useEslintRc
} = require("customize-cra");

module.exports = override(
  addDecoratorsLegacy(),
  useEslintRc(".eslintrc.json")
);


رفع اخطار مرتبط با decorators در VSCode

تا اینجا کار تنظیم کامپایلر babel، جهت پردازش decorators انجام شد. اما خود VSCode نیز چنین اخطاری را در پروژه‌هایی که از decorates استفاده می‌کنند، نمایش می‌دهد:
Experimental support for decorators is a feature that is subject to change in a future release.
Set the 'experimentalDecorators' option in your 'tsconfig' or 'jsconfig' to remove this warning.ts(1219)
برای رفع آن، فایل جدید tsconfig.json را در ریشه‌ی پروژه ایجاد کرده و آن‌را به صورت زیر تکمیل کنید تا ادیتور تایپ‌اسکریپتی VSCode، دیگر خطاهای مرتبط با decorators را نمایش ندهد:
{
    "compilerOptions": {
        "experimentalDecorators": true,
        "allowJs": true
    }
}

کدهای کامل این قسمت را می‌توانید از اینجا دریافت کنید: state-management-with-mobx-part3.zip
مطالب
مبانی TypeScript؛ ماژول‌ها
تاریخچه

تا پیش از نگارش 1.5 تایپ اسکریپت، مفاهیم internal modules و external modules وجود داشتند. جهت نامگذاری بهتر و کاهش سردرگمی در استفاده‌ی آن‌ها، از نگارش 1.5 به بعد، ماژول‌های داخلی به namespaces (فضاهای نام) تغییر نام یافتند و ماژول‌های خارجی به نام «ماژول» خلاصه شدند.
همچنین از نگارش 1.5 به بعد، پشتیبانی کاملی از نحوه‌ی تعریف «ماژول‌ها در ES 6» نیز به عمل می‌آید. بنابراین مطالعه‌ی آن نیز پیشنهاد می‌گردد.


مفهوم ماژول‌ها

هدف اصلی از ماژول‌ها، ارائه‌ی روشی برای مدیریت و ساماندهی پروژه‌های بزرگ با تعداد فایل‌های زیاد است. در اینجا فایل‌های ارجاعی، در زمان اجرا، توسط runtime جاوا اسکریپت بارگذاری شده و سپس به امکانات آن‌ها دسترسی خواهیم داشت. ماژول‌ها به صورت توکار در Node.JS نیز پشتیبانی می‌شوند؛ البته با فرمت common.js که کامپایلر TypeScript نیز قادر به تولید آن است.


امکان کامپایل به روش‌های قدیمی‌تر تعریف ماژول‌ها در TypeScript

در مورد انواع روش‌های قدیمی‌تر نحوه‌ی تعریف ماژول‌های در جاوا اسکریپت مانند common.js، AMD و امثال آن‌ها، مطالعه‌ی مطلب «ماژول‌ها در ES 6» توصیه می‌شود. فقط نکته‌ای که در اینجا حائز اهمیت است، این است که چون TypeScript قادر است به ES 5 نیز کامپایل شود و در ES 5 روش جدید ES 6 جهت تعریف ماژول‌ها وجود ندارد، امکان تبدیل و ترجمه‌ی کدهای TypeScript به تمام نوع‌های معروف و شناخته شده‌ی ماژول‌ها مانند common.js توسط کامپایلر TypeScript به صورت خودکار وجود دارد. برای این منظور از سوئیچ module کامپایلر استفاده می‌شود.


نحوه‌ی تعریف ماژول‌ها در TypeScript

برای تبدیل یک فایل ts به یک ماژول، تنها کافی است موردی را از آن export کنیم. آیتم‌های موجود در یک ماژول، تنها زمانی در سایر فایل‌ها قابل استفاده خواهند بود که از آن export شده باشند:
 // periodicals.ts
export interface Periodical {
   issueNumber: number;
}

export class Magazine implements Periodical {
   issueNumber: number;
}

export function GetMagazineByIssueNumber(issue: number): Magazine {
   // retrieve and return a magazine
}
در این مثال، یک اینترفیس، کلاس و متد export شده‌اند. برای این منظور واژه‌ی کلیدی export به پیش از هر کدام از آیتم‌های مدنظر اضافه شده‌است.
روش دیگر انجام این تعاریف، حذف واژه‌ی کلیدی export از تمام موارد تعریف شده و سپس خلاصه کردن آن‌ها در یک سطر، توسط روش export statement است؛ به نحو ذیل:
 // periodicals.ts
interface Periodical {
   issueNumber: number;
}

class Magazine implements Periodical {
   issueNumber: number;
}

function GetMagazineByTitle(title: string): Magazine {
   // retrieve and return a magazine
}

export { Periodical, Magazine, GetMagazineByTitle as GetMag}
مزیت این روش، مشخص بودن محل تعاریف خروجی‌ها است؛ بدون اینکه نیازی باشد تا تمام فایل‌را جهت یافتن exportها جستجو کرد.
همچنین در اینجا می‌توان نام دیگری را نیز برای خروجی‌ها درنظر گرفت. برای مثال بجای نام GetMagazineByTitle، با استفاده از as syntax، یک نام جدید معرفی شده‌است.


نحوه‌ی استفاده‌ی از ماژول‌ها در TypeScript

برای استفاده‌ی از امکانات خروجی مثال قبل، در یک ماژول دیگر، به نحو ذیل عمل می‌کنیم:
 // news.ts
import { Magazine, GetMag as GetMagazine} from './periodicals';
let newsMag: Magazine = GetMagazine('Weekly News');
در اینجا پس از تعریف واژه‌ی کلیدی import، لیست موارد مدنظر از خروجی‌های فایل periodicals را داخل یک {} می‌توان قید کرد. بنابراین نیازی نیست تا تمام خروجی‌های یک ماژول را import کرد. همچنین در اینجا نیز با استفاده از as syntax می‌توان نام جدیدی را برای موارد import شده تعیین کرد.
در انتها نیز مسیر نسبی فایل ts ماژول، بدون ذکر پسوند آن، پس از واژه‌ی کلیدی from ذکر می‌شود.

اگر نیاز است تمام خروجی‌های یک ماژول به صورت خودکار import شوند، می‌توان از * استفاده کرد:
 // kids.ts
import * as mag from './periodicals';
اینبار با توجه به as syntax استفاده شده، نحوه‌ی دسترسی به خروجی‌های ماژول مدنظر به صورت ذیل خواهد بود (ابتدا ذکر نام alias تعریف شده، به همراه یک دات):
 let kidMag: mag.Magazine= mag.GetMag('Games and Stuff!');


خروجی پیش فرض یک ماژول

اگر تنها قرار است یک آیتم از ماژولی export شود، می‌توان از مفهوم default export استفاده کرد:
 // movie.ts
export default class{
   title: string;
   director: string;
}
در این مثال export default بر روی یک کلاس بدون نام تعریف شده‌است. تعریف نام کلاس در اینجا اختیاری است و ماژول import کننده‌ی آن نیازی به دانستن این نام ندارد؛ زیرا در این حالت import کننده می‌تواند نام دلخواهی را به این خروجی پیش فرض بدهد؛ مانند AnimatedMovie بدون نیاز به ذکر {}:
 // kids.ts
import AnimatedMovie from './movie’;
let cartoon = new AnimatedMovie();