فرانت اند

تایپ‌اسکریپت از صفر تا صد

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

  • نویسنده تیم تتا
  • ۹ دقیقه مطالعه
  • به‌روزرسانی:
تایپ‌اسکریپت از صفر تا صد

تایپ‌اسکریپت چه مشکلی را حل می‌کند؟

در جاوااسکریپت، تابعی که انتظار یک عدد دارد بی‌صدا یک رشته هم می‌پذیرد و خطا را چند فایل آن‌طرف‌تر و معمولاً در دست کاربر نشان می‌دهد. تایپ‌اسکریپت یک لایه‌ی بررسی نوع (type checking) روی همان زبان اضافه می‌کند و این دسته از خطاها را هنگام نوشتن کد، داخل ویرایشگر، نشان می‌دهد.

سود دوم کمتر دیده می‌شود: تکمیل خودکار دقیق و تغییر نام امن. وقتی نوع هر مقدار مشخص است، ویرایشگر می‌داند یک شیء چه فیلدهایی دارد و می‌توانید نام یک فیلد را در صدها فایل بدون ترس تغییر دهید. در تیم‌های چندنفره، همین قابلیت مستندسازی زنده‌ی کد است.

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

نصب و پیکربندی <code>tsconfig.json</code>

تایپ‌اسکریپت یک وابستگی توسعه است. در یک پوشه‌ی خالی با سه دستور می‌توانید شروع کنید:

bash
npm init -y
npm install --save-dev typescript
npx tsc --init   # creates tsconfig.json

# compile once, or watch for changes
npx tsc
npx tsc --watch

فایل tsconfig.json رفتار کامپایلر را تعیین می‌کند. این پیکربندی کوچک برای بیشتر پروژه‌های جدید نقطه‌ی شروع خوبی است:

json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "skipLibCheck": true,
    "outDir": "dist"
  },
  "include": ["src"]
}

تایپ‌های پایه: از annotation تا interface

تایپ‌اسکریپت در بسیاری از جاها نوع را خودش حدس می‌زند (type inference)، پس لازم نیست همه‌چیز را بنویسید. نوع را صریح بنویسید در مرز توابع: پارامترها و مقدار برگشتی.

ts
const count = 3;            // inferred: number
let title: string = "TETA";  // explicit annotation

function total(prices: number[], tax = 0.09): number {
  return prices.reduce((sum, x) => sum + x, 0) * (1 + tax);
}

برای توصیف شکل یک شیء از interface یا type استفاده می‌کنید. در کار روزمره تفاوتشان کم است؛ قاعده‌ی ساده این است که برای شکل اشیاء و کلاس‌ها interface و برای union و ترکیب‌ها type بنویسید.

ts
interface User {
  id: number;
  name: string;
  email?: string;        // optional
  readonly createdAt: Date;
}

type Role = "admin" | "editor" | "viewer";

function canEdit(user: User, role: Role): boolean {
  return role !== "viewer" && user.id > 0;
}

<code>any</code> در برابر <code>unknown</code>

any بررسی نوع را خاموش می‌کند و خطا را به همان‌جایی برمی‌گرداند که از آن فرار کرده بودید. وقتی نوع واقعاً نامعلوم است (مثلاً پاسخ یک API)، از unknown استفاده کنید: کامپایلر شما را وادار می‌کند پیش از استفاده، نوع را بررسی کنید.

Union، narrowing و الگوی شناسه‌دار

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

ts
type Result =
  | { status: "loading" }
  | { status: "ok"; data: string[] }
  | { status: "error"; message: string };

function render(r: Result): string {
  switch (r.status) {
    case "loading": return "Loading...";
    case "ok":      return r.data.join(", ");   // data exists here
    case "error":   return "Failed: " + r.message;
  }
}

فیلد مشترک status «شناسه» است. اگر بعدها حالت چهارمی اضافه کنید و switch را به‌روز نکنید، با فعال‌بودن strict و تعریف نوع برگشتی، کامپایلر خطا می‌دهد. همین الگو برای مدیریت وضعیت در رابط کاربری هم عالی است؛ در مدیریت state در ریکت نمونه‌های بیشتری از آن را می‌بینید.

