چهارشنبه, 22 آذر 1396

آموزش تایپ اسکریپت

همه چیز در باره تایپ اسکریپت

اینترفیس ها در تایپ اسکریپت (1#)

اینترفیس  ها (Interfaces)

 

معرفی

یکی از اصول تایپ اسکریپت، کنترل نوع است، که ساختار مقادیر را بررسی می کند تا به همان صورت که باید باشند، قرار بگیرند. این ویژگی گاهی اوقات duck typing و یا structural subtyping نامیده می شود. اینترفیس در تایپ اسکریپت نقش نامگذاری انواع را برعهده دارد و روش قدرتمندی برای تعیین قراردادها (محدودیت ها) در طول پروژه هست.

آموزش اولین اینترفیس شما

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

function printLabel(labelledObj: { label: string }) {
    console.log(labelledObj.label);
}

let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);

type-checker نحوه فراخوانی تابع printLabel را چک می کند. تابع printLabel یک شی از نوع  string  به عنوان ورودی دارد.توجه کنید که شی مورد نیاز دارای خصوصیاتی بیشتر این است اما کامپایلر تنها حداقل های مورد نیاز برای تطبیق ورودی را بررسی می کند. اما در برخی موارد تایپ اسکریپت به این اندازه آسان گیر نیست.

interface LabelledValue {
    label: string;
}

function printLabel(labelledObj: LabelledValue) {
    console.log(labelledObj.label);
}

let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);

 

از اینتر فیس LabelledValue  می توان در مثال قبل استفاده کرد تا نیازهای ورودی تابع را توصیف کنیم. این مثال نشان می دهد که شی ورودی تنها یک صفت دارد که lable نامیده می شود و از نوع string  است. توجه کنید  ما مجبور نیستیم که شی که به تابع  printLabel پاس می دهیم صراحتا از interface پیروی  کند( شبیه  زبان های دیگر ) تنها در اینجا موضوع قالب شی است. وقتی که پارامتری به تابع ارسال می شود، اگر نیاز های لیست شده را داشته باشد، پذیرفته می شود.شایان ذکر است که در type-checker  لازم به ترتیب چیدمان صفات اشیا در interface نیست تنها بیان صفات مربوط به شی را ارائه می دهد.

ویژگی های انتخابی Optional Properties

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

 در اینجا مثالی از این الگو را می بینید:

 

interface SquareConfig {
    color?: string;
    width?: number;
}

function createSquare(config: SquareConfig): {color: string; area: number} {
    let newSquare = {color: "white", area: 100};
    if (config.color) {
        newSquare.color = config.color;
    }
    if (config.width) {
        newSquare.area = config.width * config.width;
    }
    return newSquare;
}

let mySquare = createSquare({color: "black"});

 اینترفیس های با صفات اختیاری (optional properties) همانند دیگر ویژگی ها نوشته می شود.در هنگام تعریف در انتهای نام صفت انتخابی علامت ؟ گذاشته می شود.مزیت استفاده از صفات اختیاری این است که شما می توانید مانع استفاده از صفاتی که در interface بیان نشده است بشوید. به عنوان مثال در صورتی که به اشتباه صفت color  را در createSquare تایپ کنیم با پیغام خطای دریافت به صورت زیر  مواجه می شویم.

interface SquareConfig {
    color?: string;
    width?: number;
}

function createSquare(config: SquareConfig): { color: string; area: number } {
    let newSquare = {color: "white", area: 100};
    if (config.color) {
        // Error: Property 'clor' does not exist on type 'SquareConfig'
        newSquare.color = config.clor;
    }
    if (config.width) {
        newSquare.area = config.width * config.width;
    }
    return newSquare;
}

let mySquare = createSquare({color: "black"});

صفات فقط خواندنی (Readonly properties) 

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

interface Point {
    readonly x: number;
    readonly y: number;
}

شما می توانید بعد از عنوان شی با قراردادن یک نقطه به صفت مورد نظر را مقدار دهی کنید بعد از مقدار دهی دیگر قادر به تغییر آن نیستید.

let p1: Point = { x: 10, y: 20 };
p1.x = 5; // error!

شما در تایپ اسکریپت می توانید نوع آرایه فقط خواندنی <ReadonlyArray<T   همانند تعریف آرایه <Array<T  تعریف کنید که در این آرایه تمامی روش های کار با آرایه حذف شده است بنابراین شما می توانید مطمئن باشید که این آرایه بعد از ایجاد هیچ گاه تغییر نخواهد کرد.

let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
ro[0] = 12; // error!
ro.push(5); // error!
ro.length = 100; // error!
a = ro; // error!

