До сих пор бывают собеседования, где просят сравнить типы и интерфейсы, а также рассказать где и что следует использовать. Раз на такие знания есть спрос, значит надо научиться его удовлетворять. Сегодня расскажу, как отвечаю на такие вопросы я. Коротко, но достаточно подробно.
Основное правило
Если некоторая сущность может изменяться и расширяться, я буду использовать interface. Во всех остальных случаях — type. Иначе говоря, лучше использовать interface всегда, пока не потребуется что-то, что умеет только type.
Что умеют интерфейсы
Для начала скажем, что интерфейс — это контракт, который описывает как можно взаимодействовать с той или иной сущностью. К слову, описание пропсов, которые принимает тот или иной компонент — это именно инструкция по его использованию, поэтому тут выбор в пользу interface почти однозначный.
Умеют интерфейсы на самом деле не так уж и много. Их можно расширять
interface Order {
id: string;
price: number;
}
interface DiscountedOrder extends Order {
discount: number;
}
const order: DiscountedOrder = {
id: "...",
price: 100,
discount: 10,
};
Их можно декларативно сливать
interface Order {
id: string;
price: number;
}
interface Order {
discount: number;
}
const order: Order = {
id: "...",
price: 100,
discount: 10,
};
Из основного — всё. Но TS тем и хорош, что этого достаточно для решения большинства наших задач. А когда недостаточно, мы будем использовать типы.
Что умеют типы
Тип можно воспринимать как описание какой-либо более мелкой сущности. Это строительный блок, из которого в том числе складываются и интерфейсы. Однако за счет того что он более гранулярен, он обладает гораздо большими возможностями.
Типы можно объединять (union types)
type Status = "pending" | "success" | "error";
type ID = string | number;
type Response =
| { status: "success"; data: User[] }
| { status: "error"; error: string };
Типами можно описать кортежи и функции
type Point = [number, number]; // кортеж
type Callback = (data: string) => void; // функция
Можно создавать Mapped (если знаете как нормально назвать по-русски, расскажите пожалуйста) и условные типы. Тут не обошлось без дженериков, поэтому отдельно их, как возможность языка, описывать не будем.
type Nullable<T> = { [P in keyof T]: T[P] | null }; // mapped
type ExtractString<T> = T extends string ? T : never; // условный
Встроенными средствами TS можно модифицировать сущетсвующие типы и интерфейсы, создавая новые типы
type Order = {
id: string;
price: number;
discount: number;
};
// {
// id?: string;
// price?: number;
// discount?: number;
// }
type PartialOrder = Partial<Order>;
// {
// id: string;
// price: number;
// discount: number;
// }
type RequiredOrder = Required<PartialOrder>;
// {
// price: number;
// }
type Discount = Pick<Order, 'discount'>;
// {
// id: string;
// price: number;
// }
type FullPriceOrder = Omit<Order, 'discount'>
Итого
Если вы слышите вопрос «типы или интерфейсы», отвечайте «и». И то и другое встречается в каждом TS-приложении. О том как применять правильно мы коротко поговорили выше. Дальше только применять и экспериментировать. Успехов!







