بررسی type casting در تایپ اسکریپت

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

در این مقاله قصد داریم تا نکات مربوط به ویژگی type casting در تایپ اسکریپت را باهم بررسی کنیم. برای درک بهتر مقاله، بهتر است با زبان برنامه نویسی تایپ اسکریپت و برنامه نویسی شی‌گرا آشنا باشیم.

Type casting در تایپ اسکریپت چیست؟

Type casting یک ویژگی در تایپ اسکریپت است که به توسعه‌دهندگان اجازه می‌دهد به صراحت تایپ یک مقدار را از یک نوع به نوع دیگر تغییر دهند. استفاده از ویژگی type casting مخصوصاً زمانی که با داده‌های داینامیک کار می‌کنیم یا زمانی که تایپ یک مقدار به‌طور خودکار به درستی استنتاج نمی‌شود، بسیار مفید می‌باشد.

Type casting می‌تواند به یکی از دو روش انجام شود: روش اول این که می‌تواند ضمنی باشد، یعنی زمانی که تایپ اسکریپت عملیات را مدیریت می‌کند، روش دوم هم این که صریح باشد، یعنی زمانی که توسعه‌دهنده conversion را مدیریت می‌کند. casting ضمنی زمانی اتفاق می‌افتد که تایپ اسکریپت یک خطای تایپ را می‌بیند و سعی می‌کند با آن را تصحیح کند.

ویژگی type casting برای انجام عملیات‌های مختلف، از جمله محاسبات ریاضی، داده‌ها، دستکاری‌ها و بررسی‌های سازگاری ضروری است. اما قبل از اینکه بتوانیم به طور موثر از آن استفاده کنیم، باید با برخی از مفاهیم اساسی مانند روابط subtype و supertype، مفهوم type widening و همینطور مفهوم type narrowing آشنا شویم.

مقایسه Type assertion و Type casting

در حالی که این دو اصطلاح اغلب در بین توسعه‌دهندگان به جای یکدیگر استفاده می‌شوند، اما تفاوت ظریفی بین type assertion و type casting در تایپ اسکریپت وجود دارد:

  • Type assertion: این راهی است که ما می‌توانیم به کامپایلر تایپ اسکریپت بگوییم که یک موجودیت را به عنوان یک تایپ متفاوت از آنچه استنباط می‌شود، در نظر بگیرد. در واقع تایپ اصلی داده را تغییر نمی‌دهد، فقط به تایپ اسکریپت دستور می‌دهد که آن را به عنوان تایپ تعیین شده در نظر بگیرد. Type assertion از کلمه کلیدی
    as
    as استفاده می‌کند.
  • Type casting/conversion: این ویژگی شامل تبدیل داده‌ها از یک تایپ به تایپ دیگر است که تایپ اصلی داده را تغییر می‌دهد. می‌توانیم type casting را با استفاده از متدهای داخلی مانند
    String()
    String()،
    Number()
    Number()،
    Boolean()
    Boolean() و غیره انجام دهیم.

تفاوت کلیدی این است که type assertion صرفاً یک ساختار مربوط به زمان کامپایل است و به تایپ اسکریپت می‌گوید که یک مقدار را به عنوان یک تایپ خاص بدون تأثیر بر رفتار زمان اجرا آن در نظر بگیرد. از طرف دیگر، type casting در واقع داده‌ها را تغییر می‌دهد و می‌تواند بر رفتار زمان اجرا تأثیر بگذارد.

Subtypeها و Supertypeها

یکی از راه‌های طبقه‌بندی تایپ‌ها، تقسیم آن‌ها به -sub و supertypeها است. به طور کلی، یک subtype یک نسخه تخصصی از یک supertype است که ویژگی‌ها و رفتارهای آن را به ارث می‌برد. از طرف دیگر، supertype تایپ کلی‌تری است که اساس subtypeهای متعدد می‌باشد.

