سرور مجازی

۲ مطلب با کلمه‌ی کلیدی «TypeScript» ثبت شده است

  • ۰
  • ۰

نحوه استفاده از رابط ها در TypeScript

ورود به سایت

معرفی

TypeScript افزونه ای از زبان جاوا اسکریپت است که از زمان اجرای جاوا اسکریپت با یک نوع تایپ کننده زمان کامپایل استفاده می کند.

TypeScript روش های متعددی را برای نمایش اشیاء در کد شما ارائه می دهد که یکی از آنها استفاده از رابط ها است. رابط های موجود در TypeScript دو ​​سناریو استفاده دارند: می توانید قراردادی ایجاد کنید که کلاسها باید از آن پیروی کنند ، مانند اعضایی که آن کلاسها باید اجرا کنند ، و همچنین می توانید انواع را در برنامه خود نشان دهید ، درست مانند اعلان نوع معمولی. (برای اطلاعات بیشتر در مورد انواع ، نحوه استفاده از انواع اصلی در TypeScript و نحوه ایجاد انواع سفارشی در TypeScript را بررسی کنید.)

ممکن است توجه داشته باشید که رابط ها و انواع دارای مجموعه ای از ویژگی های مشابه هستند. در واقع ، تقریباً همیشه می توان یکی را جایگزین دیگری کرد. تفاوت اصلی این است که رابط ها ممکن است بیش از یک اعلان برای یک رابط داشته باشند ، که TypeScript ادغام می شود ، در حالی که انواع را فقط یک بار می توان اعلام کرد. همچنین می توانید از انواع برای ایجاد نام مستعار از انواع اولیه (مانند رشته و بولی) استفاده کنید ، که رابط ها نمی توانند انجام دهند.

رابط های موجود در TypeScript راهی قدرتمند برای نمایش ساختارهای نوع هستند. آنها به شما امکان می دهند استفاده از آن ساختارها را ایمن کرده و به طور همزمان آنها را مستند کنید ، به طور مستقیم تجربه توسعه دهنده را بهبود می بخشد.

در این آموزش ، شما رابط هایی را در TypeScript ایجاد می کنید ، نحوه استفاده از آنها را یاد می گیرید و تفاوت بین انواع معمولی و رابط ها را درک می کنید. نمونه های مختلف کد را امتحان می کنید ، که می توانید آنها را در محیط TypeScript یا TypeScript Playground خود ، محیط آنلاین که به شما امکان می دهد TypeScript را مستقیماً در مرورگر بنویسید ، دنبال کنید.

پیش نیازها

برای پیگیری این آموزش ، شما نیاز دارید:

  • محیطی که در آن می توانید برنامه های TypeScript را اجرا کنید تا همراه با مثال ها دنبال کنید. برای تنظیم این دستگاه در دستگاه محلی خود ، به موارد زیر نیاز دارید.
    • هر دو Node و npm (یا نخ) به منظور اجرای یک محیط توسعه که بسته های مربوط به TypeScript را مدیریت می کند ، نصب شده است. این آموزش با Node.js نسخه 14.3.0 و npm نسخه 6.14.5 آزمایش شده است. برای نصب روی macOS یا اوبونتو 18.04 ، مراحل نحوه نصب Node.js و ایجاد محیط توسعه محلی در macOS یا قسمت Installing using a PPA از نحوه نصب Node.js در اوبونتو 18.04 را دنبال کنید. اگر از زیر سیستم Windows برای Linux (WSL) استفاده می کنید ، این نیز کار می کند.
    • علاوه بر این ، به TypeScript Compiler (tsc) نصب شده بر روی دستگاه خود نیاز دارید.
  • شما به دانش کافی از جاوا اسکریپت ، به ویژه نحو ES6+ ، مانند تخریب ، عملگرهای استراحت و واردات/صادرات نیاز دارید. اگر به اطلاعات بیشتری در مورد این موضوعات نیاز دارید ، خواندن نحوه کدگذاری در سری JavaScript توصیه می شود.
  • این آموزش به جنبه های ویرایشگرهای متنی که از TypeScript پشتیبانی می کنند اشاره می کند و خطاهای خطی را نشان می دهد. این برای استفاده از TypeScript ضروری نیست ، اما از ویژگیهای TypeScript بیشتر استفاده می کند. برای بهره مندی از این موارد ، می توانید از ویرایشگر متنی مانند Visual Studio Code استفاده کنید که از TypeScript پشتیبانی کامل دارد. همچنین می توانید این مزایا را در TypeScript Playground امتحان کنید.

همه نمونه های نشان داده شده در این آموزش با استفاده از TypeScript نسخه 4.2.2 ایجاد شده است.

ایجاد و استفاده از رابط ها در TypeScript

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

رابط ها در TypeScript با استفاده از کلید واژه رابط و سپس نام رابط و سپس یک بلوک {} با بدنه رابط ایجاد می شوند. به عنوان مثال ، در اینجا یک رابط Logger است:

interface Logger {
  log: (message: string) => void;
}

مشابه ایجاد یک نوع معمولی با استفاده از اعلان نوع ، فیلدهای نوع و نوع آنها را در {} مشخص می کنید:

interface Logger {
  log: (message: string) => void;
}

رابط Logger نشان دهنده یک شی است که دارای یک ویژگی واحد به نام log است. این ویژگی یک تابع است که یک پارامتر واحد از نوع string را می پذیرد و void را برمی گرداند.

می توانید از رابط Logger به عنوان هر نوع دیگری استفاده کنید. در اینجا یک مثال ایجاد یک شیء تحت اللفظی مطابق با رابط Logger آورده شده است:

interface Logger {
  log: (message: string) => void;
}

const logger: Logger = {
  log: (message) => console.log(message),
};

مقادیری که از رابط Logger به عنوان نوع خود استفاده می کنند باید دارای اعضایی مشابه اعلان رابط Logger باشند. اگر برخی از اعضا اختیاری باشند ، ممکن است حذف شوند.

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

interface Logger {
  log: (message: string) => void;
}

const logger: Logger = {
  log: (message) => console.log(message),
  otherProp: true,
};

در این حالت ، کامپایلر TypeScript خطای 2322 را منتشر می کند ، زیرا این ویژگی در اعلان رابط Logger وجود ندارد:

 

Output

Type '{ log: (message: string) => void; otherProp: boolean; }' is not assignable to type 'Logger'.
  Object literal may only specify known properties, and 'otherProp' does not exist in type 'Logger'. (2322)

