خطاهای رایج در تایپ اسکریپت و نحوه رفع آنها: راهنمای جامع

فهرست مطالب

مقدمه: گامی به سوی توسعه پایدار با تایپ‌اسکریپت

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

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

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

۱. خطاهای مرتبط با سیستم تایپ تایپ‌اسکریپت: هسته اصلی مشکلات

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

۱.۱. خطای `Implicit any` و فعال‌سازی `noImplicitAny`

یکی از رایج‌ترین خطاهایی که توسعه‌دهندگان جدید تایپ‌اسکریپت با آن مواجه می‌شوند، خطای `Parameter ‘x’ implicitly has an ‘any’ type.` یا مشابه آن است. این خطا زمانی رخ می‌دهد که تایپ‌اسکریپت نتواند تایپ یک متغیر، پارامتر تابع، یا مقدار بازگشتی تابع را به صورت خودکار استنتاج کند و شما نیز به صراحت تایپی برای آن تعریف نکرده باشید. در حالت پیش‌فرض، تایپ‌اسکریپت در چنین مواقعی تایپ `any` را به آن اختصاص می‌دهد که به معنی “هر تایپی” است. هرچند `any` می‌تواند در مواقع خاصی مفید باشد، استفاده بی‌رویه از آن، مزایای تایپ‌اسکریپت را از بین می‌برد و عملاً کد شما را به جاوا‌اسکریپت عادی تبدیل می‌کند، زیرا کامپایلر هیچ بررسی تایپی روی آن انجام نمی‌دهد.

چرا این اتفاق می‌افتد؟

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


function greet(name) { // 'name' implicitly has an 'any' type.
  console.log(`Hello, ${name}`);
}

let data; // 'data' implicitly has an 'any' type.
data = "some string";
data = 123; // No error here, which defeats the purpose of TypeScript.

نحوه رفع:

بهترین راهکار، فعال‌سازی گزینه `noImplicitAny` در فایل `tsconfig.json` است. با فعال‌سازی این گزینه، تایپ‌اسکریپت هر جا که نتواند تایپی را استنتاج کند و شما نیز آن را صراحتاً تعریف نکرده باشید، یک خطا صادر می‌کند. این کار شما را مجبور می‌کند تا تایپ‌های مورد نظر را به صراحت تعریف کنید:


// tsconfig.json
{
  "compilerOptions": {
    "noImplicitAny": true
  }
}

// Fixed code:
function greet(name: string) { // Explicitly define 'name' as string
  console.log(`Hello, ${name}`);
}

let data: string; // Explicitly define 'data' as string
data = "some string";
// data = 123; // Error: Type 'number' is not assignable to type 'string'.

با تعریف صریح تایپ‌ها، هم خوانایی کد شما افزایش می‌یابد و هم اطمینان حاصل می‌کنید که کامپایلر تایپ‌ها را به درستی بررسی می‌کند.

۱.۲. خطاهای ناسازگاری تایپ (Type Mismatch Errors)

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

چرا این اتفاق می‌افتد؟

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


let age: number = 30;
age = "thirty"; // Error: Type 'string' is not assignable to type 'number'.

function processUser(user: { name: string, age: number }) {
  console.log(`User: ${user.name}, Age: ${user.age}`);
}

processUser({ name: "Alice", age: "twenty" }); // Error: Type 'string' is not assignable to type 'number'.

نحوه رفع:

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


let age: number = 30;
// age = "thirty"; // Correct the value or the type definition

function processUser(user: { name: string, age: number }) {
  console.log(`User: ${user.name}, Age: ${user.age}`);
}

processUser({ name: "Alice", age: 20 }); // Corrected: age is a number.

۱.۳. خطای `Property ‘X’ does not exist on type ‘Y’`

این خطای بسیار رایج زمانی رخ می‌دهد که شما سعی می‌کنید به یک پراپرتی (property) یا متد (method) در یک شیء دسترسی پیدا کنید، در حالی که تایپ‌اسکریپت از وجود آن پراپرتی بر روی تایپ آن شیء اطلاعی ندارد. این معمولاً به دلیل استنتاج تایپ نادرست یا عدم تعریف کامل اینترفیس‌ها/تایپ‌ها رخ می‌دهد.

چرا این اتفاق می‌افتد؟

فرض کنید شما یک شیء دارید که به صورت `any` یا یک تایپ کمتر دقیق تعریف شده است. هنگامی که شما به یک پراپرتی خاص از آن شیء دسترسی پیدا می‌کنید، تایپ‌اسکریپت بررسی می‌کند که آیا این پراپرتی در تعریف تایپ آن شیء وجود دارد یا خیر. اگر وجود نداشته باشد، این خطا را صادر می‌کند.


const user = {
  firstName: "John",
  lastName: "Doe"
};

console.log(user.fullName); // Error: Property 'fullName' does not exist on type '{ firstName: string; lastName: string; }'.

let data: any = { value: 10 };
console.log(data.count); // No error here, but could be a runtime error!

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

نحوه رفع:

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

  1. تعریف صریح اینترفیس یا تایپ: بهترین راهکار، تعریف یک اینترفیس یا تایپ برای شیء مورد نظر است.
  2. تایپ گاردها (Type Guards): اگر پراپرتی ممکن است وجود نداشته باشد (مثلاً از یک API می‌آید)، از تایپ گاردها (مانند `if (‘propertyName’ in object)`) برای بررسی وجود آن قبل از دسترسی استفاده کنید.
  3. اختیاری کردن پراپرتی: اگر پراپرتی اختیاری است، آن را با علامت `?` تعریف کنید.

interface User {
  firstName: string;
  lastName: string;
  fullName?: string; // Optional property
}

const user: User = {
  firstName: "John",
  lastName: "Doe"
};

// Option 1: Add the property to the object (if applicable)
// user.fullName = `${user.firstName} ${user.lastName}`;
// console.log(user.fullName); // OK

// Option 2: Check for existence before access (Type Guard)
if (user.fullName) {
  console.log(user.fullName);
} else {
  console.log(`${user.firstName} ${user.lastName}`);
}

// Example with data from API (could be unknown type initially)
function processApiResponse(data: unknown) {
  if (typeof data === 'object' && data !== null && 'value' in data && typeof (data as { value: any }).value === 'number') {
    console.log((data as { value: number }).value);
  }
}

۱.۴. Null و Undefined: عصر `strictNullChecks`

یکی از بزرگترین کابوس‌های جاوا‌اسکریپت، خطای `Cannot read property ‘x’ of undefined` یا `null` است. تایپ‌اسکریپت با معرفی `strictNullChecks` (که بخشی از تنظیمات strict mode است)، به طور چشمگیری این مشکلات را کاهش می‌دهد.

چرا این اتفاق می‌افتد؟