سناریویی را در نظر می‌گیریم که در آن ما یک سلسله مراتب کلاس با یک superclass به نام

Animal
Animal و دو subclass به نام‌های
Cat
Cat و
Dog
Dog داریم. در این سناریو،
Animal
Animal یک supertype است، در حالی که
Cat
Cat و
Dog
Dog هر دو subtype هستند. زمانی که ما نیاز داریم یک آبجکت از یک subtype خاص را به عنوان supertype آن در نظر بگیریم یا برعکس، جایی است که باید از type casting استفاده کنیم.

Type widening: از subtype به supertype

Type widening یا upcasting زمانی اتفاق می‌افتد که ما نیاز داریم یک متغیر را از یک subtype به یک supertype تبدیل کنیم. ویژگی type widening معمولاً ضمنی است، به این معنی که توسط تایپ اسکریپت انجام می‌شود، زیرا شامل حرکت از یک دسته محدود به یک دسته گسترده‌تر است. این ویژگی safe می‌باشد و هیچ خطایی ایجاد نمی‌کند، زیرا یک subtype ذاتاً دارای تمام ویژگی‌ها و رفتارهای supertype خود است.

Type narrowing: از supertype به subtype

زمانی که متغیری را از supertype به subtype تبدیل می‌کنیم، type narrowing یا downcasting رخ می‌دهد. تبدیل type narrowing صریح است و برای اطمینان از اعتبار تبدیل نیاز به تأیید تایپ یا بررسی تایپ دارد. این فرآیند می‌تواند باعث ایجاد مشکلاتی در برنامه شود، زیرا همه متغیرهای supertype دارای مقادیری نیستند که با subtype سازگار باشد.

Type casting در تایپ اسکریپت با عملگر as

عملگر

as
as مکانیزم اصلی تایپ اسکریپت برای type casting صریح است.
as
as با سینتکس بصری خود، این امکان را ما می‌دهد تا کامپایلر را در مورد تایپ مورد نظر یک متغیر یا عبارت مطلع کنیم. شکل کلی عملگر
as
as را در مثال زیر مشاهده می‌کنیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
value as Type
value as Type
value as Type

در این مثال

value
value نشان دهنده متغیر یا عبارتی است که می‌توانیم آن را cast کنیم، در حالی که
Type
Type نشان دهنده تایپ هدف مورد نظر ما می‌باشد. با استفاده از
as
as، ما به صراحت اعلام می‌کنیم که
value
value از تایپ
Type
Type است.

عملگر

as
as زمانی مفید است که با تایپ‌هایی کار می‌کنیم که اجداد مشترکی دارند، از جمله سلسله مراتب کلاس یا پیاده‌سازی interface. این عملگر به ما این امکان را می‌دهد تا نشان دهیم که یک متغیر خاص باید به عنوان یک subtype خاص‌تر در نظر گرفته شود. به عنوان مثال:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class Animal {
eat(): void {
console.log('Eating...');
}
}
class Dog extends Animal {
bark(): void {
console.log('Woof!');
}
}
const animal: Animal = new Dog();
const dog = animal as Dog;
dog.bark(); // Output: "Woof!"
class Animal { eat(): void { console.log('Eating...'); } } class Dog extends Animal { bark(): void { console.log('Woof!'); } } const animal: Animal = new Dog(); const dog = animal as Dog; dog.bark(); // Output: "Woof!"
class Animal {
  eat(): void {
    console.log('Eating...');
  }
}

class Dog extends Animal {
  bark(): void {
    console.log('Woof!');
  }
}

const animal: Animal = new Dog();
const dog = animal as Dog;
dog.bark(); // Output: "Woof!"

در مثالی که داریم، کلاس

