Что сложное используешь в TypeScript

Ответ

Один из сложных аспектов — условные типы (Conditional Types). Они позволяют создавать типы, которые зависят от других типов. Например:

type IsString<T> = T extends string ? true : false;

type A = IsString<'hello'>; // true
type B = IsString<42>; // false

Другой пример — Mapped Types с модификаторами (readonly, ?):

type Partial<T> = {
    [P in keyof T]?: T[P];
};

interface User {
    name: string;
    age: number;
}

type PartialUser = Partial<User>; // { name?: string; age?: number }

Также сложны утилиты типа infer для вывода типов внутри условных типов:

type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

function foo() { return 42; }
type FooReturn = ReturnType<typeof foo>; // number

Ответ 18+ 🔞

Ах ты ж ёпта, ну и тема у нас сегодня, блядь! Условные типы в TypeScript — это как хитрая жопа, которая подстраивается под обстоятельства. Сидишь такой, думаешь: "А не сделать ли мне тип, который сам решит, что ему делать?" И вот они, эти самые Conditional Types, блядь!

Смотри, вот тебе простейший пример, чтобы мозг не взорвался сразу:

type IsString<T> = T extends string ? true : false;

type A = IsString<'hello'>; // true
type B = IsString<42>; // false

Видишь? Это как спрашиваешь у типа: "Мужик, ты строка?" Он такой: "Да, братан, true" или "Нет, нахуй, false". Всё просто, как три копейки, блядь.

А теперь погнали дальше, в дебри! Вот смотри на Mapped Types с этими... как их... модификаторами, сука! readonly, вопросительные знаки эти, ёбана!

type Partial<T> = {
    [P in keyof T]?: T[P];
};

interface User {
    name: string;
    age: number;
}

type PartialUser = Partial<User>; // { name?: string; age?: number }

Во, представляешь? Был у тебя тип User — жёсткий, обязательный, нихуя не пропустит. А ты взял и сделал из него PartialUser — и всё, можно поля пропускать, делать что хочешь! Магия, блядь, чистой воды! Взял все ключи из T, прошёлся по ним как по магазину и каждому поставил вопросик, типа "а может быть, а может и нет". Ёперный театр!

Но это ещё цветочки, ягодки впереди, блядь! Самое интересное — это infer. Вот это уже настоящая магия, волшебство на уровне "я тебя из шляпы достану". Смотри, не дыши:

type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

function foo() { return 42; }
type FooReturn = ReturnType<typeof foo>; // number

Вот тут-то и начинается пиздец, понимаешь? Мы говорим: "Эй, тип T, если ты функция, дай-ка мне заглянуть тебе в кишки и вытащить, что ты там возвращаешь (infer R)". И если она функция — вытаскиваем этот R (в нашем случае number), а если нет — посылаем нахуй с типом never. Вообще красота, блядь! Удивление пиздец!

Вот так вот, сидишь, ковыряешь эти условные типы, и вроде бы нихуя не понятно, а потом — бац! — и осенило. Главное — не бояться, блядь. Поначалу мозг будет вскипать, как чайник, но потом привыкнешь. Это как с собакой Муму — сначала жалко топить, а потом понимаешь, что так надо для архитектуры, сука.