نوع دیگر Extending

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

تصور کنید یک رابط کاربری قابل پاک کردن دارید ، مانند این یکی:

interface Clearable {
  clear: () => void;
}

سپس می توانید یک رابط جدید ایجاد کنید که از آن گسترش یافته و همه زمینه های آن را به ارث برده است. در مثال زیر ، رابط Logger در حال گسترش از رابط Clearble است. به خطوط برجسته توجه کنید:

interface Clearable {
  clear: () => void;
}

interface Logger extends Clearable {
  log: (message: string) => void;
}

رابط Logger هم اکنون دارای یک عضو واضح است که یک تابع است که هیچ پارامتری را نمی پذیرد و void را برمی گرداند. این عضو جدید از رابط Clearable به ارث برده شده است. مثل این است که ما این کار را کرده ایم:

interface Logger {
  log: (message: string) => void;
  clear: () => void;
}

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

برای بازگشت به مثال Clearble که قبلاً استفاده شد ، تصور کنید که برنامه شما نیاز به رابط متفاوتی دارد ، مانند رابط StringList زیر ، برای نشان دادن یک ساختار داده که دارای چندین رشته است:

interface StringList {
  push: (value: string) => void;
  get: () => string[];
}

با گسترش این رابط StringList جدید ، رابط Clearable موجود را گسترش می دهید ، شما مشخص می کنید که این رابط همچنین اعضایی را در رابط Clearable تنظیم کرده است و ویژگی روشن را به تعریف نوع رابط StringList اضافه می کند:

interface StringList extends Clearable {
  push: (value: string) => void;
  get: () => string[];
}

رابط ها می توانند از هر نوع شیء مانند رابط ها ، انواع معمولی و حتی کلاس ها گسترش یابند.

رابط هایی با امضای قابل تماس

اگر رابط نیز قابل تماس باشد (یعنی یک تابع نیز باشد) ، می توانید با ایجاد یک امضای قابل تماس ، این اطلاعات را در اعلان رابط منتقل کنید.

یک امضای قابل تماس با افزودن یک اعلان تابع در داخل رابط که به هیچ عضوی متصل نیست و با استفاده از: به جای => هنگام تنظیم نوع بازگشتی تابع ، ایجاد می شود.

به عنوان مثال ، یک امضای قابل تماس به رابط Logger خود اضافه کنید ، مانند کد برجسته زیر:

interface Logger {
  (message: string): void;
  log: (message: string) => void;
}

توجه داشته باشید که امضای قابل تماس شبیه به اعلان نوع یک تابع ناشناس است ، اما در نوع بازگشتی که استفاده می کنید: به جای =>. این بدان معناست که هر مقدار مقید به نوع رابط Logger می تواند مستقیماً به عنوان یک تابع فراخوانی شود.

برای ایجاد مقداری که با رابط Logger شما مطابقت داشته باشد ، باید الزامات رابط را در نظر بگیرید:

  1. باید قابل تماس باشد.
  2. باید دارای یک ویژگی به نام log باشد که یک تابع است که یک پارامتر رشته را قبول می کند.

بیایید متغیری به نام logger ایجاد کنیم که می تواند به نوع رابط Logger شما اختصاص داده شود:

interface Logger {
  (message: string): void;
  log: (message: string) => void;
}

const logger: Logger = (message: string) => {
  console.log(message);
}
logger.log = (message: string) => {
  console.log(message);
}

برای مطابقت با رابط Logger ، مقدار باید قابل تماس باشد ، به همین دلیل است که متغیر logger را به یک تابع اختصاص می دهید:

interface Logger {
  (message: string): void;
  log: (message: string) => void;
}

const logger: Logger = (message: string) => {
  console.log(message);
}
logger.log = (message: string) => {
  console.log(message);
}

سپس ویژگی log را به تابع logger اضافه می کنید:

interface Logger {
  (message: string): void;
  log: (message: string) => void;
}

const logger: Logger = (message: string) => {
  console.log(message);
}
logger.log = (message: string) => {
  console.log(message);
}

این توسط رابط Logger مورد نیاز است. مقادیر متصل به رابط Logger نیز باید دارای ویژگی log باشند که تابعی است که یک پارامتر یک رشته را می پذیرد و void را برمی گرداند.

اگر ویژگی log را وارد نکرده باشید ، کامپایلر TypeScript خطای 2741 را به شما می دهد:

 

Output

Property 'log' is missing in type '(message: string) => void' but required in type 'Logger'. (2741)

اگر ویژگی log در متغیر logger دارای امضای نوع ناسازگار باشد ، مانند تنظیم آن روی true ، خطای مشابهی را منتشر می کند:

interface Logger {
  (message: string): void;
  log: (message: string) => void;
}


const logger: Logger = (message: string) => {
  console.log(message);
}
logger.log = true;

در این مورد ، کامپایلر TypeScript خطای 2322 را نشان می دهد:

 

Output

Type 'boolean' is not assignable to type '(message: string) => void'. (2322)

یکی از ویژگیهای خوب تنظیم متغیرها برای داشتن یک نوع خاص ، در این مورد تنظیم متغیر logger برای نوع رابط Logger ، این است که TypeScript اکنون می تواند نوع پارامترهای عملکرد logger و عملکرد در log را استنباط کند. ویژگی.

با حذف اطلاعات نوع از آرگومان هر دو تابع ، می توانید آن را بررسی کنید. توجه داشته باشید که در کد برجسته زیر ، پارامترهای پیام یک نوع ندارند:

interface Logger {
  (message: string): void;
  log: (message: string) => void;
}


const logger: Logger = (message) => {
  console.log(message);
}
logger.log = (message) => {
  console.log(message);
}

و در هر دو مورد ، ویرایشگر شما هنوز باید بتواند نشان دهد که نوع پارامتر یک رشته است ، زیرا این نوع مورد انتظار رابط Logger است.

رابط با امضای فهرست

شما می توانید یک امضای فهرست به رابط کاربری خود اضافه کنید ، درست مانند انواع معمولی ، بنابراین به رابط اجازه می دهد تا تعداد نامحدودی از ویژگی ها را داشته باشد.

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

interface DataRecord {
  [key: string]: string;
}

سپس می توانید از رابط DataRecord برای تعیین نوع هر شیئی که دارای پارامترهای متعدد رشته نوع است استفاده کنید:

interface DataRecord {
  [key: string]: string;
}