در خط آخر از قطعه شما می توانید ببینید که حتی اختصاص کل ReadonlyArray به یک آرایه طبیعی غیر قانونی است. شما هنوز هم می توانید آن را  با تعریف نوع زیر پا بگذارند:

a = ro as number[];

 readonly یا  const

 ساده ترین راه برای بیاد داشتن اینکه آیا از const  و یا readonly استفاده کنیم ، این است که از خودمان بپرسیم آیا از متغیر ها استفاده می کنیم و یا از صفات(property). برای متغیر ها از const   و برای صفات از readonly  استفاده می کنیم.

 Excess Property Checks (چک صفات مازاد)

در مثال اول که از interface استفاده کردیم، تایپ اسکریپت به ما اجازه می دهد که شی ای  از  { size: number; label: string; } به متغییری که تنها  { label: string; } می پذیرد پاس دهیم. هم چنین ما مطالبی در باره optional properties یاد گرفتیم و اینکه آنها چقدر مفیدند برای مواقعی که بخواهیم متغیر های به اصطلاح option bags داشته باشیم.

 Excess Property Checks (چک کردن صفات مازاد)

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

interface SquareConfig {
    color?: string;
    width?: number;
}

function createSquare(config: SquareConfig): { color: string; area: number } {
    // ...
}

let mySquare = createSquare({ colour: "red", width: 100 });

 آرگومان داده شده به createSquare به جای color به صورت colour  نوشته شده است . این چنین خطاها در جاوااسکریپت در سکوت faild می شود (با شکست روبرو می شود).ممکن است شما چنین استدلال کنید که تا زمانی که صفت width سازگار باشد و هیچ مقداری برای صفت color وجود نداشته باشد، این برنامه درست تایپ شده است.با این حال تایپ اسکریپت، این احتمال را در نظر می گیرد که ممکن است خطایی رخ در کد برنامه وجود داشته باشد.

اشیاء لفظی (Object literals) رفتار خاصی دارند، زمان ارسال آنها به عنوان آرگومان به تابع و  یا موقع اختصاص دادن آن ها به دیگر متغیر ها از لحاظ داشتن ویژگی های اضافه مورد بررسی قرار می گیرند.اگر هر شی لفظی (Object literal) صفت اضافه ای نسبت به نوع (متغییری که باید به آن اختصاص داده شود و یا آرگومان ورودی تابع) داشته باشد، با خطا مواجه می شوید.

// error: 'colour' not expected in type 'SquareConfig'
let mySquare = createSquare({ colour: "red", width: 100 });

 بررسی این خطاها واقعا ساده است، ساده ترین روش استفاده از تعریف نوع (type assertion) است.

let mySquare = createSquare({ width: 100, opacity: 0.5 } as SquareConfig);

  با این حال  برای مواقعی که شما مطمئن هستید شی مورد نظر در شرایط خاص صفات بیشتری از آنچه استفاده می شود، دارد بهترین رویکرد این است که از شاخص index استفاده کنید.اگر SquareConfigs دو  صفت color و width مثل نوع بالا داشته باشداگر به صورت زیر تعریف شود می تواند هر تعداد از صفات را نیز داشته باشد.

interface SquareConfig {
    color?: string;
    width?: number;
    [propName: string]: any;
}

 ما  شاخص index را  مطرح می کنیم، اما اینحا ما در باره SquareConfig صحبت می کنیم که  هر تعداد از صفات را تازمانی که از color  یا width نباشند، داشته باشد و اصلا نوع آنها مهم نیست.راه حل نهایی برای این چنین بررسی ها، که ممکن است کمی تعجب انگیز باشد این است که شی را به متغیر دیگر مقداردهی (assign )کنیم. از آنجا که SquareConfig نمی خواهد بررسی صفات را انجام دهد، کامپایل نیز خطا نمی دهد. 

let squareOptions = { colour: "red", width: 100 };
let mySquare = createSquare(squareOptions);

 به خاطر داشته باشید که برای کد ساده ای مثل بالا، شما احتمالا نباید این چک ها را بررسی کنید. برای اشیا لفظی پیچیده که حالت را در خود نگه می دارند و متد دارند، احتیاج دارید این شیوه را در ذهن داشته باشد، اما اکثر خطاهای صفات اضافه واقعا باگ هستند. این بدین معناست که زمانی که شما در حال اجرا به مشکلات بررسی صفات اضافه برای چیزهایی مثل option bags برخورد می کنید، باید در تعریف نوع تجدید نظر کنید. در این مثال، اگر پاس دادن شی ای با دو   صفت color  و colour به createSquare درست باشد، باید تعریف SquareConfig  را به آنچه که بر می گرداند، اصلاح کنید.