Как принцип разделения интерфейсов помогает проектировать пропсы и типы в TypeScript?
SOLID во фронтенде
ISP в TypeScript: компонент или функция объявляет в типе только то, что реально читает, — не весь `User` из API, а `Pick<User, 'name' | 'avatarUrl'>`.
Проблема толстых пропсов
Avatarпринимаетuser: Userс 30 полями, а использует два. В Storybook и тестах нужен полный мок, а изменение схемы API ломает компиляцию там, где поле даже не читается.- Компонент не переиспользовать для
AuthorилиEmployee, хотя у них те жеnameиavatarUrl.
Приёмы
- Узкие типы:
Pick,Omit, собственные интерфейсы-роли. - Структурная типизация: подходит любой объект нужной формы,
implementsне нужен. - Роли в сервисах:
Readable<T>иWritable<T>вместоRepository<T>на 12 методов. - Из стора — селектор с нужным срезом, а не весь стейт: заодно меньше лишних ререндеров.
- Обёртка над нативной кнопкой: расширить
ComponentPropsWithoutRef<'button'>, остальное передать через...rest.
interface User { id: string; name: string; avatarUrl: string; roles: string[] }
type AvatarProps = Pick<User, 'name' | 'avatarUrl'>;
function Avatar({ name, avatarUrl }: AvatarProps) {
return <img src={avatarUrl} alt={name} />;
}
const author = { name: 'Ann', avatarUrl: '/ann.png', bio: 'Editor' };
const el = <Avatar {...author} />; // не User, но подходит по формеПерегиб
Десяток типов XxxProps на один компонент, которые никто больше не использует. Сужают там, где есть второй потребитель или боль с моками.
Коротко для собеседования
Тип пропсов описывает только поля, которые компонент читает, — через Pick или узкий тип-роль. Благодаря структурной типизации такой компонент принимает любой объект подходящей формы, моки в тестах маленькие, а смена схемы API не ломает лишнего.