زمانی که `strictNullChecks` فعال نیست، `null` و `undefined` می‌توانند به هر تایپی اختصاص داده شوند. این یعنی شما می‌توانید متغیری از تایپ `string` داشته باشید که مقدار `null` یا `undefined` به آن اختصاص داده شده باشد، و تایپ‌اسکریپت خطایی صادر نمی‌کند. اما در زمان اجرا، تلاش برای دسترسی به متدها یا پراپرتی‌های یک مقدار `null` یا `undefined` منجر به خطای زمان اجرا می‌شود.


// tsconfig.json (if strictNullChecks is false or not set)
let name: string = null; // No error!
console.log(name.toUpperCase()); // Runtime error: Cannot read property 'toUpperCase' of null

با فعال‌سازی `strictNullChecks`، تایپ‌اسکریپت رفتار خود را تغییر می‌دهد:


// tsconfig.json
{
  "compilerOptions": {
    "strictNullChecks": true
  }
}

// With strictNullChecks enabled:
let name: string = null; // Error: Type 'null' is not assignable to type 'string'.

نحوه رفع:

فعال‌سازی `strictNullChecks` بهترین راه حل برای جلوگیری از این دسته از خطاها است. پس از فعال‌سازی، باید کد خود را طوری تغییر دهید که به درستی با مقادیر `null` و `undefined` برخورد کند:

  1. Optional Chaining (`?.`): برای دسترسی به پراپرتی‌ها یا متدها در شیءهایی که ممکن است `null` یا `undefined` باشند.
  2. Nullish Coalescing (`??`): برای ارائه یک مقدار پیش‌فرض زمانی که یک عبارت `null` یا `undefined` است.
  3. Type Guards: استفاده از `if (value !== null && value !== undefined)` یا `if (value)` برای بررسی وجود مقدار.
  4. Non-null Assertion Operator (`!`): در مواردی که شما به طور قطعی می‌دانید یک مقدار `null` یا `undefined` نیست، می‌توانید از `!` استفاده کنید. اما این کار را با احتیاط انجام دهید، زیرا اگر حدس شما اشتباه باشد، به خطای زمان اجرا منجر خواهد شد.

interface User {
  name: string;
  email?: string | null; // email can be string, undefined, or null
}

function getUserEmail(user: User): string {
  // Option 1: Optional Chaining and Nullish Coalescing
  return user.email ?? "No email provided";

  // Option 2: Type Guard
  // if (user.email) {
  //   return user.email;
  // }
  // return "No email provided";
}

let user1: User = { name: "Alice", email: "alice@example.com" };
let user2: User = { name: "Bob", email: null };
let user3: User = { name: "Charlie" };

console.log(getUserEmail(user1)); // alice@example.com
console.log(getUserEmail(user2)); // No email provided
console.log(getUserEmail(user3)); // No email provided

// Non-null assertion (use with caution!)
function printName(name: string | undefined) {
  // Assuming name will always be defined here due to external logic
  console.log(name!.toUpperCase());
}
printName("John");
// printName(undefined); // This would cause a runtime error even with '!'

۱.۵. خطاهای Function Overload Mismatches

تایپ‌اسکریپت از مفهوم “function overloading” پشتیبانی می‌کند، که به شما اجازه می‌دهد چندین امضای تابع (signature) برای یک تابع با همین نام تعریف کنید، به شرطی که تایپ پارامترها یا تعداد آن‌ها متفاوت باشد. با این حال، پیاده‌سازی (implementation) تابع باید با تمامی امضاهای تعریف شده سازگار باشد.

چرا این اتفاق می‌افتد؟

خطا زمانی رخ می‌دهد که پیاده‌سازی تابع (بدنه تابع) نتواند تمام امضاهای تعریف شده را پوشش دهد، یا تایپ پارامترها در پیاده‌سازی با امضاهای ارائه شده ناسازگار باشد.


// Overload signatures
function add(a: number, b: number): number;
function add(a: string, b: string): string;

// Implementation
function add(a: any, b: any): any {
  // Error: This overload signature is not compatible with its implementation.
  // The implementation must cover all possible call signatures.
  if (typeof a === 'number' && typeof b === 'number') {
    return a + b;
  }
  return a.toString() + b.toString(); // This is fine for string, but 'a' and 'b' could be mixed types.
}

// A correct implementation might look like:
// function add(a: number | string, b: number | string): number | string {
//   if (typeof a === 'number' && typeof b === 'number') {
//     return a + b;
//   } else if (typeof a === 'string' && typeof b === 'string') {
//     return a + b;
//   }
//   throw new Error("Invalid arguments for add function");
// }

نحوه رفع:

برای رفع این خطا، شما باید اطمینان حاصل کنید که پیاده‌سازی تابع شما از یک امضای فراگیرتر (encompassing signature) استفاده می‌کند که تمامی تایپ‌های ممکن پارامترها و مقادیر بازگشتی را پوشش دهد. سپس، در داخل بدنه تابع، از تایپ گاردها برای مدیریت حالت‌های مختلف تایپی استفاده کنید.


// Overload signatures
function add(a: number, b: number): number;
function add(a: string, b: string): string;
function add(a: string, b: number): string; // Another overload
function add(a: number, b: string): string; // Another overload

// Corrected implementation: Uses union types to cover all possible inputs
function add(a: number | string, b: number | string): number | string {
  if (typeof a === 'number' && typeof b === 'number') {
    return a + b;
  } else if (typeof a === 'string' && typeof b === 'string') {
    return a + b;
  } else {
    // Handle mixed types if necessary, or throw an error
    return String(a) + String(b);
  }
}

console.log(add(1, 2));        // 3
console.log(add("Hello", "World")); // HelloWorld
console.log(add("Count: ", 5));    // Count: 5
console.log(add(10, " items"));   // 10 items

۲. خطاهای مرتبط با پیکربندی `tsconfig.json`

`tsconfig.json` فایل پیکربندی مرکزی برای پروژه‌های تایپ‌اسکریپت است که نحوه کامپایل کد شما را توسط کامپایلر `tsc` تعیین می‌کند. تنظیمات نادرست در این فایل می‌تواند منجر به خطاهای کامپایل، مشکلات زمان اجرا و حتی عدم تشخیص صحیح فایل‌ها شود.

۲.۱. اشتباهات `compilerOptions` (مانند `target`, `module`, `lib`)