const data: DataRecord = {
  fieldA: "valueA",
  fieldB: "valueB",
  fieldC: "valueC",
  // ...
};

در این بخش ، با استفاده از ویژگی های مختلف موجود در TypeScript ، رابط هایی ایجاد کردید و نحوه استفاده از رابط هایی را که ایجاد کردید آموختید. در بخش بعدی ، با تفاوت بین اعلان های نوع و رابط بیشتر آشنا می شوید و با ادغام اعلانات و افزایش ماژول تمرین می کنید.

تفاوت بین Types and Interfaces

تا کنون ، مشاهده کرده اید که اعلان رابط و اعلامیه مشابه هستند و تقریباً مجموعه ای از ویژگی ها را دارند.

به عنوان مثال ، شما یک رابط Logger ایجاد کرده اید که از یک رابط Clearble توسعه یافته است:

interface Clearable {
  clear: () => void;
}

interface Logger extends Clearable {
  log: (message: string) => void;
}

نمایش نوع یک را می توان با استفاده از دو نوع اعلان تکرار کرد:

type Clearable = {
  clear: () => void;
}

type Logger = Clearable & {
  log: (message: string) => void;
}

همانطور که در بخشهای قبلی نشان داده شد ، اعلان رابط می تواند برای نمایش انواع اشیاء ، از توابع گرفته تا اشیاء پیچیده با تعداد نامحدودی از ویژگی ها استفاده شود. این امر با اعلانات نوع ، حتی از انواع دیگر امکان پذیر است ، زیرا شما می توانید چندین نوع را با استفاده از عملگر تقاطع و با یکدیگر قطع کنید.

از آنجا که اعلانات نوع و اعلان های رابط بسیار شبیه هستند ، باید ویژگی های خاص منحصر به فرد هر یک را در نظر بگیرید و در پایگاه کد خود سازگار باشید. یکی را برای ایجاد نمایندگی های نوع در پایگاه کد خود انتخاب کنید و فقط زمانی از ویژگی دیگر استفاده کنید که به ویژگی خاصی که فقط در دسترس آن است نیاز داشته باشید.

به عنوان مثال ، اعلان نوع دارای چند ویژگی است که اعلان رابط فاقد آن است ، مانند:

  • انواع اتحادیه
  • انواع نقشه برداری شده
  • نام مستعار به انواع اولیه

یکی از ویژگی هایی که فقط برای اعلان رابط موجود است ادغام اعلامیه است که در قسمت بعدی با آن آشنا خواهید شد. توجه به این نکته ضروری است که ادغام اظهارنامه ممکن است مفید باشد اگر شما در حال نوشتن کتابخانه هستید و می خواهید به کاربران کتابخانه قدرت گسترش انواع ارائه شده توسط کتابخانه را بدهید ، زیرا این امر با اعلانات نوع امکان پذیر نیست.

اعلامیه ادغام

TypeScript می تواند چندین اظهارنامه را در یک واحد ادغام کند و به شما این امکان را می دهد که چندین اظهارنامه را برای یک ساختار داده یکسان بنویسید و آنها را در طول کامپایلر توسط کامپایلر TypeScript به هم متصل کرده و گویی یک نوع واحد هستند. در این بخش خواهید دید که چگونه این کار می کند و چرا هنگام استفاده از رابط ها مفید است.

رابط های TypeScript را می توان دوباره باز کرد. یعنی چندین اعلان از یک رابط یکسان می توانند ادغام شوند. این زمانی مفید است که می خواهید زمینه های جدیدی را به یک رابط موجود اضافه کنید.

به عنوان مثال ، تصور کنید که یک رابط به نام DatabaseOptions مانند زیر دارید:

interface DatabaseOptions {
  host: string;
  port: number;
  user: string;
  password: string;
}

از این رابط برای انتقال گزینه ها هنگام اتصال به پایگاه داده استفاده می شود.

بعداً در کد ، یک رابط با همان نام اما با یک رشته رشته به نام dsnUrl اعلام می کنید ، مانند این:

interface DatabaseOptions {
  dsnUrl: string;
}

وقتی کامپایلر TypeScript شروع به خواندن کد شما می کند ، تمام اعلانات رابط DatabaseOptions را در یک واحد ادغام می کند. از نظر کامپایلر TypeScript ، DatabaseOptions در حال حاضر:

interface DatabaseOptions {
  host: string;
  port: number;
  user: string;
  password: string;
  dsnUrl: string;
}

رابط شامل تمام زمینه هایی است که ابتدا اعلام کرده اید ، به علاوه فیلد جدید dsnUrl که به طور جداگانه اعلام کرده اید. هر دو اعلامیه ادغام شده اند.

افزایش ماژول

ادغام بیانیه زمانی مفید است که نیاز به افزایش ماژول های موجود با ویژگی های جدید داشته باشید. یک مورد استفاده برای آن زمانی است که شما فیلدهای بیشتری را به ساختار داده ارائه شده توسط یک کتابخانه اضافه می کنید. این نسبتاً با کتابخانه Node.js به نام express رایج است ، که به شما امکان می دهد سرورهای HTTP ایجاد کنید.

هنگام کار با اکسپرس ، یک درخواست و یک شیء پاسخ به کنترل کننده های درخواست شما ارسال می شود (توابع مسئول ارائه پاسخ به درخواست HTTP). شیء درخواست معمولاً برای ذخیره داده های خاص یک درخواست خاص استفاده می شود. به عنوان مثال ، می توانید از آن برای ذخیره کاربر ثبت شده که درخواست اولیه HTTP کرده است استفاده کنید:

const myRoute = (req: Request, res: Response) => {
  res.json({ user: req.user });
}

در اینجا ، کنترل کننده درخواست json را با کلاینت کاربر روی کاربر ثبت شده به مشتری ارسال می کند. کاربر ثبت شده در مکان دیگری از کد به شی درخواست اضافه می شود ، با استفاده از یک واسطه سریع مسئول احراز هویت کاربر.

تعاریف نوع برای رابط درخواست بدون فیلد کاربر است ، بنابراین کد بالا خطای 2339 را نشان می دهد:

Property 'user' does not exist on type 'Request'. (2339)

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

اگر نوع شیء Request را در اعلان نوع express بررسی کنید ، متوجه می شوید که یک رابط اضافه شده در داخل یک فضای نام عمومی به نام Express است ، همانطور که در اسناد مخزن DefinitelyTyped نشان داده شده است:

declare global {
    namespace Express {
        // These open interfaces may be extended in an application-specific manner via declaration merging.
        // See for example method-override.d.ts (https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/method-override/index.d.ts)
        interface Request {}
        interface Response {}
        interface Application {}
    }
}

برای استفاده از افزودن ماژول برای افزودن یک ویژگی جدید به رابط درخواست ، باید همان ساختار را در یک فایل اعلان نوع محلی تکرار کنید. به عنوان مثال ، تصور کنید که شما یک فایل با نام express.d.ts مانند فایل زیر ایجاد کرده اید و سپس آن را به گزینه انواع tsconfig.json خود اضافه کرده اید:

import 'express';

declare global {
  namespace Express {
    interface Request {
      user: {
        name: string;
      }
    }
  }
}

از دیدگاه کامپایلر TypeScript ، رابط درخواست دارای یک ویژگی کاربر است که نوع آنها روی یک شیء تنظیم شده است که دارای یک ویژگی واحد به نام name of type string است. این اتفاق می افتد زیرا همه اعلان ها برای یک رابط یکسان ادغام می شوند.

فرض کنید شما در حال ایجاد یک کتابخانه هستید و می خواهید به کاربران کتابخانه خود این امکان را بدهید که انواع ارائه شده توسط کتابخانه خود را افزایش دهند ، مانند آنچه در بالا با اکسپرس انجام دادید. در این حالت ، شما باید از اینترفیس های کتابخانه خود صادر کنید ، زیرا اظهارات نوع معمولی از افزایش ماژول پشتیبانی نمی کند.

نتیجه

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

 

برچسب‌ها:

 

 

  • behnam gol mohamadi
  • ۰
  • ۰

نحوه استفاده از دکوراتورها در TypeScript

ورود به سایت

معرفی

TypeScript افزونه ای از زبان جاوا اسکریپت است که از زمان اجرای جاوا اسکریپت با یک نوع تایپ کننده زمان کامپایل استفاده می کند. این ترکیب به توسعه دهندگان اجازه می دهد تا از اکوسیستم جاوا اسکریپت و ویژگی های زبان کامل استفاده کنند ، در حالی که علاوه بر این ، نوع تایپ استاتیک ، شمارش ها ، کلاس ها و رابط ها را در بالای آن اضافه می کنند. یکی از این ویژگی های اضافی پشتیبانی از دکوراتور است.

دکوراتورها راهی برای decorator اعضای یک کلاس یا خود کلاس با قابلیت های اضافی هستند. هنگامی که از یک decorator کننده برای یک کلاس یا عضو کلاس استفاده می کنید ، در واقع یک تابع را فراخوانی می کنید که جزئیات decorator شده را دریافت می کند ، و اجرای decorator قادر خواهد بود کد را به صورت پویا تغییر داده و قابلیت های اضافی را اضافه کند. کاهش کد بویلر آنها راهی برای متا برنامه نویسی در TypeScript هستند ، که یک تکنیک برنامه نویسی است که برنامه نویس را قادر می سازد تا کدی ایجاد کند که از کدهای دیگر خود برنامه به عنوان داده استفاده کند.

این آموزش به شما نشان می دهد که چگونه decorator خود را در TypeScript برای کلاس ها و اعضای کلاس ایجاد کنید ، و همچنین نحوه استفاده از آنها. شما را از طریق نمونه کد های مختلف راهنمایی می کند ، که می توانید آنها را در محیط TypeScript خود یا TypeScript Playground دنبال کنید ، یک محیط آنلاین که به شما امکان می دهد TypeScript را مستقیماً در مرورگر بنویسید.

پیش نیازها

برای پیگیری این آموزش ، شما نیاز دارید:

  • محیطی که در آن می توانید برنامه های TypeScript را اجرا کنید تا همراه با مثال ها دنبال کنید. برای تنظیم این دستگاه در دستگاه محلی خود ، به موارد زیر نیاز دارید.
    • هر دو Node و npm (یا نخ) به منظور اجرای یک محیط توسعه که بسته های مربوط به TypeScript را مدیریت می کند ، نصب شده است. این آموزش با Node.js نسخه 14.3.0 و npm نسخه 6.14.5 آزمایش شده است. برای نصب روی macOS یا اوبونتو 18.04 ، مراحل نحوه نصب Node.js و ایجاد محیط توسعه محلی در macOS یا بخش Installing using a PPA از نحوه نصب Node.js در اوبونتو 18.04 را دنبال کنید. اگر از زیر سیستم Windows برای Linux (WSL) استفاده می کنید ، این نیز کار می کند.
    • علاوه بر این ، به TypeScript Compiler (tsc) نصب شده بر روی دستگاه خود نیاز دارید. برای انجام این کار ، به وب سایت رسمی TypeScript مراجعه کنید.
  • اگر نمی خواهید محیط TypeScript را در دستگاه محلی خود ایجاد کنید ، می توانید از زمین بازی رسمی TypeScript برای پیگیری استفاده کنید.
  • شما به دانش کافی در مورد جاوا اسکریپت ، به ویژه نحو ES6+ ، مانند تخریب ، عملگرهای استراحت و واردات/صادرات نیاز دارید. اگر به اطلاعات بیشتری در مورد این موضوعات نیاز دارید ، خواندن نحوه کدگذاری در سری JavaScript توصیه می شود
  • این آموزش به جنبه های ویرایشگرهای متنی که از TypeScript پشتیبانی می کنند اشاره می کند و خطاهای خطی را نشان می دهد. این برای استفاده از TypeScript ضروری نیست ، اما از ویژگیهای TypeScript بیشتر استفاده می کند. برای به دست آوردن مزایای این موارد ، می توانید از ویرایشگر متنی مانند Visual Studio Code استفاده کنید که از TypeScript پشتیبانی کامل دارد. همچنین می توانید این مزایا را در TypeScript Playground امتحان کنید

همه نمونه های نشان داده شده در این آموزش با استفاده از TypeScript نسخه 4.2.2 ایجاد شده است.

فعال کردن پشتیبانی Decorators در TypeScript

در حال حاضر ، decorator هنوز یک ویژگی آزمایشی در TypeScript هستند و بنابراین ، ابتدا باید آن را فعال کنید. در این بخش ، بسته به نحوه کار با TypeScript ، نحوه فعال کردن decorator در TypeScript را خواهید دید.

CLI کامپایلر TypeScript

برای فعال کردن پشتیبانی دکوراتورها در هنگام استفاده از TypeScript Compiler CLI (tsc) تنها مرحله اضافی مورد نیاز این است که یک پرچم اضافی –experimentalDecorators:

tsc –experimentalDecorators

tsconfig.json

هنگام کار در پروژه ای که دارای فایل tsconfig.json است ، برای فعال کردن decorator تجربی ، باید ویژگی experimentalDecorators را به شیء compilerOptions اضافه کنید:

{
“compilerOptions”: {
“experimentalDecorators”: true
}
}

در زمین بازی TypeScript ، دکوراتورها به طور پیش فرض فعال هستند.

با استفاده از نحو دکوراتور

در این بخش ، شما در کلاسهای TypeScript از decorator استفاده خواهید کرد.

در TypeScript ، می توانید با استفاده از نحو خاصexpression ، decorator کننده ایجاد کنید ، جایی که بیان یک تابع است که به طور خودکار در زمان اجرا با جزئیات مربوط به هدفdecorator فراخوانی می شود.

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

  • خود اعلام کلاس
  • خواص
  • دستیاران
  • مواد و روش ها
  • مولفه های

برای مثال ، فرض کنید شما یک decorator به نام مهر و موم شده دارید که Object.seal را در یک کلاس فراخوانی می کند. برای استفاده از دکوراتور خود می توانید موارد زیر را بنویسید:

@sealed
class Person {}

در کد برجسته شده توجه کنید که decorator را درست قبل از هدف decorator مهر و موم شده خود اضافه کرده اید ، در این مورد ، اعلان کلاس Person.

همین امر برای انواع دیگر decorators معتبر است:

@classDecorator
class Person {
  @propertyDecorator
  public name: string;

  @accessorDecorator
  get fullName() {
    // ...
  }

  @methodDecorator
  printName(@parameterDecorator prefix: string) {
    // ...
  }
}

 

برای افزودن چند decorator ، آنها را یکی پس از دیگری به هم اضافه کنید:

@decoratorA
@decoratorB
class Person {}

ایجاد دکوراتورهای کلاس در TypeScript

در این بخش مراحل ایجاد decorator کلاس در TypeScript را طی خواهید کرد.

برای decorator به نامdecoratorA ، به TypeScript می گویید که باید تابع decoratorA را فراخوانی کند. یک تابع با جزئیات نحوه استفاده از decorator در کد خود فراخوانی می شود. به عنوان مثال ، اگر decorator را برای اعلان کلاس اعمال کرده اید ، تابع جزئیات مربوط به کلاس را دریافت می کند. این عملکرد باید در حدی باشد که دکوراتور شما بتواند کار کند.

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

@sealed
class Person {}

function sealed(target: Function) {
  Object.seal(target);
  Object.seal(target.prototype);
}

پارامتر (های) منتقل شده به دکوراتور بستگی به محل استفاده از دکوراتور دارد. پارامتر اول معمولاً هدف نامیده می شود.

دکوراتور مهر و موم شده فقط در اعلان های کلاس استفاده می شود ، بنابراین عملکرد شما یک پارامتر واحد ، هدف ، که از نوع Function است دریافت می کند. این سازنده کلاسی خواهد بود که decorator کننده روی آن اعمال شده است.

در تابع مهر و موم شده ، سپس Object.seal را روی هدف ، که سازنده کلاس است ، و همچنین در نمونه اولیه آنها فراخوانی می کنید. هنگامی که این کار را انجام می دهید ، هیچ ویژگی جدیدی نمی تواند به سازنده کلاس یا ویژگی آنها اضافه شود ، و خصوصیات موجود به عنوان غیر قابل تنظیم علامت گذاری می شوند.

مهم است که به یاد داشته باشید که در حال حاضر نمی توان نوع TypeScript هدف را هنگام استفاده از decorator گسترش داد. این بدان معناست که ، به عنوان مثال ، شما نمی توانید یک فیلد جدید با استفاده از یک تزئین کننده به کلاس اضافه کنید و آن را از نظر نوع ایمن کنید.

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

شما اولین decorator کننده خود را ایجاد کرده اید و از آن در کلاس استفاده کرده اید. در قسمت بعدی نحوه ایجاد کارخانه های decorator را خواهید آموخت.

ایجاد Decorator Factories

گاهی اوقات هنگام استفاده از دکوراتور باید گزینه های اضافی را به دکوراتور منتقل کنید و برای این کار باید از کارخانه های decorator استفاده کنید. در این بخش نحوه ایجاد آن کارخانه ها و استفاده از آنها را خواهید آموخت.

کارخانه های decorator توابعی هستند که عملکرد دیگری را برمی گردانند. آنها این نام را دریافت می کنند زیرا خود اجرای decorator نیستند. در عوض ، آنها عملکرد دیگری را که مسئول اجرای decorator است ، باز می گردانند و به عنوان یک تابع بسته بندی عمل می کنند. آنها در سفارشی سازی دکوراتورها مفید هستند ، زیرا اجازه می دهند کد مشتری هنگام استفاده از گزینه ها به decorator منتقل کند.

بیایید تصور کنیم که شما یک دکوراتور کلاس به نام decoratorA دارید و می خواهید گزینه ای را اضافه کنید که می توانید هنگام فراخوانی با دکوراتور مانند پرچم بولی تنظیم کنید. شما می توانید با نوشتن یک کارخانه تزئیناتی مشابه کارخانه زیر به این مهم برسید:

const decoratorA = (someBooleanFlag: boolean) => {
  return (target: Function) => {
  }
}

در اینجا ، تابع decoratorA با پیاده سازی تزئین کننده ، عملکرد دیگری را برمی گرداند. توجه کنید که چگونه کارخانه تزئینات پرچم بولی را به عنوان تنها پارامتر خود دریافت می کند:

const decoratorA = (someBooleanFlag: boolean) => {
  return (target: Function) => {
  }
}

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

const decoratorA = (someBooleanFlag: boolean) => {
  return (target: Function) => {
  }
}

@decoratorA(true)
class Person {}

در اینجا ، وقتی از decoratorA decorator استفاده می کنید ، قرار است کارخانه تزئینات با پارامتر someBooleanFlag روی true تنظیم شود. سپس اجرای تزئین کننده خود اجرا می شود. این به شما امکان می دهد رفتار دکوراتور خود را بر اساس نحوه استفاده از آن تغییر دهید ، و سفارشی سازی و استفاده مجدد از دکوراتورها را از طریق برنامه خود آسان کنید.

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