Dog
Dog کلاس
Animal
Animal را extend می‌کند. نمونه
Dog
Dog به یک متغیر
animal
animal از نوع
Animal
Animal اختصاص داده می‌شود. با استفاده از عملگر
as
as، می‌توانیم
animal
animal را به عنوان
Dog
Dog انتخاب کنیم. بنابراین، این امکان را داریم تا به متد
bark()
bark() که مخصوص کلاس
Dog
Dog است دسترسی داشته باشیم.

می‌توانیم از عملگر

as
as برای cast به تایپ‌های خاصی استفاده کنیم. این قابلیت زمانی مفید است که ما نیاز به تعامل با تایپی داریم که با تایپ استنتاج شده توسط سیستم استنتاج تایپ تایپ اسکریپت متفاوت است. به عنوان مثال:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function getLength(obj: any): number {
if (typeof obj === 'string') {
return (obj as string).length;
} else if (Array.isArray(obj)) {
return (obj as any[]).length;
}
return 0;
}
function getLength(obj: any): number { if (typeof obj === 'string') { return (obj as string).length; } else if (Array.isArray(obj)) { return (obj as any[]).length; } return 0; }
function getLength(obj: any): number {
  if (typeof obj === 'string') {
    return (obj as string).length;
  } else if (Array.isArray(obj)) {
    return (obj as any[]).length;
  }
  return 0;
}

تابع

getLength
getLength یک پارامتر
obj
obj از تایپ
any
any
را می‌پذیرد. در تابع
getLength
getLength، عملگر
as
as، پارامتر
obj
obj را به یک رشته برای
any[]
any[] بر اساس نوع آن cast می‌کند. این عملیات این امکان را برای ما فراهم می‌کند تا به ترتیب به ویژگی
length
length که مخصوص رشته‌ها یا آرایه‌ها است، دسترسی داشته باشیم. علاوه بر این، برای این که بیان کنیم یک مقدار می‌تواند یکی از چندین تایپ باشد، می‌توانیم به یک تایپ Union کست کنیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function processValue(value: string | number): void {
if (typeof value === 'string') {
console.log((value as string).toUpperCase());
} else {
console.log((value as number).toFixed(2));
}
}
function processValue(value: string | number): void { if (typeof value === 'string') { console.log((value as string).toUpperCase()); } else { console.log((value as number).toFixed(2)); } }
function processValue(value: string | number): void {
  if (typeof value === 'string') {
    console.log((value as string).toUpperCase());
  } else {
    console.log((value as number).toFixed(2));
  }
}

تابع

processValue
processValue یک پارامتر
value
value از نوع
string | number
string | number را می‌پذیرد که نشان می‌دهد می‌تواند یک رشته یا یک عدد باشد. با استفاده از عملگر
as
as، می‌توانیم
value
value را به
string
string یا
number
number در شرایط مربوطه cast کنیم، و در نتیجه این امکان را داشته باشیم که عملیات‌های تایپ خاص مانند
toUpperCase()
toUpperCase() یا
toFixed()
toFixed() را اعمال نماییم.

محدودیت‌های عملگر as

در حالی که عملگر

as
as ابزار قدرتمندی برای type casting در تایپ اسکریپت است، اما محدودیت‌هایی هم دارد. یکی از محدودیت‌ها این است که
as
as صرفاً در زمان کامپایل عمل می‌کند و هیچ‌گونه بررسی زمان اجرا را انجام نمی‌دهد. این بدان معنی است که اگر تایپ cast شده نادرست باشد، ممکن است منجر به ایجاد خطاهای زمان اجرا شود. بنابراین، اطمینان از صحت تایپ cast شده بسیار مهم است.

یکی دیگر از محدودیت‌های عملگر

as
as این است که نمی‌توانیم از آن برای cast بین تایپ‌های نامرتبط استفاده کنیم. سیستم تایپ تایپ اسکریپت برای جلوگیری از casting ناامن، بررسی‌های دقیقی را ارائه می‌کند و از type safety در سراسر پایگاه کد ما اطمینان می‌دهد. در چنین مواردی، می‌توانیم رویکردهای جایگزین، مانند توابع تایید تایپ یا محافظ تایپ را در نظر بگیریم.