گزینه‌های `compilerOptions` در `tsconfig.json` کنترل می‌کنند که کامپایلر تایپ‌اسکریپت چگونه کد شما را به جاوا‌اسکریپت کامپایل کند. تنظیمات نادرست این گزینه‌ها می‌تواند به مشکلات عمده‌ای منجر شود.

  • `target` (هدف کامپایل): این گزینه مشخص می‌کند که کد جاوا‌اسکریپت خروجی برای کدام نسخه ECMAScript (ES) تولید شود (مثلاً `ES5`, `ES2015`/`ES6`, `ESNext`).

    مشکل: اگر `target` را روی نسخه‌ای قدیمی‌تر تنظیم کنید (مثلاً `ES5`) و از ویژگی‌های جدیدتر جاوا‌اسکریپت (مانند `async/await`، `Map`، `Set`، `Promise`) در کد تایپ‌اسکریپت خود استفاده کنید، ممکن است کامپایلر آن‌ها را به جاوا‌اسکریپت قدیمی‌تر تبدیل نکند یا نیاز به پلی‌فیل (polyfill) داشته باشید که در `lib` تعریف نشده باشد.

    رفع: `target` را متناسب با محیط اجرایی کد خود (مرورگرها، Node.js) تنظیم کنید. اگر از ویژگی‌های جدید استفاده می‌کنید، مطمئن شوید که یا `target` به اندازه کافی جدید است (مثلاً `ES2017` برای `async/await`) یا پلی‌فیل‌های لازم را در `lib` اضافه کرده‌اید.

  • `module` (سیستم ماژول): این گزینه نحوه تولید کدهای مربوط به ماژول‌ها را تعیین می‌کند (مانند `CommonJS` برای Node.js، `ESNext` برای ماژول‌های ES در مرورگرها، `UMD` برای کتابخانه‌ها).

    مشکل: ناسازگاری بین `module` انتخابی و سیستم ماژول محیط اجرایی (مثلاً استفاده از `ESNext` در یک پروژه Node.js که با `CommonJS` کار می‌کند) منجر به خطاهای `require is not defined` یا مشکلات ایمپورت/اکسپورت می‌شود.

    رفع: `module` را با سیستم ماژول محیطی که کد در آن اجرا می‌شود (Node.js -> `CommonJS` یا `NodeNext`، مرورگرهای مدرن با Bundler -> `ESNext` یا `ES2015`) هماهنگ کنید.

  • `lib` (کتابخانه‌های محیطی): این گزینه تایپ‌های گلوبال (مانند `window`, `document`, `Promise`, `console`) را برای تایپ‌اسکریپت مشخص می‌کند.

    مشکل: اگر `lib` به درستی تنظیم نشده باشد، تایپ‌اسکریپت نمی‌تواند تایپ‌های مربوط به APIهای مرورگر (مانند `DOM`) یا Node.js را شناسایی کند و خطاهایی مانند `Cannot find name ‘document’` یا `Cannot find name ‘Promise’` را صادر می‌کند.

    رفع: `lib` را برای شامل کردن تایپ‌های لازم بر اساس محیط اجرایی خود تنظیم کنید (مثلاً `[“ES2018”, “DOM”]` برای برنامه‌های وب). برخی گزینه‌های `target` به صورت پیش‌فرض شامل کتابخانه‌های خاصی می‌شوند. برای مثال، `ES2017` به صورت پیش‌فرض شامل `Promise` و `async/await` می‌شود.