const decoratorA = (someBooleanFlag: boolean) => {
  return (target: Function) => {
  }
}

@decoratorA
class Person {}

کامپایلر TypeScript دو خطا به شما می دهد که ممکن است بسته به نوع تزئین کننده متفاوت باشد. برای تزئین کنندگان کلاس خطاها 1238 و 1240 است:

 

Output

Unable to resolve signature of class decorator when called as an expression.
  Type '(target: Function) => void' is not assignable to type 'typeof Person'.
    Type '(target: Function) => void' provides no match for the signature 'new (): Person'. (1238)
Argument of type 'typeof Person' is not assignable to parameter of type 'boolean'. (2345)

شما فقط یک کارخانه تزئینات ایجاد کرده اید که قادر به دریافت پارامترها و تغییر رفتار آنها بر اساس این پارامترها است. در مرحله بعد با نحوه ایجاد تزئین کننده املاک آشنا می شوید.

ایجاد Property Decorators

ویژگی های کلاس مکان دیگری است که می توانید از دکوراتور استفاده کنید. در این بخش نحوه ایجاد آنها را بررسی می کنید.

هر دکوراتور املاک پارامترهای زیر را دریافت می کند:

  • برای خواص استاتیک ، تابع سازنده کلاس. برای سایر ویژگی ها ، نمونه اولیه کلاس.
  • نام عضو.

در حال حاضر ، هیچ راهی برای به دست آوردن توصیف ویژگی به عنوان پارامتر وجود ندارد. این به دلیل روشی است که طراحان ویژگی در TypeScript راه اندازی می شوند.

در اینجا یک عملکرد تزئین کننده وجود دارد که نام اعضا را روی کنسول چاپ می کند:

const printMemberName = (target: any, memberName: string) => {
console.log(memberName);
};

class Person {
@printMemberName
name: string = “Jon”;
}

وقتی کد TypeScript بالا را اجرا می کنید ، موارد زیر را در کنسول چاپ می کنید:

 

Output

name

می توانید از طراحان املاک برای نادیده گرفتن ملک مورد تزئین استفاده کنید. این را می توان با استفاده از Object.defineProperty همراه با یک setter و getter جدید برای ویژگی انجام داد. بیایید ببینیم چگونه می توانید یک تزئین کننده با نام فهرست مجاز ایجاد کنید ، که فقط اجازه می دهد یک ویژگی بر روی مقادیر موجود در یک لیست مجاز استاتیک تنظیم شود:

const allowlist = ["Jon", "Jane"];

const allowlistOnly = (target: any, memberName: string) => {
  let currentValue: any = target[memberName];

  Object.defineProperty(target, memberName, {
    set: (newValue: any) => {
      if (!allowlist.includes(newValue)) {
        return;
      }
      currentValue = newValue;
    },
    get: () => currentValue
  });
};

ابتدا ، شما یک لیست مجاز استاتیک در بالای کد ایجاد می کنید:

const allowlist = [“Jon”, “Jane”];

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

const allowlistOnly = (target: any, memberName: string) => {
  let currentValue: any = target[memberName];

  Object.defineProperty(target, memberName, {
    set: (newValue: any) => {
      if (!allowlist.includes(newValue)) {
        return;
      }
      currentValue = newValue;
    },
    get: () => currentValue
  });
};

توجه داشته باشید که چگونه از هر کدام به عنوان نوع هدف استفاده می کنید:

const allowlistOnly = (target: any, memberName: string) => {

برای طراحان ویژگی ، نوع پارامتر هدف می تواند سازنده کلاس یا نمونه اولیه کلاس باشد ، در این شرایط استفاده از آن آسان تر است.

در خط اول پیاده سازی تزئین کننده خود ، مقدار فعلی دارایی را که تزئین می کنید در متغیر currentValue ذخیره می کنید:

let currentValue: any = target[memberName];

برای خواص استاتیک ، در صورت وجود ، مقدار پیش فرض آنها تنظیم می شود. برای خواص غیر استاتیک ، این همیشه تعریف نشده است. این به این دلیل است که در زمان اجرا ، در کد جاوا اسکریپت کامپایل شده ، تزئین کننده قبل از اینکه ویژگی نمونه روی مقدار پیش فرض تنظیم شود ، اجرا می شود.

سپس با استفاده از Object.defineProperty ویژگی را لغو می کنید:

Object.defineProperty(target, memberName, {
  set: (newValue: any) => {
    if (!allowlist.includes(newValue)) {
      return;
    }
    currentValue = newValue;
  },
  get: () => currentValue
});

فراخوانی Object.defineProperty دارای getter و setter است. دریافت کننده مقدار ذخیره شده در متغیر currentValue را برمی گرداند. اگر تنظیم کننده مقدار currentVariable را روی newValue تنظیم کند ، اگر در لیست مجاز باشد.

بیایید از دکوراتوری که شما نوشتید استفاده کنیم. کلاس Person زیر را ایجاد کنید:

class Person {
  @allowlistOnly
  name: string = "Jon";
}

اکنون یک نمونه جدید از کلاس خود ایجاد می کنید ، و تنظیمات آزمایشی و بدست آوردن ویژگی نمونه نام:

const allowlist = ["Jon", "Jane"];

const allowlistOnly = (target: any, memberName: string) => {
  let currentValue: any = target[memberName];

  Object.defineProperty(target, memberName, {
    set: (newValue: any) => {
      if (!allowlist.includes(newValue)) {
        return;
      }
      currentValue = newValue;
    },
    get: () => currentValue
  });
};

class Person {
  @allowlistOnly
  name: string = "Jon";
}

const person = new Person();
console.log(person.name);

person.name = "Peter";
console.log(person.name);

person.name = "Jane";
console.log(person.name);

 

هنگام اجرای کد باید خروجی زیر را مشاهده کنید:

 

Output

Jon
Jon
Jane

مقدار هرگز روی Peter تنظیم نمی شود ، زیرا Peter در لیست مجاز نیست.

اگر می خواهید کد خود را کمی بیشتر استفاده کنید و اجازه دهید هنگام استفاده از دکوراتور ، لیست مجاز تنظیم شود ، چه می کنید؟ این یک مورد استفاده عالی برای decorator factories. است. بیایید دقیقاً همین کار را انجام دهیم ، با تبدیل دکوراسیون مجاز خود فقط دکوراتور به decorator factories.:

const allowlistOnly = (allowlist: string[]) => {
  return (target: any, memberName: string) => {
    let currentValue: any = target[memberName];

    Object.defineProperty(target, memberName, {
      set: (newValue: any) => {
        if (!allowlist.includes(newValue)) {
          return;
        }
        currentValue = newValue;
      },
      get: () => currentValue
    });
  };
}

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

اکنون برای استفاده از دکوراتور خود ، باید از فهرست مجاز مانند کد برجسته زیر عبور کنید:

class Person {
  @allowlistOnly(["Claire", "Oliver"])
  name: string = "Claire";
}

سعی کنید کدی شبیه کد قبلی که نوشتید اجرا کنید ، اما با تغییرات جدید:

const allowlistOnly = (allowlist: string[]) => {
  return (target: any, memberName: string) => {
    let currentValue: any = target[memberName];

    Object.defineProperty(target, memberName, {
      set: (newValue: any) => {
        if (!allowlist.includes(newValue)) {
          return;
        }
        currentValue = newValue;
      },
      get: () => currentValue
    });
  };
}

class Person {
  @allowlistOnly(["Claire", "Oliver"])
  name: string = "Claire";
}

const person = new Person();
console.log(person.name);
person.name = "Peter";
console.log(person.name);
person.name = "Oliver";
console.log(person.name);

کد باید خروجی زیر را به شما بدهد:

 

Output

Claire
Claire
Oliver

نشان می دهد که آنطور که انتظار می رود کار می کند ، person.name هرگز روی Peter تنظیم نمی شود ، زیرا Peter در فهرست مجاز داده شده نیست.

اکنون که اولین تزئین کننده دارایی خود را با استفاده از عملکرد معمول تزئین کننده و کارخانه تزئینات ایجاد کرده اید ، وقت آن است که به نحوه ایجاد تزئینات برای دستیاران کلاس نگاهی بیندازید.

ایجاد Accessor Decorators

در این قسمت نگاهی به چگونگی تزئین اعضای کلاس می اندازید.

درست مانند دکوراتورهای املاک ، دکوراتورهای مورد استفاده در یک accessor پارامترهای زیر را دریافت می کنند:

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

با توجه به این واقعیت که ویژگی های توصیف کننده شامل هر دو تنظیم کننده و گیرنده برای یک عضو خاص است ، تزئین کننده های جانبی فقط می توانند برای تنظیم کننده یا گیرنده یک عضو واحد اعمال شوند ، نه برای هر دو.

اگر یک مقدار از تزئین کننده accessor خود را برگردانید ، این مقدار به عنوان توصیف کننده ویژگی جدید accessor برای هر دو گروه getter و setter تبدیل می شود.

در اینجا نمونه ای از یک تزئین کننده است که می تواند برای تغییر پرچم قابل شمارش دسترسی گیرنده/تنظیم کننده استفاده شود:

const enumerable = (value: boolean) => {
  return (target: any, memberName: string, propertyDescriptor: PropertyDescriptor) => {
    propertyDescriptor.enumerable = value;
  }
}

در مثال به نحوه استفاده از کارخانه تزئینات توجه کنید. این به شما این امکان را می دهد که هنگام تماس با دکوراتور پرچم شمارش شده را مشخص کنید. در اینجا نحوه استفاده از دکوراتور خود آورده شده است:

class Person {
  firstName: string = "Jon"
  lastName: string = "Doe"

  @enumerable(true)
  get fullName () {
    return `${this.firstName} ${this.lastName}`;
  }
}

دکوراتورهای اکسسوری شبیه به دکوراتورهای املاک هستند. تنها تفاوت این است که آنها پارامتر سوم را با توصیف کننده ویژگی دریافت می کنند. اکنون که اولین تزئین کننده اکسسوری خود را ایجاد کرده اید ، بخش بعدی نحوه ایجاد تزئین کننده متد را به شما نشان می دهد.

ایجاد Method Decorators

در این قسمت نگاهی به نحوه استفاده از تزئین کننده های متد می اندازید.

اجرای روش های تزئینات بسیار شبیه به نحوه ایجاد تزئینات اکسسوری است. پارامترهای ارسال شده به اجرای تزئینی مشابه پارامترهایی است که به تزئین کننده های اکسسوری ارسال شده است.

بیایید از همان دکوراتور قابل شمارش که قبلاً ایجاد کرده اید دوباره استفاده کنیم ، اما این بار در متد getFullName کلاس Person زیر:

const enumerable = (value: boolean) => {
  return (target: any, memberName: string, propertyDescriptor: PropertyDescriptor) => {
    propertyDescriptor.enumerable = value;
  }
}

class Person {
  firstName: string = "Jon"
  lastName: string = "Doe"

  @enumerable(true)
  getFullName () {
    return `${this.firstName} ${this.lastName}`;
  }
}

اگر مقداری را از تزئین کننده متد خود برگردانده اید ، این مقدار به توصیف کننده جدید ویژگی متد تبدیل می شود.

بیایید یک دکوراتور منسوخ ایجاد کنیم که وقتی از متد استفاده می شود پیام ارسال شده را روی کنسول چاپ می کند و پیامی را ثبت می کند که می گوید این روش منسوخ شده است:

const deprecated = (deprecationReason: string) => {
  return (target: any, memberName: string, propertyDescriptor: PropertyDescriptor) => {
    return {
      get() {
        const wrapperFn = (...args: any[]) => {
          console.warn(`Method ${memberName} is deprecated with reason: ${deprecationReason}`);
          propertyDescriptor.value.apply(this, args)
        }

        Object.defineProperty(this, memberName, {
            value: wrapperFn,
            configurable: true,
            writable: true
        });
        return wrapperFn;
      }
    }
  }
}

 

در اینجا ، شما در حال ایجاد یک تزئین کننده با استفاده از یک کارخانه تزئینات هستید. این کارخانه تزئینی یک آرگومان واحد از نوع string دریافت می کند ، که دلیل عدم استفاده از آن است ، همانطور که در قسمت برجسته زیر نشان داده شده است:

const deprecated = (deprecationReason: string) => {
  return (target: any, memberName: string, propertyDescriptor: PropertyDescriptor) => {
    // ...
  }
}

deprecationReason بعداً هنگام ورود پیام منسوخ شدن به کنسول استفاده می شود. در اجرای تزئین منسوخ شده خود ، یک مقدار را برمی گردانید. هنگامی که مقداری را از یک تزئین کننده متد برمی گردانید ، این مقدار روی توصیف کننده ویژگی این عضو بازنویسی می کند.

شما از این مزیت استفاده می کنید تا متد را به روش کلاس تزئین شده خود اضافه کنید. به این ترتیب می توانید پیاده سازی خود روش را تغییر دهید.

اما چرا فقط از Object.defineProperty به جای بازگشت یک تزئین کننده جدید برای روش استفاده نکنید؟ این امر ضروری است زیرا باید به مقدار این دسترسی داشته باشید ، که برای روشهای کلاس غیر استاتیک ، به نمونه کلاس متصل است. اگر مستقیماً از Object.defineProperty استفاده می کنید ، هیچ راهی برای بازیابی مقدار آن برای شما وجود نخواهد داشت ، و اگر متدی که به هر نحوی از این روش استفاده می کرد ، تزئین کننده هنگام اجرای روش پیچیده شده از داخل اجرای تزئین کننده ، کد شما را می شکند.

در مورد شما ، خود گیرنده این مقدار را برای نمونه های غیراستاتیک به نمونه کلاس و برای متدهای استاتیک به سازنده کلاس محدود می کند.

در داخل getter شما سپس یک تابع wrapper به صورت محلی ایجاد می کنید ، به نام wrapperFn ، این تابع با استفاده از console یک پیام را به کنسول وارد می کند. هشدار دهید ، با رد deprecationReason دریافت شده از کارخانه decorator ، سپس با استفاده از propertyDescriptor.value روش اصلی را فراخوانی می کنید. apply (this، args) ، به این ترتیب متد اصلی نامیده می شود که این مقدار به درستی به نمونه کلاس متصل شده است در صورتی که یک روش غیر استاتیک بود.

سپس از definProperty برای بازنویسی مقدار متد خود در کلاس استفاده می کنید. این مانند یک مکانیسم یادآوری عمل می کند ، زیرا تماس های متعدد با یک روش دیگر با گیرنده شما تماس نمی گیرد ، بلکه مستقیماً با wrapperFn تماس می گیرد. اکنون شما با استفاده از Object.defineProperty ، عضو کلاس را طوری تنظیم می کنید که wrapperFn شما به عنوان مقدار آن باشد.

بیایید از دکوراتور منسوخ شده شما استفاده کنیم:

const deprecated = (deprecationReason: string) => {
  return (target: any, memberName: string, propertyDescriptor: PropertyDescriptor) => {
    return {
      get() {
        const wrapperFn = (...args: any[]) => {
          console.warn(`Method ${memberName} is deprecated with reason: ${deprecationReason}`);
          propertyDescriptor.value.apply(this, args)
        }

        Object.defineProperty(this, memberName, {
            value: wrapperFn,
            configurable: true,
            writable: true
        });
        return wrapperFn;
      }
    }
  }
}

class TestClass {
  static staticMember = true;

  instanceMember: string = "hello"

  @deprecated("Use another static method")
  static deprecatedMethodStatic() {
    console.log('inside deprecated static method - staticMember =', this.staticMember);
  }

  @deprecated("Use another instance method")
  deprecatedMethod () {
    console.log('inside deprecated instance method - instanceMember =', this.instanceMember);
  }
}

TestClass.deprecatedMethodStatic();

const instance = new TestClass();
instance.deprecatedMethod();

در اینجا ، شما یک TestClass با دو ویژگی ایجاد کرده اید: یکی استاتیک و دیگری غیر استاتیک. شما همچنین دو روش ایجاد کرده اید: یکی استاتیک و دیگری غیر استاتیک.

سپس از تزئین کننده منسوخ خود برای هر دو روش استفاده می کنید. هنگام اجرای کد ، موارد زیر در کنسول ظاهر می شود:

 

Output

(warning) Method deprecatedMethodStatic is deprecated with reason: Use another static method
inside deprecated static method - staticMember = true
(warning)) Method deprecatedMethod is deprecated with reason: Use another instance method
inside deprecated instance method - instanceMember = hello

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