وقتی تایپ اسکریپت اجازه as casting را نمی‌دهد

مواردی وجود دارد که تایپ اسکریپت مخالفت‌هایی را مطرح می‌کند و از دادن مجوز برای

as
as casting امتناع می‌نماید. در ادامه برخی از موقعیت‌هایی که ممکن است باعث ایجاد این اتفاق شود را بررسی می‌کنیم.

ناسازگاری ساختاری با تایپ‌های سفارشی

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

as
as ارسال کنیم، کامپایلر سازگاری ساختاری بین تایپ اصلی و تایپ مورد نظر را ارزیابی می‌کند. اگر ویژگی‌های ساختاری دو تایپ سفارشی ناسازگار باشد، تایپ اسکریپت خطایی ایجاد می‌کند که نشان‌دهنده ناامن بودن عملیات casting است. در ادامه مثالی داریم که نمونه‌ای از type casting با خطاهای ناسازگاری ساختاری با استفاده از تایپ‌های سفارشی را نشان می‌دهد:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
interface Square {
sideLength: number;
}
interface Rectangle {
width: number;
height: number;
}
const square: Square = { sideLength: 5 };
const rectangle = square as Rectangle; // Error: Incompatible types
interface Square { sideLength: number; } interface Rectangle { width: number; height: number; } const square: Square = { sideLength: 5 }; const rectangle = square as Rectangle; // Error: Incompatible types
interface Square {
  sideLength: number;
}

interface Rectangle {
  width: number;
  height: number;
}

const square: Square = { sideLength: 5 };
const rectangle = square as Rectangle; // Error: Incompatible types

تایپ اسکریپت از عملیات

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

Type guardها با union typeها

Union typeها در تایپ اسکریپت این امکان را به ما می‌دهند تا مقداری را تعریف کنیم که می‌تواند یکی از چندین تایپ ممکن باشد. Type guardها نقش مهمی در محدود کردن تایپ خاصی از یک مقدار در یک بلاک شرطی ایفا می‌کنند و عملیات type-safe را امکان‌پذیر می‌سازند.

با این حال، هنگام تلاش برای cast کردن یک union type با عملگر