// tsconfig.json example for a web application using modern JS features
{
  "compilerOptions": {
    "target": "ES2020", // Output ES2020 JS
    "module": "ESNext", // Use ES Modules
    "lib": ["ES2020", "DOM", "DOM.Iterable"], // Include DOM types and modern ES features
    "strict": true, // Enable all strict type-checking options
    "esModuleInterop": true, // Enables default imports for CommonJS modules
    "forceConsistentCasingInFileNames": true,
    "skipLibCheck": true,
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

۲.۲. مسائل `include`, `exclude` و مشکلات حل فایل

گزینه‌های `include` و `exclude` در `tsconfig.json` مشخص می‌کنند که کدام فایل‌ها باید توسط کامپایلر تایپ‌اسکریپت پردازش شوند و کدامیک نادیده گرفته شوند. تنظیمات نادرست در این قسمت می‌تواند منجر به عدم کامپایل برخی فایل‌ها یا تلاش برای کامپایل فایل‌های غیرضروری شود.

چرا این اتفاق می‌افتد؟

  • عدم کامپایل فایل‌ها: اگر فایل‌های تایپ‌اسکریپت شما در الگوی `include` قرار نگیرند، یا به طور تصادفی در `exclude` گنجانده شوند، کامپایلر آن‌ها را نادیده می‌گیرد و خروجی جاوا‌اسکریپت برای آن‌ها تولید نمی‌کند.
  • خطاهای حل ماژول: اگر `paths` یا `baseUrl` به درستی تنظیم نشده باشند، یا ماژول‌های ثالث به درستی تشخیص داده نشوند، منجر به خطاهایی مانند `Cannot find module ‘module-name’ or its corresponding type declarations.` می‌شود.
  • کش کردن (Caching) نادرست: در محیط‌های توسعه با ابزارهایی مانند Webpack یا Parcel، پیکربندی `tsconfig.json` باید با پیکربندی Bundler هماهنگ باشد تا از مشکلات کش و عدم رفرش صحیح جلوگیری شود.

// tsconfig.json
{
  "compilerOptions": {
    // ...
    "baseUrl": "./src", // Base directory for module resolution
    "paths": {
      "@components/*": ["components/*"], // Path aliases
      "@utils/*": ["utils/*"]
    }
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx" // Ensure all relevant files are included
  ],
  "exclude": [
    "node_modules", // Exclude node_modules by default
    "dist",
    "**/*.spec.ts" // Exclude test files from compilation
  ]
}

نحوه رفع:

  1. بررسی `include` و `exclude`: اطمینان حاصل کنید که الگوهای `glob` در `include` تمامی فایل‌های منبع شما را پوشش می‌دهند و `exclude` فقط فایل‌هایی را نادیده می‌گیرد که نیازی به کامپایل ندارند (مانند `node_modules`, `dist`, `test` files).
  2. تنظیم `baseUrl` و `paths`: اگر از ایمپورت‌های مطلق یا alias استفاده می‌کنید، `baseUrl` و `paths` را در `compilerOptions` به درستی تنظیم کنید.
  3. بررسی `rootDir` و `outDir`: اطمینان حاصل کنید که `rootDir` به ریشه فایل‌های تایپ‌اسکریپت شما اشاره دارد و `outDir` محل خروجی فایل‌های جاوا‌اسکریپت را مشخص می‌کند.
  4. `typeRoots` و `types`: اگر در حال اضافه کردن تایپ‌های سفارشی برای ماژول‌هایی هستید که فایل‌های `.d.ts` ندارند، یا می‌خواهید فقط تایپ‌های خاصی را شامل کنید، از این گزینه‌ها استفاده کنید.

۲.۳. مشکلات فایل‌های تعریف (Declaration Files – `.d.ts`)

فایل‌های `.d.ts` یا “declaration files” مسئول ارائه اطلاعات تایپی برای کد جاوا‌اسکریپت موجود (مثل کتابخانه‌های بدون تایپ داخلی) به تایپ‌اسکریپت هستند. مشکلات در این فایل‌ها می‌توانند منجر به خطاهای تایپی یا عدم تشخیص ماژول‌ها شوند.

چرا این اتفاق می‌افتد؟

  • عدم وجود تایپ: اگر از یک کتابخانه جاوا‌اسکریپت استفاده می‌کنید که تایپ‌های تایپ‌اسکریپت را به همراه ندارد و فایل `.d.ts` برای آن نصب نکرده‌اید، با خطاهایی مانند `Cannot find module ‘your-library’ or its corresponding type declarations.` مواجه می‌شوید.
  • تایپ‌های ناسازگار/نادرست: گاهی اوقات تایپ‌هایی که برای یک کتابخانه تعریف شده‌اند (چه توسط خود کتابخانه و چه از `@types/`)، دقیق نیستند یا با نسخه مورد استفاده شما از کتابخانه مطابقت ندارند، که منجر به خطاهای تایپی می‌شود.
  • مشکلات ماژول‌های گلوبال در مقابل ماژول‌های ES: برخی کتابخانه‌ها به صورت گلوبال (global) تایپ می‌شوند در حالی که شما سعی می‌کنید آن‌ها را به عنوان یک ماژول ES ایمپورت کنید، یا برعکس.

// Example: If 'lodash' is used but @types/lodash is not installed
// import _ from 'lodash'; // Error: Cannot find module 'lodash' or its corresponding type declarations.
// console.log(_.get({a:1}, 'a'));

نحوه رفع:

  1. نصب تایپ‌های `@types/`: برای اکثر کتابخانه‌های جاوا‌اسکریپت محبوب، می‌توانید تایپ‌های مربوطه را از `@types` در npm نصب کنید:
    
    npm install --save-dev @types/lodash
            
  2. ایجاد فایل‌های تعریف سفارشی: اگر کتابخانه‌ای تایپ‌های آماده ندارد، می‌توانید فایل `.d.ts` سفارشی برای آن ایجاد کنید. مثلاً `my-library.d.ts` با محتوایی مانند:
    
    declare module 'my-library' {
      export function doSomething(param: string): number;
    }
            

    اطمینان حاصل کنید که این فایل‌ها در `include` پروژه شما قرار دارند.

  3. بررسی نسخه: مطمئن شوید که نسخه تایپ‌ها (مثلاً `@types/react` و `react`) با یکدیگر سازگار هستند. گاهی اوقات نیاز به به‌روزرسانی یا دان‌گرید کردن یکی از آن‌ها است.
  4. تنظیم `allowSyntheticDefaultImports` و `esModuleInterop`: برای حل مشکلات مربوط به ایمپورت دیفالت (default import) از ماژول‌های CommonJS، این گزینه‌ها را در `tsconfig.json` فعال کنید. این امر باعث می‌شود که بتوانید ماژول‌های CommonJS را با گرامر `import` در تایپ‌اسکریپت به شکل بهتری ایمپورت کنید.

۳. خطاهای زمان اجرا با منشاء کامپایل تایپ‌اسکریپت

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

۳.۱. مشکلات قابلیت همکاری جاوا‌اسکریپت (JavaScript Interoperability Issues)

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

چرا این اتفاق می‌افتد؟

  • عدم تطابق سیستم ماژول (CJS vs ESM): اگر یک کتابخانه جاوا‌اسکریپت از CommonJS استفاده می‌کند و پروژه تایپ‌اسکریپت شما برای ماژول‌های ES پیکربندی شده است (یا برعکس)، مشکلات ایمپورت ممکن است منجر به `undefined` شدن ایمپورت‌ها در زمان اجرا شوند. به عنوان مثال، تلاش برای ایمپورت یک ماژول CommonJS با `import MyModule from ‘my-module’;` در حالی که آن ماژول یک اکسپورت دیفالت (default export) ندارد.
  • تایپ‌های نادرست: حتی اگر تایپ‌ها برای یک کتابخانه جاوا‌اسکریپت موجود باشد، ممکن است دقیقاً با رفتار زمان اجرای آن مطابقت نداشته باشند. به عنوان مثال، یک پراپرتی که در تایپ تعریف شده است، در زمان اجرا وجود نداشته باشد یا برعکس.
  • آینده‌نگری بیش از حد تایپ‌اسکریپت: گاهی اوقات تایپ‌اسکریپت فرض می‌کند که یک متد یا پراپرتی در یک شیء وجود دارد، در حالی که در جاوا‌اسکریپت واقعی، این مقدار ممکن است در زمان اجرا `undefined` باشد (مثلاً زمانی که یک شیء به صورت دینامیک ساخته می‌شود یا به دلیل یک شرط خاص).

// Assume a legacy JS library 'my-legacy-lib.js'
// It exports a CommonJS module: module.exports = { calculate: () => {} };

// In TypeScript:
// import MyLib from 'my-legacy-lib'; // This might be undefined in runtime
import * as MyLib from 'my-legacy-lib'; // This is often the correct way for CommonJS
// MyLib.calculate(); // If MyLib is undefined, this will be a runtime error

نحوه رفع:

  1. `esModuleInterop` و `allowSyntheticDefaultImports`: این دو گزینه در `tsconfig.json` برای حل بسیاری از مشکلات ایمپورت/اکسپورت بین CommonJS و ES Modules طراحی شده‌اند. فعال کردن آن‌ها توصیه می‌شود:
    
    "compilerOptions": {
      "esModuleInterop": true,
      "allowSyntheticDefaultImports": true
    }
            
  2. استفاده از `import * as`: برای ایمپورت ماژول‌های CommonJS که یک اکسپورت دیفالت واقعی ندارند، استفاده از `import * as MyModule from ‘my-module’;` اغلب راه حل است.
  3. بررسی دقیق تایپ‌ها و مستندات: هنگام استفاده از کتابخانه‌های جاوا‌اسکریپت، مستندات آن‌ها را برای درک نحوه ایمپورت و استفاده صحیح از آن‌ها مطالعه کنید. تایپ‌های `@types/` نیز می‌توانند گمراه‌کننده باشند، بنابراین همیشه با رفتار واقعی کتابخانه در زمان اجرا، آن را بررسی کنید.
  4. تایپ‌ گاردها و بررسی‌های زمان اجرا: در مواجهه با APIهای جاوا‌اسکریپت که ممکن است رفتار غیرمنتظره‌ای داشته باشند، از تایپ‌ گاردها (`typeof`, `instanceof`, `in`) و بررسی‌های زمان اجرا برای اطمینان از وجود پراپرتی‌ها و تایپ‌های مورد انتظار استفاده کنید.

۳.۲. عدم تطابق هدف کامپایل (Transpilation Target Mismatches)

این خطاها زمانی رخ می‌دهند که `target` در `tsconfig.json` به درستی با محیط اجرایی کد شما مطابقت نداشته باشد، که منجر به تولید کدی می‌شود که محیط مقصد قادر به درک آن نیست.

چرا این اتفاق می‌افتد؟

تایپ‌اسکریپت کد شما را به نسخه‌ای از جاوا‌اسکریپت تبدیل می‌کند که برای `target` مشخص شده است. اگر `target` را روی `ESNext` یا `ES2020` تنظیم کنید اما کد شما روی یک محیط قدیمی‌تر (مانند یک مرورگر قدیمی یا نسخه قدیمی Node.js) اجرا شود، ممکن است با خطاهایی مانند `SyntaxError: Unexpected token ‘async’` یا `ReferenceError: Promise is not defined` مواجه شوید.


// tsconfig.json
{
  "compilerOptions": {
    "target": "ESNext", // Using async/await
    // ...
  }
}

// If this code runs on an environment that doesn't support ESNext (e.g., old Node.js)
async function fetchData() {
  // ...
}
fetchData(); // Runtime error: async keyword not understood

نحوه رفع:

  1. تنظیم صحیح `target`: `target` را روی قدیمی‌ترین نسخه جاوا‌اسکریپت که محیط اجرایی شما پشتیبانی می‌کند، تنظیم کنید. اگر نیاز به پشتیبانی از مرورگرهای قدیمی‌تر دارید، `ES5` یا `ES2015` (ES6) ممکن است مناسب باشد. برای Node.js، می‌توانید از نسخه‌های جدیدتر `ES` استفاده کنید.
  2. استفاده از پلی‌فیل‌ها (Polyfills): اگر نیاز به استفاده از ویژگی‌های جدید جاوا‌اسکریپت دارید اما باید از نسخه‌های قدیمی‌تر پشتیبانی کنید، از پلی‌فیل‌ها مانند `core-js` یا `regenerator-runtime` استفاده کنید. این پلی‌فیل‌ها ویژگی‌های جدید را برای محیط‌های قدیمی‌تر شبیه‌سازی می‌کنند.
  3. بررسی `lib` در کنار `target`: مطمئن شوید که `lib` شامل کتابخانه‌های تایپی مناسب برای `target` شما باشد تا تایپ‌اسکریپت به درستی بداند که کدام APIها در دسترس هستند و کدام نیاز به پلی‌فیل دارند.

۳.۳. پیکربندی Decorator و ویژگی‌های تجربی

Decoratorها و سایر ویژگی‌های تجربی (مانند Metadata Reflection API) که اغلب در فریم‌ورک‌هایی مانند Angular، TypeORM یا NestJS استفاده می‌شوند، نیاز به پیکربندی خاصی در `tsconfig.json` دارند. عدم پیکربندی صحیح می‌تواند منجر به خطاهای کامپایل یا زمان اجرا شود.

چرا این اتفاق می‌افتد؟

Decoratorها هنوز یک ویژگی تجربی در جاوا‌اسکریپت هستند و نیاز به پرچم‌های خاصی در کامپایلر تایپ‌اسکریپت دارند تا فعال شوند. علاوه بر این، برخی از آن‌ها (مانند آن‌هایی که برای تزریق وابستگی استفاده می‌شوند) به `reflect-metadata` برای ذخیره فراداده (metadata) در زمان اجرا نیاز دارند. اگر این پرچم‌ها فعال نباشند یا `reflect-metadata` ایمپورت نشده باشد، Decoratorها کار نمی‌کنند و خطاهای زمان اجرا رخ می‌دهد.


// If emitDecoratorMetadata or experimentalDecorators are not enabled
// @Component({ ... }) // Syntax error or runtime error
// class MyClass {}

نحوه رفع:

  1. فعال‌سازی `experimentalDecorators`: این پرچم برای فعال کردن پشتیبانی از Decoratorها در تایپ‌اسکریپت ضروری است:
    
    "compilerOptions": {
      "experimentalDecorators": true,
      // ...
    }
            
  2. فعال‌سازی `emitDecoratorMetadata` و نصب `reflect-metadata`: اگر Decoratorهای شما نیاز به فراداده تایپی در زمان اجرا دارند (مثلاً برای تزریق وابستگی)، باید `emitDecoratorMetadata` را فعال کنید و `reflect-metadata` را نصب کرده و آن را در نقطه ورود برنامه خود ایمپورت کنید:
    
    "compilerOptions": {
      "experimentalDecorators": true,
      "emitDecoratorMetadata": true,
      // ...
    }
            
    
    // npm install reflect-metadata
    // In your main.ts or app.ts
    import "reflect-metadata";
            

۴. خطاهای لینتر (ESLint/TSLint) و سبک کد

ابزارهای لینتینگ مانند ESLint (با پلاگین `@typescript-eslint/parser`) و TSLint (که اکنون منسوخ شده و ESLint جایگزین آن شده است) نقش مهمی در اعمال سبک کد، کشف مشکلات منطقی و جلوگیری از خطاهای احتمالی قبل از کامپایل دارند. خطاهای لینتر، هرچند مستقیماً به سیستم تایپ مربوط نمی‌شوند، اما می‌توانند روند توسعه را مختل کنند.

۴.۱. تداخلات پیکربندی و قوانین متناقض

چرا این اتفاق می‌افتد؟

زمانی که از ESLint در کنار تایپ‌اسکریپت استفاده می‌کنید، نیاز به پیکربندی خاصی دارید (پلاگین `@typescript-eslint`). تداخلات می‌توانند به دلایل زیر رخ دهند:

  • قوانین تکراری: وجود قوانین ESLint که با قوانین تایپ‌اسکریپت ناسازگار هستند یا تکراری هستند (مثلاً قوانینی که قبلاً در ESLint برای جاوا‌اسکریپت تعریف شده بودند و اکنون توسط پلاگین تایپ‌اسکریپت مدیریت می‌شوند).
  • تنظیمات نادرست Parser: عدم تنظیم `parser` به `@typescript-eslint/parser` باعث می‌شود ESLint نتواند سینتکس تایپ‌اسکریپت را به درستی تجزیه کند.
  • تداخل با Prettier: اگر از Prettier برای فرمت‌بندی کد استفاده می‌کنید، ممکن است قوانین لینتر شما با قوانین فرمت‌بندی Prettier تداخل پیدا کنند که منجر به جنگ بین این دو ابزار می‌شود.

// .eslintrc.js example (simplified)
module.exports = {
  parser: '@typescript-eslint/parser', // Correct parser
  plugins: ['@typescript-eslint'],
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended', // Recommended TypeScript rules
    'prettier/@typescript-eslint', // Integrates with Prettier (if used)
    'plugin:prettier/recommended' // Ensures Prettier formatting is applied
  ],
  rules: {
    // Custom rules or overrides
    '@typescript-eslint/explicit-function-return-type': 'off', // Example: turn off a rule
  }
};

نحوه رفع:

  1. پیکربندی صحیح ESLint برای TS: مطمئن شوید که ESLint با `@typescript-eslint/parser` و پلاگین‌های مرتبط به درستی پیکربندی شده است.
  2. استفاده از `extends` توصیه شده: از `extends` در فایل پیکربندی ESLint برای استفاده از مجموعه‌های قوانین توصیه شده (مانند `plugin:@typescript-eslint/recommended`) استفاده کنید.
  3. ادغام با Prettier: اگر از Prettier استفاده می‌کنید، از `eslint-config-prettier` و `eslint-plugin-prettier` برای غیرفعال کردن قوانینی که با Prettier تداخل دارند و اعمال فرمت‌بندی Prettier به عنوان یک قانون ESLint استفاده کنید.
  4. غیرفعال کردن قوانین تکراری/متناقض: در صورت لزوم، قوانینی را که باعث تداخل می‌شوند به صورت دستی `off` کنید.

۴.۲. متغیرها/ایمپورت‌های استفاده نشده (Unused Variables/Imports)

این خطاها معمولاً توسط لینترها (و گاهی توسط خود کامپایلر تایپ‌اسکریپت با فعال بودن `noUnusedLocals` یا `noUnusedParameters`) شناسایی می‌شوند و به توسعه‌دهنده اطلاع می‌دهند که کد مرده (dead code) یا وابستگی‌های غیرضروری دارد.

چرا این اتفاق می‌افتد؟

هنگامی که شما یک متغیر، تابع، کلاس، یا ایمپورت را تعریف می‌کنید اما هرگز از آن در کد خود استفاده نمی‌کنید، لینتر این مورد را به عنوان یک خطای احتمالی یا نشانه‌ای از کد غیرضروری علامت‌گذاری می‌کند. این کار به تمیز نگه داشتن کد و کاهش حجم باندل (bundle size) کمک می‌کند.


import { unusedFunction } from './utils'; // Lint error: 'unusedFunction' is defined but never used.

const unusedVariable = 10; // Lint error: 'unusedVariable' is assigned a value but never used.

function myFunc(param1: number, param2: string) { // Lint error: 'param2' is defined but never used.
  console.log(param1);
}

نحوه رفع:

  1. حذف کدهای استفاده نشده: بهترین راه حل، حذف ایمپورت‌ها، متغیرها، یا پارامترهای استفاده نشده است.
  2. پیشوند `_` برای پارامترهای استفاده نشده: اگر یک پارامتر تابع استفاده نمی‌شود اما لازم است (مثلاً برای تطابق با امضای یک اینترفیس)، می‌توانید نام آن را با پیشوند `_` شروع کنید (`_param2`). برخی لینترها این را به عنوان نشانه‌ای برای نادیده گرفتن آن پارامتر در نظر می‌گیرند.
  3. غیرفعال کردن موقت قانون: در موارد بسیار نادر که نمی‌توانید کد را حذف کنید یا پیشوند اضافه کنید، می‌توانید به صورت موقت قانون لینتر را برای آن خط خاص غیرفعال کنید (با استفاده از کامنت‌های `eslint-disable-next-line` یا `// tslint:disable-next-line`). این کار توصیه نمی‌شود مگر در شرایط خاص.
  4. تنظیم `noUnusedLocals` و `noUnusedParameters` در `tsconfig.json`: این گزینه‌ها را فعال کنید تا کامپایلر تایپ‌اسکریپت نیز این خطاها را تشخیص دهد.

۴.۳. اشکالات `async/await` و وعده‌ها (Promises)

استفاده نادرست از `async/await` و Promises می‌تواند منجر به خطاهای منطقی، مشکلات کنترل جریان، یا عدم مدیریت صحیح خطاها شود که توسط لینترها قابل تشخیص است.

چرا این اتفاق می‌افتد؟

  • نادیده گرفتن `await`: فراخوانی یک تابع `async` بدون استفاده از `await` (یا `then`/`catch`) باعث می‌شود که اجرای کد ادامه پیدا کند قبل از اینکه عملیات ناهمزمان به پایان برسد. این می‌تواند منجر به دسترسی به مقادیر `undefined` یا `Promise`های حل نشده شود.
  • نادیده گرفتن خطاهای Promise: عدم استفاده از `catch` برای Promiseها یا `try/catch` برای توابع `async` می‌تواند منجر به خطاهای مدیریت نشده و Crash برنامه شود.
  • بازگشت Promise در توابع همزمان: اگر یک تابع که انتظار می‌رود یک مقدار عادی بازگرداند، به اشتباه یک Promise بازگرداند (مثلاً به دلیل نادیده گرفتن `await` در داخل آن)، می‌تواند مشکلات تایپی یا منطقی ایجاد کند.

async function fetchData() {
  // Simulate an async operation that fails
  return Promise.reject(new Error("Failed to fetch"));
}

// Without proper error handling
fetchData(); // Unhandled promise rejection warning/error

// Lint error: A 'Promise' must be awaited or handled.

نحوه رفع:

  1. همیشه `await` کنید: اطمینان حاصل کنید که هر فراخوانی تابع `async` یا Promise را با `await` مدیریت می‌کنید یا از `then()` و `catch()` استفاده می‌کنید.
  2. مدیریت خطا با `try/catch`: همیشه فراخوانی‌های `async/await` را در بلوک `try/catch` قرار دهید تا خطاهای احتمالی را مدیریت کنید.
  3. قوانین Linting برای `async/await`: از قوانین ESLint مانند `@typescript-eslint/no-floating-promises` برای تشخیص Promiseهایی که مدیریت نشده‌اند، و `no-unused-vars` برای متغیرهایی که Promise هستند و await نشده‌اند، استفاده کنید.
  4. بررسی تایپ مقدار بازگشتی: اطمینان حاصل کنید که تایپ مقدار بازگشتی توابع `async` به درستی تعریف شده است (`Promise`).

۵. بهترین روش‌ها برای دیباگینگ و پیشگیری از خطاهای تایپ‌اسکریپت

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

۵.۱. بهره‌گیری از بازخورد کامپایلر تایپ‌اسکریپت

کامپایلر `tsc` تایپ‌اسکریپت بهترین دوست شما در یافتن و رفع خطاها است. درک پیام‌های خطا و نحوه کار با آن‌ها بسیار مهم است.

چرا مهم است؟

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


// Example compiler error output
// src/app.ts:5:10 - error TS2322: Type 'string' is not assignable to type 'number'.
// 5   let num: number = "hello";
//             ~~~~~~~

بهترین روش:

  1. خواندن دقیق پیام‌های خطا: همیشه پیام کامل خطا را بخوانید، نه فقط قسمت اول آن. کد خطا (مانند `TS2322`) را به خاطر بسپارید یا در مستندات تایپ‌اسکریپت جستجو کنید.
  2. فعال کردن `strict` mode: همانطور که قبلاً ذکر شد، فعال کردن گزینه `strict: true` در `tsconfig.json` تمام بررسی‌های سخت‌گیرانه تایپ‌اسکریپت را فعال می‌کند. این کار ممکن است در ابتدا منجر به تعداد زیادی خطا شود، اما در بلندمدت کیفیت کد شما را به شدت افزایش می‌دهد و از خطاهای زمان اجرا جلوگیری می‌کند.
  3. استفاده از `noEmitOnError`: این گزینه در `tsconfig.json` باعث می‌شود که کامپایلر در صورت وجود خطا، فایل‌های خروجی جاوا‌اسکریپت را تولید نکند. این کار از اجرای کد ناقص یا دارای خطا جلوگیری می‌کند.
  4. کامنت‌گذاری `// @ts-ignore` و `// @ts-expect-error` با احتیاط: این کامنت‌ها به شما اجازه می‌دهند خطاهای خاصی را نادیده بگیرید. از آن‌ها با نهایت دقت و فقط در مواقعی که اطمینان دارید خطا بی‌اهمیت است یا راه‌حل بهتری وجود ندارد، استفاده کنید. برای خطاهایی که انتظار دارید رخ دهند اما می‌خواهید آن‌ها را نادیده بگیرید، `// @ts-expect-error` ترجیح داده می‌شود، زیرا اگر خطا از بین برود، خودش خطا می‌دهد.

۵.۲. استفاده مؤثر از VS Code و قابلیت‌های IDE

ویرایشگرهای کد مدرن مانند VS Code، پشتیبانی عالی از تایپ‌اسکریپت دارند و می‌توانند به طور چشمگیری در شناسایی و رفع خطاها به شما کمک کنند.

چرا مهم است؟

VS Code از سرویس زبان تایپ‌اسکریپت استفاده می‌کند که در حین تایپ، بازخورد لحظه‌ای (real-time feedback) ارائه می‌دهد. این شامل هایلایت کردن خطاها، تکمیل خودکار کد (intellisense)، اطلاعات تایپی در هنگام هاور (hover)، و قابلیت‌های Refactoring می‌شود.

بهترین روش:

  1. پیکربندی VS Code: اطمینان حاصل کنید که VS Code از نسخه صحیح تایپ‌اسکریپت (نسخه ورک‌اسپیس یا نسخه نصب شده سراسری) استفاده می‌کند.
  2. استفاده از Error Squiggles: خطوط موج‌دار قرمز زیر کد، نشانگر خطاها هستند. ماوس را روی آن‌ها نگه دارید تا پیام خطا را ببینید.
  3. `Peek Problem` (Ctrl+Shift+M): این قابلیت لیستی از تمام خطاها و هشدارهای موجود در پروژه را نمایش می‌دهد. کلیک کردن روی هر خطا شما را به محل آن در کد می‌برد.
  4. Code Actions / Quick Fixes: لامپ کوچک کنار خطا (یا Alt+Enter) گزینه‌هایی برای رفع سریع خطاها ارائه می‌دهد، مانند اضافه کردن ایمپورت‌های از دست رفته، افزودن پراپرتی به اینترفیس، یا تبدیل تایپ‌ها.
  5. Go to Definition / Type Definition: با کلیک راست (یا F12) روی یک متغیر، تابع یا تایپ، می‌توانید به تعریف آن در کد منبع یا فایل `.d.ts` مربوطه بروید که در درک ساختار تایپی کمک می‌کند.
  6. Refactoring: از قابلیت‌های Refactoring (مانند Extract to function/variable) استفاده کنید که به حفظ صحت تایپی کد کمک می‌کند.

۵.۳. توسعه مبتنی بر تست (TDD) و تست واحد (Unit Testing)

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

چرا مهم است؟

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

بهترین روش:

  1. پوشش تست بالا: برای کد تایپ‌اسکریپت خود پوشش تست مناسبی داشته باشید. هرچه پوشش تست بالاتر باشد، احتمال نادیده گرفته شدن خطاهای زمان اجرا کمتر می‌شود.
  2. تست لبه‌ها (Edge Cases): به طور خاص، برای سناریوهایی که ممکن است `null`، `undefined`، مقادیر غیرمنتظره، یا خطاهای API رخ دهند، تست بنویسید.
  3. استفاده از فریم‌ورک‌های تست مناسب: از فریم‌ورک‌هایی مانند Jest یا Mocha به همراه Chai/Expect برای نوشتن تست‌ها استفاده کنید. این فریم‌ورک‌ها از تایپ‌اسکریپت پشتیبانی می‌کنند و تجربه تست‌نویسی را آسان‌تر می‌کنند.
  4. ادغام تست‌ها در CI/CD: اطمینان حاصل کنید که تست‌ها به عنوان بخشی از خط لوله CI/CD (Continuous Integration/Continuous Deployment) شما اجرا می‌شوند تا از ورود کدهای دارای خطا به محیط تولید جلوگیری شود.

۵.۴. بازبینی کد (Code Reviews) و ابزارهای تحلیل استاتیک

چرا مهم است؟

بازبینی کد توسط همکاران به کشف خطاها و بهبود کیفیت کد کمک می‌کند، زیرا دیدگاه‌های مختلف می‌توانند مشکلات را شناسایی کنند که ممکن است از دید توسعه‌دهنده اصلی پنهان مانده باشد. ابزارهای تحلیل استاتیک مانند SonarQube یا Codacy به صورت خودکار کد شما را برای شناسایی بدهکاری فنی (technical debt)، آسیب‌پذیری‌ها و الگوهای کدگذاری نامناسب تحلیل می‌کنند.

بهترین روش:

  1. بازبینی کد منظم: یک فرآیند بازبینی کد استاندارد را در تیم خود پیاده‌سازی کنید. در طول بازبینی‌ها، به درستی تایپ‌ها، استفاده صحیح از ویژگی‌های تایپ‌اسکریپت، و مدیریت خطاها توجه کنید.
  2. استفاده از ابزارهای تحلیل استاتیک: این ابزارها می‌توانند به طور خودکار مسائلی مانند کدهای تکراری، پیچیدگی بالای متدها، و مشکلات امنیتی را شناسایی کنند. بسیاری از این ابزارها از تایپ‌اسکریپت پشتیبانی می‌کنند.
  3. ابزارهای کیفیت کد: علاوه بر لینترها، ابزارهایی مانند Type Coverage می‌توانند به شما در مشاهده میزان پوشش تایپی پروژه کمک کنند و مناطقی را که تایپ‌اسکریپت در آن‌ها کمتر سخت‌گیرانه است، شناسایی کنند.

۶. تکنیک‌های پیشرفته عیب‌یابی

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

۶.۱. ردیابی استنتاج تایپ (Tracing Type Inference)

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

چرا این اتفاق می‌افتد؟

گاهی اوقات، کامپایلر تایپ را به گونه‌ای استنتاج می‌کند که شما انتظار ندارید، به خصوص در توابع جنریک (generics)، Union Types، Intersection Types، یا در سناریوهای پیچیده کنترل جریان. این می‌تواند منجر به خطاهایی شود که در نگاه اول مبهم به نظر می‌رسند.


function merge<T, U>(obj1: T, obj2: U) {
  return { ...obj1, ...obj2 };
}

const merged = merge({ a: 1 }, { b: "hello" });
// Type of 'merged' is { a: number } & { b: string } (Intersection Type)
// But if you expected a specific combined type, and it's not inferred correctly,
// it might lead to errors later.

نحوه رفع:

  1. استفاده از IntelliSense: در VS Code، ماوس را روی یک متغیر یا عبارت نگه دارید تا تایپ استنتاج شده توسط تایپ‌اسکریپت را مشاهده کنید. این اولین گام برای درک تایپ‌ها است.
  2. استفاده از کامنت‌های JSDoc: گاهی اوقات اضافه کردن JSDoc به توابع و پارامترها می‌تواند به تایپ‌اسکریپت کمک کند تا تایپ‌ها را بهتر استنتاج کند و همچنین مستندات خوبی برای کد شما ایجاد می‌کند.
  3. استفاده از توابع کمکی `infer` و Conditional Types: در سناریوهای تایپ‌نویسی پیشرفته، می‌توانید از `infer` در Conditional Types برای استخراج تایپ‌ها از ساختارهای پیچیده استفاده کنید.
  4. بازنگری `compilerOptions` برای سخت‌گیری بیشتر: گزینه‌هایی مانند `strictFunctionTypes`، `strictPropertyInitialization` و `noImplicitReturns` می‌توانند به شما کمک کنند تا تایپ‌اسکریپت در استنتاج تایپ‌ها و اعمال قوانین سخت‌گیرانه‌تر شود.
  5. کاهش پیچیدگی: اگر استنتاج تایپ بیش از حد پیچیده می‌شود، سعی کنید توابع یا اینترفیس‌های خود را ساده‌تر کنید. گاهی اوقات، تعریف صریح یک تایپ به جای تکیه بر استنتاج، می‌تواند خوانایی و قابلیت نگهداری کد را افزایش دهد.

۶.۲. استفاده از `ts-node` برای تکرار سریع‌تر

`ts-node` یک ابزار بسیار مفید است که به شما اجازه می‌دهد فایل‌های تایپ‌اسکریپت را به صورت مستقیم در Node.js اجرا کنید، بدون نیاز به کامپایل دستی آن‌ها به جاوا‌اسکریپت.

چرا مفید است؟

در چرخه‌های توسعه‌ای که شامل تغییرات کوچک و تست سریع می‌شوند، کامپایل دستی با `tsc` و سپس اجرای فایل جاوا‌اسکریپت می‌تواند وقت‌گیر باشد. `ts-node` این فرآیند را با کامپایل در حافظه و اجرای مستقیم، سرعت می‌بخشد و تجربه توسعه را روان‌تر می‌کند.


// Instead of:
// tsc my-script.ts
// node my-script.js

// Use ts-node:
// ts-node my-script.ts

نحوه استفاده:

  1. نصب `ts-node`:
    
    npm install -g ts-node typescript
            

    یا به صورت محلی در پروژه:

    
    npm install --save-dev ts-node typescript
            
  2. اجرای فایل‌ها: به سادگی از دستور `ts-node` به جای `node` برای اجرای فایل‌های `.ts` خود استفاده کنید.
  3. پیکربندی: `ts-node` از `tsconfig.json` پروژه شما استفاده می‌کند. می‌توانید گزینه‌های خاصی را نیز از طریق خط فرمان به آن ارسال کنید.
  4. یکپارچه‌سازی با تست‌ها: بسیاری از فریم‌ورک‌های تست (مانند Jest) می‌توانند با `ts-node` یا Babel/SWC ادغام شوند تا تست‌های تایپ‌اسکریپت را مستقیماً اجرا کنند.

۶.۳. نظارت بر خروجی `tsc –watch`

پرچم `–watch` (یا `-w`) در دستور `tsc` به کامپایلر تایپ‌اسکریپت می‌گوید که تغییرات فایل‌ها را زیر نظر داشته باشد و به طور خودکار کد را مجدداً کامپایل کند.

چرا مفید است؟

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

نحوه استفاده:

  1. اجرای دستور: در ترمینال پروژه خود، دستور `tsc –watch` را اجرا کنید.
  2. مشاهده خروجی: ترمینال را باز نگه دارید. هر بار که یک فایل تایپ‌اسکریپت را ذخیره می‌کنید، کامپایلر به طور خودکار اجرا شده و هرگونه خطا یا هشدار را نمایش می‌دهد.
  3. ترکیب با ابزارهای دیگر: می‌توانید `tsc –watch` را با ابزارهای دیگری مانند `nodemon` (برای ری‌استارت خودکار سرور Node.js پس از تغییر فایل‌های JS خروجی) یا ابزارهای باندل‌کننده (bundler) مانند Webpack’s `watch` mode ترکیب کنید تا یک تجربه توسعه کامل و خودکار داشته باشید.

نتیجه‌گیری: قدرت تایپ‌اسکریپت در دستان شما

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

تمرکز بر فعال‌سازی گزینه‌های سخت‌گیرانه در `tsconfig.json` (به ویژه `strict: true` و `noImplicitAny`)، استفاده مؤثر از قابلیت‌های IDE مانند VS Code، و اتخاذ بهترین روش‌ها در توسعه (مانند تست‌نویسی و بازبینی کد)، نه تنها به شما کمک می‌کند تا خطاهای موجود را رفع کنید، بلکه از بروز بسیاری از آن‌ها در وهله اول جلوگیری خواهد کرد. این رویکرد پیشگیرانه منجر به کدی تمیزتر، قابل نگهداری‌تر و باگ کمتر می‌شود.

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

“تسلط به برنامه‌نویسی پایتون با هوش مصنوعی: آموزش کدنویسی هوشمند با ChatGPT”

قیمت اصلی 2.290.000 ریال بود.قیمت فعلی 1.590.000 ریال است.

"تسلط به برنامه‌نویسی پایتون با هوش مصنوعی: آموزش کدنویسی هوشمند با ChatGPT"

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

ویژگی‌های کلیدی:

بدون نیاز به تجربه قبلی برنامه‌نویسی

زیرنویس فارسی با ترجمه حرفه‌ای

۳۰ ٪ تخفیف ویژه برای دانشجویان و دانش آموزان