بررسی داده‌ی بیرونی

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

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

جنریک: کد قابل‌استفاده‌ی مجدد با تایپ دقیق

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

ts
function first<T>(items: T[]): T | undefined {
  return items[0];
}

const n = first([1, 2, 3]);        // number | undefined
const s = first(["a", "b"]);       // string | undefined

interface ApiResponse<T> {
  data: T;
  fetchedAt: string;
}

async function getJson<T>(url: string): Promise<ApiResponse<T>> {
  const res = await fetch(url);
  return { data: (await res.json()) as T, fetchedAt: new Date().toISOString() };
}

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

  • Partial<T>: همه‌ی فیلدها را اختیاری می‌کند؛ برای فرم‌های ویرایش.
  • Pick<T, K> و Omit<T, K>: زیرمجموعه‌ای از فیلدها را انتخاب یا حذف می‌کنند.
  • Record<K, V>: شیئی با کلیدهای مشخص و مقدارهای هم‌نوع.
  • ReturnType<typeof fn>: نوع برگشتی یک تابع موجود را می‌گیرد.

حالت strict و مهاجرت تدریجی

گزینه‌ی strict مجموعه‌ای از بررسی‌ها را روشن می‌کند که مهم‌ترین‌شان strictNullChecks است: null و undefined دیگر وارد هر نوعی نمی‌شوند و باید صریحاً مدیریت شوند. این همان گزینه‌ای است که بیشترین خطای واقعی را می‌گیرد.

برای پروژه‌ی جاوااسکریپتی موجود، بازنویسی یک‌باره لازم نیست. این مسیر در عمل بهتر جواب می‌دهد:

  1. tsconfig.json را با allowJs و checkJs: false اضافه کنید تا فایل‌های js و ts کنار هم کار کنند.
  2. فایل‌های جدید را فقط با .ts یا .tsx بنویسید.
  3. فایل‌های کوچک و پرکاربرد (ابزارها، تایپ‌های دامنه، لایه‌ی API) را اول تبدیل کنید؛ بیشترین اثر را دارند.
  4. در CI دستور tsc --noEmit را اجرا کنید تا خطای نوع ادغام نشود.
  5. در پایان strict را روشن کنید؛ اگر تعداد خطاها زیاد است، گزینه‌های آن را یکی‌یکی فعال کنید.

چک‌لیست تایپ‌اسکریپت تمیز

  • strict روشن است و tsc --noEmit در CI اجرا می‌شود.
  • هیچ any بدون دلیل مکتوب در کد نیست؛ داده‌ی ناشناخته با unknown و اعتبارسنجی مدیریت می‌شود.
  • وضعیت‌های رابط کاربری با union شناسه‌دار مدل شده‌اند، نه با چند boolean جدا.
  • نوع برگشتی توابع عمومی (exported) صریح نوشته شده است.
  • تایپ‌های مشترک (مدل داده، پاسخ API) در یک مکان مشخص تعریف شده‌اند.

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

پرسش‌های متداول

تایپ‌اسکریپت را بدون یادگیری جاوااسکریپت می‌شود یاد گرفت؟

توصیه نمی‌شود. تایپ‌اسکریپت روی جاوااسکریپت ساخته شده و رفتار اجرایی همان است. ابتدا توابع، شیء‌ها، آرایه‌ها و async/await را بشناسید؛ سپس تایپ‌ها را مرحله‌به‌مرحله اضافه کنید.

تفاوت interface و type چیست؟

هر دو شکل اشیاء را توصیف می‌کنند. interface قابل گسترش و ادغام است و برای کلاس‌ها مناسب‌تر است؛ type می‌تواند union، tuple و ترکیب‌های پیچیده بسازد. در بیشتر موارد هر دو جواب می‌دهند.

آیا تایپ‌اسکریپت سرعت برنامه را کم می‌کند؟

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

برای پروژه‌ی کوچک هم تایپ‌اسکریپت لازم است؟

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

دیدگاه‌ها

    هنوز دیدگاهی ثبت نشده است. اولین نفر باشید.

    دیدگاه‌ها پس از بررسی منتشر می‌شوند.