as
as، لازم است که تایپ مورد نظر، یکی از تایپ‌های تشکیل‌دهنده union باشد. اگر تایپ مورد نظر در union گنجانده نشده باشد، تایپ اسکریپت اجازه عملیات casting را نمی‌دهد:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
type Shape = Square | Rectangle;
function getArea(shape: Shape) {
if ('sideLength' in shape) {
// Type guard: 'sideLength' property exists, so shape is of type Square
return shape.sideLength ** 2;
} else {
// shape is of type Rectangle
return shape.width * shape.height;
}
}
const square: Shape = { sideLength: 5 };
const area = getArea(square); // Returns 25
type Shape = Square | Rectangle; function getArea(shape: Shape) { if ('sideLength' in shape) { // Type guard: 'sideLength' property exists, so shape is of type Square return shape.sideLength ** 2; } else { // shape is of type Rectangle return shape.width * shape.height; } } const square: Shape = { sideLength: 5 }; const area = getArea(square); // Returns 25
type Shape = Square | Rectangle;

function getArea(shape: Shape) {
  if ('sideLength' in shape) {
    // Type guard: 'sideLength' property exists, so shape is of type Square
    return shape.sideLength ** 2;
  } else {
    // shape is of type Rectangle
    return shape.width * shape.height;
  }
}

const square: Shape = { sideLength: 5 };
const area = getArea(square); // Returns 25

در قطعه کد بالا، یک

Shape
Shape از union type داریم که نشان‌دهنده
Square
Square یا
Rectangle
Rectangle است. تابع
getArea
getArea پارامتری از نوع
Shape
Shape می‌گیرد و باید مساحت را بر اساس شکل خاص محاسبه نماید.

برای تعیین تایپ

Shape
Shape داخل تابع
getArea
getArea از type guard استفاده می‌کنیم که وجود ویژگی
sideLength
sideLength را با استفاده از عملگر
in
in بررسی می‌کند. اگر ویژگی
sideLength
sideLength وجود داشته باشد، تایپ اسکریپت تایپ
shape
shape را در آن بلاک شرطی به
Square
Square محدود می‌کند و این امکان را به ما می‌دهد تا به ویژگی
sideLength
sideLength دسترسی پیدا کنیم.

محدودیت‌های Type assertion

Type assertionها، که با کلمه کلیدی

as
as نشان داده می‌شوند، یک فانکشنالیتی برای نادیده گرفتن تایپ استنتاج شده یا اعلام شده برای یک مقدار ارائه می‌دهند. با این حال، تایپ اسکریپت محدودیت‌های خاصی در type assertionها دارد. به طور خاص، تایپ اسکریپت هنگام محدود کردن یک تایپ از طریق تجزیه و تحلیل جریان کنترل،
as
as casting را ممنوع می‌کند:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function processShape(shape: Shape) {
if ("width" in shape) {
const rectangle = shape as Rectangle;
// Process rectangle
} else {
const square = shape as Square;
// Process square
}
}
function processShape(shape: Shape) { if ("width" in shape) { const rectangle = shape as Rectangle; // Process rectangle } else { const square = shape as Square; // Process square } }
function processShape(shape: Shape) {
  if ("width" in shape) {
    const rectangle = shape as Rectangle;
    // Process rectangle
  } else {
    const square = shape as Square;
    // Process square
  }
}

تایپ اسکریپت یک خطا ایجاد می‌کند، زیرا نمی‌تواند تایپ

shape
shape را بر اساس type assertionها محدود کند. برای غلبه بر این محدودیت، می‌توانیم یک متغیر جدید در هر شاخه از جریان کنترل معرفی کنیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function processShape(shape: Shape) {
if ("width" in shape) {
const
rectangle: Rectangle = shape;
// Process rectangle
} else {
const square: Square = shape;
// Process square
}
}
function processShape(shape: Shape) { if ("width" in shape) { const rectangle: Rectangle = shape; // Process rectangle } else { const square: Square = shape; // Process square } }
function processShape(shape: Shape) {
  if ("width" in shape) {
    const

 rectangle: Rectangle = shape;
    // Process rectangle
  } else {
    const square: Square = shape;
    // Process square
  }
}

تایپ اسکریپت می‌تواند با تخصیص مستقیم type assertion به یک متغیر جدید، تایپی که محدود شده است را به درستی استنتاج کند.

Discriminated unionها

یک discriminated union گونه‌ای است که نشان‌دهنده مقداری می‌باشد که می‌تواند چندین احتمال داشته باشد. discriminated unionها مجموعه‌ای از تایپ‌های مرتبط را تحت یک parent مشترک ترکیب می‌کنند، که در آن هر تایپ child به‌طور منحصربه‌فردی توسط یک ویژگی متمایز شناسایی می‌شود. این ویژگی متمایز به عنوان یک تایپ literal عمل می‌کند که به تایپ اسکریپت اجازه می‌دهد تا exhaustiveness checking را انجام دهد:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
type Circle = {
kind: 'circle';
radius: number;
};
type Square = {
kind: 'square';
sideLength: number;
};
type Triangle = {
kind: 'triangle';
base: number;
height: number;
};
type Shape = Circle | Square | Triangle;
type Circle = { kind: 'circle'; radius: number; }; type Square = { kind: 'square'; sideLength: number; }; type Triangle = { kind: 'triangle'; base: number; height: number; }; type Shape = Circle | Square | Triangle;
type Circle = {
  kind: 'circle';
  radius: number;
};

type Square = {
  kind: 'square';
  sideLength: number;
};

type Triangle = {
  kind: 'triangle';
  base: number;
  height: number;
};

type Shape = Circle | Square | Triangle;

ما سه تایپ shape را تعریف کرده‌ایم:

Circle
Circle،
Square
Square و
Triangle
Triangle که همه مجموع
Shape
Shape discriminated union را تشکیل می‌دهند. ویژگی
kind
kind متمایزکننده است و با مقدار literal تایپ shape را نشان می‌دهد.

Discriminated unionها زمانی قدرتمندتر می‌شوند که آن‌ها را با type guardها ترکیب کنیم. type guard یک بررسی زمان اجرا است که به تایپ اسکریپت اجازه می‌دهد تا تایپ‌های ممکن را در union بر اساس ویژگی discriminant محدود کند. تابع زیر را در نظر می‌گیریم که مساحت یک شکل را محاسبه می‌کند:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function calculateArea(shape: Shape): number {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2;
case 'square':
return shape.sideLength ** 2;
case 'triangle':
return (shape.base * shape.height) / 2;
default:
throw new Error('Invalid shape!');
}
}
function calculateArea(shape: Shape): number { switch (shape.kind) { case 'circle': return Math.PI * shape.radius ** 2; case 'square': return shape.sideLength ** 2; case 'triangle': return (shape.base * shape.height) / 2; default: throw new Error('Invalid shape!'); } }
function calculateArea(shape: Shape): number {
  switch (shape.kind) {
    case 'circle':
      return Math.PI * shape.radius ** 2;
    case 'square':
      return shape.sideLength ** 2;
    case 'triangle':
      return (shape.base * shape.height) / 2;
    default:
      throw new Error('Invalid shape!');
  }
}

تایپ اسکریپت از ویژگی discriminant،

kind
kind، در دستور
switch
switch برای انجام exhaustiveness checking استفاده می‌کند. اگر ما تصادفاً یک مورد را حذف کنیم، تایپ اسکریپت یک خطای کامپایل ایجاد کرده و به ما یادآوری می‌کند که باید همه تایپ‌های shape را مدیریت کنیم.

Type casting و discriminatorها

ما می‌توانیم از discriminated unionها برای type casting استفاده کنیم. سناریویی را تصور می‌کنیم که در آن یک generic

response
response object داریم که می‌تواند یکی از این دو تایپ باشد:
Success
Success یا
Failure
Failure. می‌توانیم از یک ویژگی discriminant،
status
status، برای ایجاد تمایز بین این دو استفاده کنیم و بر این اساس type assertionها را انجام دهیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
type Success = {
status: 'success';
data: unknown;
};
type Failure = {
status: 'failure';
error: string;
};
type APIResponse = Success | Failure;
function handleResponse(response: APIResponse) {
if (response.status === 'success') {
// Type assertion: response is of type Success
console.log(response.data);
} else {
// Type assertion: response is of type Failure
console.error(response.error);
}
}
const successResponse: APIResponse = {
status: 'success',
data: 'Some data',
};
const failureResponse: APIResponse = {
status: 'failure',
error: 'An error occurred',
};
handleResponse(successResponse); // Logs: Some data
handleResponse(failureResponse); // Logs: An error occurred
type Success = { status: 'success'; data: unknown; }; type Failure = { status: 'failure'; error: string; }; type APIResponse = Success | Failure; function handleResponse(response: APIResponse) { if (response.status === 'success') { // Type assertion: response is of type Success console.log(response.data); } else { // Type assertion: response is of type Failure console.error(response.error); } } const successResponse: APIResponse = { status: 'success', data: 'Some data', }; const failureResponse: APIResponse = { status: 'failure', error: 'An error occurred', }; handleResponse(successResponse); // Logs: Some data handleResponse(failureResponse); // Logs: An error occurred
type Success = {
  status: 'success';
  data: unknown;
};

type Failure = {
  status: 'failure';
  error: string;
};

type APIResponse = Success | Failure;

function handleResponse(response: APIResponse) {
  if (response.status === 'success') {
    // Type assertion: response is of type Success
    console.log(response.data);
  } else {
    // Type assertion: response is of type Failure
    console.error(response.error);
  }
}

const successResponse: APIResponse = {
  status: 'success',
  data: 'Some data',
};

const failureResponse: APIResponse = {
  status: 'failure',
  error: 'An error occurred',
};

handleResponse(successResponse); // Logs: Some data
handleResponse(failureResponse); // Logs: An error occurred

ویژگی

status
status در کد بالا discriminator است. تایپ اسکریپت تایپ
response
response object را بر اساس مقدار
status
status محدود می‌کند و این امکان را به ما می‌دهد تا بدون نیاز به بررسی تایپ صریح، به ویژگی‌های مربوطه دسترسی داشته باشیم.

Non-casting: عملگر satisfies

عملگر

satisfies
satisfies یک ویژگی جدید در نسخه ۴٫۹ تایپ اسکریپت است که این امکان را برای ما فراهم می‌کند تا بدون casting عبارت، بررسی کنیم که آیا تایپ عبارت مورد نظر با تایپ دیگری مطابقت دارد یا خیر. این کار می‌تواند برای اعتبارسنجی تایپ‌‌های متغیرها و عبارات، بدون تغییر تایپ اصلی آن‌ها مفید باشد.

در ادامه سینتکس استفاده از عملگر

satisfies
satisfies را بررسی می‌کنیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
expression satisfies type
expression satisfies type
expression satisfies type

برنامه‌ای داریم که با استفاده از عملگر

satisfies
satisfies بررسی می‌کند که آیا یک متغیر بزرگ‌تر از
۵
۵ است یا خیر:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const number = 10;
number satisfies number > 5;
const number = 10; number satisfies number > 5;
const number = 10;
number satisfies number > 5;

اگر تایپ عبارت مطابقت داشته باشد عملگر

satisfies
satisfies مقدار
true
true را return می‌کند و در غیر این صورت،
false
false را برمی‌گرداند. عملگر
satisfies
satisfies ابزار قدرتمندی برای بهبود type safety در کد تایپ اسکریپت ما می‌باشد. این عملگر یک ویژگی نسبتاً جدید است، بنابراین هنوز به اندازه سایر ویژگی‌های تایپ اسکریپت به طور گسترده مورد استفاده قرار نگرفته است. با این حال، این ویژگی ابزار ارزشمندی به حساب می‌آید که می‌تواند در نوشتن کد‌های قابل نگه‌داری و قابل اعتمادتر به ما کمک کند.

تبدیل تایپ‌های داده

در data manipulation، ما همیشه باید داده‌ها را از یک تایپ به تایپ دیگر تبدیل کنیم، و دو تبدیل رایجی که با آن مواجه خواهیم شد عبارتند از: casting یک string به یک number یا تبدیل یک value به string.

casting یک string به یک number

چندین روش برای casting یک string به یک number در تایپ اسکریپت وجود دارد:

استفاده از تابع

Number()
Number():

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
let numString: string = '42';
let num: number = Number(numString);
let numString: string = '42'; let num: number = Number(numString);
let numString: string = '42';
let num: number = Number(numString);

استفاده از عملگر unary

+
+:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
let numString: string = '42';
let num: number = +numString;
let numString: string = '42'; let num: number = +numString;
let numString: string = '42';
let num: number = +numString;

و در نهایت، استفاده از

parseInt()
parseInt() یا
parseFloat()
parseFloat():

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
let intString: string = '42';
let int: number = parseInt(intString);
let floatString: string = '3.14';
let float: number = parseFloat(floatString);
let intString: string = '42'; let int: number = parseInt(intString); let floatString: string = '3.14'; let float: number = parseFloat(floatString);
let intString: string = '42';
let int: number = parseInt(intString); 

let floatString: string = '3.14';
let float: number = parseFloat(floatString);

parseInt()
parseInt() و
parseFloat()
parseFloat() بسیار انعطاف‌پذیرتر هستند، زیرا امکان extract کردن یک عدد از یک رشته که شامل کاراکترهای غیرعددی نیز می‌باشد را فراهم می‌کنند. همچنین، خوب است به این نکته توجه داشته باشیم که اگر نتوانیم رشته را به عنوان یک عدد تجزیه کنیم، همه این متدها
NaN
NaN (Not a Number) را به دست خواهند آورد.

تبدیل به string

در تایپ اسکریپت برای تبدیل یک مقدار به string می‌توانیم از تابع

String()
String() یا متد
toString()
toString() استفاده کنیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
let num: number = 42;
let numString: string = String(num);
// or
let numString2: string = num.toString();
let bool: boolean = true;
let boolString: string = String(bool);
// or
let boolString2: string = bool.toString();
let num: number = 42; let numString: string = String(num); // or let numString2: string = num.toString(); let bool: boolean = true; let boolString: string = String(bool); // or let boolString2: string = bool.toString();
let num: number = 42;
let numString: string = String(num);
// or
let numString2: string = num.toString();

let bool: boolean = true;
let boolString: string = String(bool);
// or
let boolString2: string = bool.toString();

هم

String()
String() و هم
toString()
toString() اساسا روی هر تایپی کار می‌کنند و آن را به یک نمایش رشته‌ای تبدیل می‌کنند.

toString()
toString() یک متد بر روی خود آبجکت است، در حالی که
String()
String() یک تابع سراسری می‌باشد. این دو مورد در بیشتر موارد نتیجه یکسانی خواهند داشت، اما
toString()
toString() اجازه می‌دهد تا نمایش رشته را با نادیده گرفتن متد در custom typeها سفارشی کنیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class CustomType {
value: number;
constructor(value: number) {
this.value = value;
}
toString() {
return `CustomType: ${this.value}`;
}
}
let custom = new CustomType(42);
console.log(String(custom)); // Output: [object Object]
console.log(custom.toString()); // Output: CustomType: 42
class CustomType { value: number; constructor(value: number) { this.value = value; } toString() { return `CustomType: ${this.value}`; } } let custom = new CustomType(42); console.log(String(custom)); // Output: [object Object] console.log(custom.toString()); // Output: CustomType: 42
class CustomType {
  value: number;

  constructor(value: number) {
    this.value = value;
  }

  toString() {
    return `CustomType: ${this.value}`;
  }
}

let custom = new CustomType(42);
console.log(String(custom)); // Output: [object Object]
console.log(custom.toString()); // Output: CustomType: 42

در قطعه کد بالا،

String(custom)
String(custom) رفتار خاصی برای
CustomType
CustomType ما ندارد، در حالی که
custom.toString()
custom.toString() از پیاده‌سازی سفارشی ما استفاده می‌کند.

جمع‌بندی

در این مقاله با روش‌های مختلف type casting در تایپ اسکریپت آشنا شدیم از جمله type assertion با عملگر

as
as، تبدیل تایپ با استفاده از متدهای داخلی مانند
String()
String()،
Number()
Number() و
Boolean()
Boolean()، و تفاوت‌های ظریف بین type assertion و type casting.

همچنین در مورد مفاهیمی مانند type guardها و discriminated unionها بیشتر یاد گرفتیم. این مفاهیم به ما این امکان را می‌دهند تا تایپ خاصی را در یک union type بر اساس بررسی‌های زمان اجرا یا ویژگی‌های discriminant محدود نماییم. با استفاده از این تکنیک‌ها، می‌توانیم type safety برنامه‌های خود را به طور موثر بهبود ببخشیم و خطاهای احتمالی را در زمان کامپایل تشخیص دهیم.

دیدگاه‌ها:

افزودن دیدگاه جدید