تفاوت Type و Interface در TypeScript با مثالهای کاربردی
اگه با TypeScript کار کرده باشی، حتماً با دو تا مفهوم پرکاربرد برخورد کردی: type و interface. هر دو برای تعریف قرارداد و شکل دادهها استفاده میشن، اما هر کدوم یه سری قابلیت و محدودیتهای خاص خودشون رو دارن. توی این مطلب با مثالهای واقعی و سناریوهای روزمره برنامهنویسی جلو میریم تا دقیق بفهمیم کِی کدوم بهتره.
۱) از کجا شروع کنیم؟ شباهتها و تعریف اولیه
هم type alias و هم interface میان به TypeScript میگن که فلان آبجکت، تابع یا ساختار باید چه شکلی داشته باشه. هر دو:
- میتونن شکل یک آبجکت رو تعریف کنن
- میتونن تابع یا کالسیگنچر تعریف کنن
- میتونن در کلاسها با implements استفاده بشن (تا وقتی به یک نوع آبجکتی resolve بشن)
یک مثال ساده از هر دو
// با type
type User = {
id: number;
name: string;
email?: string;
}
// با interface
interface IUser {
id: number;
name: string;
email?: string;
}
// تابع با هر دو شکل
type Fetcher = (url: string) => Promise<Response>;
interface IFetcher {
(url: string): Promise<Response>;
}
۲) تفاوتهای کلیدی که باید حتماً بلد باشی
الف) Union و Intersection
اینجاست که type میدرخشه. با type خیلی راحت میتونی Union یا اینترسکشن بسازی: نکته: Union یعنی اینکه شما به یک متغیر چند "نوع ممکن" بدی و هر کدوم بشه اوکیه!
مثلا type Result = string | number
type ApiState =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: User[] }
| { status: 'error'; error: string };
// اینترسکشن
type WithTimestamps = { createdAt: Date; updatedAt: Date };
type UserEntity = User & WithTimestamps;
interface همچین یونین مستقیمی نداره. میتونی چندتا interface بنویسی، ولی برای یونین باید از type کمک بگیری.
ب) Declaration Merging (ادغام اعلان)
interface میتونه چند بار با یک نام تعریف بشه و خودشون با هم merge میشن. type این قابلیت رو نداره.
interface RequestMeta { requestId: string; }
interface RequestMeta { locale?: string; }
// حالا RequestMeta شامل هر دو پراپرتی میشه
این ویژگی برای augmentation خیلی کاربردیه (مثلاً اضافهکردن تایپ به Window یا کتابخونهها).
ج) Extends در مقابل &
interface با extends توسعه پیدا میکنه، ولی type معمولاً با & ترکیب میشه:
interface Entity { id: string }
interface Account extends Entity { role: 'admin' | 'user' }
type EntityT = { id: string };
type AccountT = EntityT & { role: 'admin' | 'user' };
د) Mapped Types و قابلیتهای پیشرفته
Mapped Type ها (مثل Readonly، Partial و…) ذاتاً با type تعریف میشن. برای تغییر ساختاری کلیدهای یک تایپ، type گزینه بهتریه.
type ReadonlyRecord<T> = {
readonly [K in keyof T]: T[K];
};
ه) امضاهای ایندکس و تابع
هر دو از امضای تابع و ایندکس پشتیبانی میکنن:
interface StringMap { [key: string]: string }
type Dict = { [key: string]: string }
| ویژگی | type | interface |
|---|---|---|
| یونین (A | B) | ✓ | ✗ |
| اینترسکشن (A & B) | ✓ | به صورت مستقیم خیر (بهجای آن extends) |
| Declaration Merging | ✗ | ✓ |
| Mapped Types | ✓ | محدودتر |
| implements در کلاس | ✓ (اگر به آبجکت resolve شود) | ✓ |
| Augmentation کتابخانهها/Window | محدودتر | ✓ و مرسوم |
۳) سناریوهای واقعی: کِی کدوم بهتره؟
الف) مدلهای دامنه و قراردادهای پایدار بین ماژولها
وقتی میخوای یه «قرارداد پایدار» بین بخشهای مختلف پروژه داشته باشی، interface انتخاب خوبیه. چون قابل توسعه و merge شدنه و برای API عمومی کتابخونهها خیلی منطقی تره.
interface Pagination {
page: number;
pageSize: number;
}
interface PagedResponse<T> extends Pagination {
items: T[];
total: number;
}
ب) حالتهای مختلف داده و ماشین وضعیت
وقتی حالتهای مختلف لازم داری (مثل loading، error، success)، type با Union خیلی تمیز تره:
type LoadState =
| { kind: 'idle' }
| { kind: 'loading' }
| { kind: 'success'; data: User[] }
| { kind: 'error'; message: string };
ج) React Props و ترکیبپذیری
برای Props هم هر دو جواب میدن. اگر نیاز به Union یا ترکیب با & داری، type منعطف تره؛ اگر قرار یه قرارداد پایدار باشه که بعداً بقیه توسعهاش بدن، interface حس بهتری میده.
// با type
type ButtonProps = {
variant: 'primary' | 'ghost';
disabled?: boolean;
} & React.ButtonHTMLAttributes<HTMLButtonElement>;
// با interface
interface ICardProps {
imageUrl?: string;
onClick?: () => void;
}
د) کتابخونهها و تایپهای خارجی
برای Augmentation تو کتابخونهها یا گلوبالها، interface بهترین انتخابه:
declare global {
interface Window {
analytics?: { track: (event: string, data?: unknown) => void };
}
}
۴) نکات پیشرفته، بهترینتمرینها و دامهای معمول
- قرارداد عمومی؟ interface: اگر قراره بقیه اعضای تیم تایپ رو توسعه بدن، از interface استفاده کن تا از Declaration Merging سود ببری.
- ترکیب پذیری و Union ها؟ type: برای مدلکردن حالتها، Variantها و ترکیب ساختارها با & و |، از type استفاده کن.
- کلاسها: کلاس میتونه هم interface و هم type آبجکتی رو implement کنه؛ اما برای API عمومی معمولاً interface خواناتر و استاندارد تره.
- Mapped Types: اگر میخوای کلیدها رو تبدیل کنی یا readonly/optional رو بهصورت کلی اعمال کنی، type انتخاب طبیعیه.
- Performance: از نظر کارایی کامپایل، تفاوت خاصی که خیلی مهم باشه معمولاً دیده نمیشه؛ خوانایی و توسعهپذیری مهم تره.
چند تمرین ترکیبی
// 1) interface برای قرارداد، type برای حالتها
interface BaseError { code: string; }
interface NetworkError extends BaseError { retryable: boolean; }
type Result<T> =
| { ok: true; data: T }
| { ok: false; error: NetworkError };
// 2) augmentation با interface
declare global {
interface Window {
config?: { apiBaseUrl: string };
}
}
// 3) ترکیب سریع با & در type
type WithMeta = { createdBy: string };
type Product = { id: string; title: string } & WithMeta;
جمعبندی
اگه بخوای یه قانون کلی داشته باشی: برای قراردادهای پایدار و قابلگسترش از interface استفاده کن؛ برای Union ها، ترکیب سریع و Mapped Types برو سراغ type. در عمل هم خیلی وقتها ترکیبی از هر دو جواب میده و کدت رو هم خواناتر میکنه هم انعطاف پذیرتر.
نظر تو چیه؟ تو پروژههای خودت بیشتر کِی سراغ type میری و کِی interface؟ تجربهت رو تو کامنتها بنویس تا بقیه هم استفاده کنن.
ثبت دیدگاه
اگه در مورد این مطلب نظری داری یا در همین موضوع سوالی داری، همینجا مطرح کن تا از دیدگاه ارزشمندت استفاده کنیم و انرژی بگیریم، یا سوالت رو جواب بدیم
در ضمن، شماره موبایلت تو سایت نمایش داده نمیشه و پیش ما به صورت محرمانه میمونه
کد تایید پیامک شده به شماره را وارد نمایید

دیدگاه شما