Тернарные операторы — отличный инструмент для небольших ветвлений. Ключевое слово здесь — небольших. Как только в них начинает проникать сложная логика, код быстро теряет читаемость.
Представьте, что вы встретили в коде вот такой фрагмент:
const renderButtonLabel = (
user: User | null,
cart: Cart,
isMobile: boolean,
hasUnreadNotifications: boolean,
currentRoute: string
): string =>
(user != null && cart.items.length > 0 && !cart.hasBackorderedItems && user.balance >= cart.total && !isMobile && !hasUnreadNotifications && currentRoute !== '/checkout')
? 'Перейти к оплате'
: (user != null && cart.items.length > 0 && cart.hasBackorderedItems)
? 'Часть товаров временно недоступна'
: (user != null && cart.items.length === 0)
? 'Корзина пуста'
: (user == null)
? 'Войдите, чтобы оформить заказ'
: (isMobile || hasUnreadNotifications || currentRoute === '/checkout')
? 'Продолжить'
: 'Готово';
Чтобы понять его, приходится мысленно строить дерево как компилятор. А зачем? Ради экономии строк? Так теперь код выглядит ещё страшнее.
Более того, такой код ещё и становится хрупким. Любое изменение в нём — риск: легко нарушить логику, особенно если он не покрыт тестами.
А вот как тот же функционал может выглядеть с
if:const renderButtonLabel = (
user: User | null,
cart: Cart,
isMobile: boolean,
hasUnreadNotifications: boolean,
currentRoute: string
): string => {
if (user == null) {
return 'Войдите, чтобы оформить заказ';
}
if (cart.items.length === 0) {
return 'Корзина пуста';
}
if (cart.hasBackorderedItems) {
return 'Часть товаров временно недоступна';
}
if (
cart.items.length > 0 &&
user.balance >= cart.total &&
!isMobile &&
!hasUnreadNotifications &&
currentRoute !== '/checkout'
) {
return 'Перейти к оплате';
}
return 'Продолжить';
};
Здесь всё последовательно и без раздувания когнитивной сложности. Да и ещё для расширения списка условий не придётся ломать голову.
#неДелайТак - не усложняй.
🎤 feelin #cleanCode