اکنون شما اولین تزئین کننده متد خود را با استفاده از TypeScript ایجاد کرده اید. بخش بعدی نحوه ایجاد آخرین نوع تزئین کننده پشتیبانی شده توسط TypeScript ، یک تزئین کننده پارامتر را به شما نشان می دهد.

ایجاد پارامترهای Decorators

تزئینات پارامتری را می توان در پارامترهای روش کلاس استفاده کرد. در این بخش نحوه ایجاد آن را خواهید آموخت.

عملکرد تزئین کننده مورد استفاده با پارامترها پارامترهای زیر را دریافت می کند:

برای خواص استاتیک ، تابع سازنده کلاس. برای سایر خصوصیات ، نمونه اولیه کلاس.
نام عضو.
فهرست پارامترها در لیست پارامترهای متد.
تغییر هر چیزی که مربوط به خود پارامتر باشد امکان پذیر نیست ، بنابراین چنین تزئیناتی فقط برای مشاهده استفاده از خود پارامترها مفید هستند (مگر اینکه از موارد پیشرفته تری مانند بازتاب داده های بازتاب استفاده کنید).

در اینجا یک نمونه از تزئین کننده است که شاخص پارامتر تزئین شده را به همراه نام روش چاپ می کند:

function print(target: Object, propertyKey: string, parameterIndex: number) {
  console.log(`Decorating param ${parameterIndex} from ${propertyKey}`);
}

سپس می توانید پارامتر decorators خود را مانند این استفاده کنید:

class TestClass {
  testMethod(param0: any, @print param1: any) {}
}

اجرای کد بالا باید موارد زیر را در کنسول نمایش دهد:

 

Output

Decorating param 1 from testMethod

شما اکنون یک  پارامتر decorator ایجاد کرده و اجرا کرده اید و نتیجه ای را که شاخص پارامتر decorator را برمی گرداند چاپ می کنید.

نتیجه
در این آموزش ، شما تمام decorators پشتیبانی شده توسط TypeScript را پیاده سازی کرده اید ، از آنها در کلاس ها استفاده کرده اید و تفاوت های هر یک را یاد گرفته اید. اکنون می توانید برای کاهش کد صفحه بویلر در پایگاه کد خود ، نوشتن دکوراتورهای خود را شروع کنید ، یا با اطمینان بیشتر از دکوراتورهای کتابخانه هایی مانند Mobx استفاده کنید.

decoratorMethod DecoratorsProperty DecoratorsTypeScriptپارامترهای Decoratorsجاواجاوا اسکریپتسرورسرور مجازیکامپایلر TypeScript

  • behnam gol mohamadi