Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Для чего нужен Infer в TypeScript
Infer — это ключевое слово в TypeScript для извлечения и создания новых типов из уже существующих типов. Это мощный инструмент для работы с условными типами, позволяющий делать тип-безопасное кодирование.
Основная идея
Infer позволяет сказать TypeScript: "Посмотри на этот тип и вытащи из него конкретную часть". Это напоминает деструктуризацию, но для типов.
// Без infer — нужно писать вручную
type MyArray = string[];
type Element = string; // Пришлось написать вручную
// С infer — TypeScript вытащит автоматически
type MyArray2 = string[];
type Element2 = MyArray2 extends (infer T)[] ? T : never;
// Element2 = string (TypeScript вывел автоматически)
Основной синтаксис
// Условный тип с infer
type MyType<T> = T extends Something<infer U> ? U : never;
// Читается как:
// "Если T соответствует Pattern с типом U,
// то верни U, иначе верни never"
Практические примеры
1. Извлечение типа элемента из массива
// Возвращает тип элемента из массива
type ArrayElement<T> = T extends (infer U)[] ? U : never;
type StringArray = string[];
type Element = ArrayElement<StringArray>; // string
type NumberArray = number[];
type Element2 = ArrayElement<NumberArray>; // number
// Это то же самое что Array.prototype's built-in
type MyArray = [1, 2, 3];
type MyElement = MyArray[number]; // 1 | 2 | 3
2. Извлечение типа из Promise
// Вытаскиваем тип, который вернет Promise
type Awaited<T> = T extends Promise<infer U> ? U : T;
type PromiseOfString = Promise<string>;
type StringType = Awaited<PromiseOfString>; // string
type JustString = string;
type StringType2 = Awaited<JustString>; // string (если не Promise)
// Полезно при работе с async функциями
async function fetchData() {
return { id: 1, name: 'John' };
}
type FetchResult = Awaited<ReturnType<typeof fetchData>>;
// { id: number; name: string }
3. Извлечение типа параметра функции
// Берем тип первого параметра функции
type FirstParam<T> = T extends (first: infer U, ...rest: any[]) => any
? U
: never;
const handleClick = (event: MouseEvent) => {
console.log(event);
};
type ClickParam = FirstParam<typeof handleClick>; // MouseEvent
// Работает и с конструкторами
class User {
constructor(id: number, name: string) {}
}
type UserParams = FirstParam<typeof User>; // number
4. Извлечение типа возвращаемого значения
type ReturnType<T> = T extends (...args: any[]) => infer R
? R
: never;
const getData = (): { id: number; title: string } => ({
id: 1,
title: 'Test'
});
type DataType = ReturnType<typeof getData>;
// { id: number; title: string }
// Встроенный ReturnType делает то же самое
type DataType2 = ReturnType<typeof getData>;
5. Извлечение из Union типов
// Вытаскиваем конкретный элемент из объединения (Union)
type Flatten<T> = T extends Array<infer U> ? U : T;
type MyType = Flatten<string[]>; // string
type MyType2 = Flatten<string>; // string (если не массив)
// Более сложный пример: вытащить все типы из Union
type Union = string | number | boolean;
// К сожалению, нельзя вытащить "все" из Union одной строкой
// Нужно использовать распределенные условные типы
type UnwrapArray<T> = T extends Array<infer U> ? U : T;
type Numbers = UnwrapArray<number[]>; // number
type Strings = UnwrapArray<string[]>; // string
Распределенные условные типы (Distributive Conditional Types)
Это продвинутая возможность, которая применяется к Union типам:
// Условные типы распределяются для каждого члена Union
type Flatten<T> = T extends Array<infer U> ? U : T;
type StringOrNumber = string | number;
type Result = Flatten<(string | number)[]>;
// Result = string | number (применяется к каждому члену Union)
// Пример: извлечь только строки из Union
type ExtractString<T> = T extends string ? T : never;
type Mixed = string | number | boolean;
type OnlyString = ExtractString<Mixed>; // string (остальные стали never)
// Это работает потому, что Union распределяется:
// ExtractString<string> | ExtractString<number> | ExtractString<boolean>
// = string | never | never
// = string
React примеры
Пример 1: Извлечение props типа компонента
import { FC, ComponentProps } from 'react';
interface ButtonProps {
onClick: () => void;
disabled?: boolean;
children: React.ReactNode;
}
const Button: FC<ButtonProps> = ({ onClick, disabled, children }) => {
return <button onClick={onClick}>{children}</button>;
};
// Вытащить тип props из компонента
type ButtonPropsType = ComponentProps<typeof Button>;
// = ButtonProps
Пример 2: Строго типизированный useSelector для Redux
import { useSelector } from 'react-redux';
import { AppState } from './store';
// Функция, которая возвращает строго типизированный selector
type AppDispatch = typeof store.dispatch;
type RootState = ReturnType<typeof store.getState>;
// Теперь при использовании
const user = useSelector((state: RootState) => state.user);
// TypeScript знает, что это типа User
Пример 3: Условный рендеринг с типизацией
type ExtractArrayType<T> = T extends Array<infer U> ? U : T;
interface Props<T> {
items: T[];
render: (item: ExtractArrayType<T[]>) => React.ReactNode;
}
const List = <T,>({ items, render }: Props<T>) => {
return <ul>{items.map((item) => <li key={item.id}>{render(item)}</li>)}</ul>;
};
// Использование: TypeScript знает, что item имеет тип из массива
<List
items={[{ id: 1, name: 'John' }]}
render={(item) => item.name} // item правильно типизирован
/>
Утилиты TypeScript, использующие Infer
В стандартной библиотеке TypeScript множество утилит построены на Infer:
// ReturnType — извлекает возвращаемый тип
type MyReturn = ReturnType<() => string>; // string
// Parameters — извлекает типы параметров в кортеж
type MyParams = Parameters<(a: string, b: number) => void>;
// [a: string, b: number]
// ConstructorParameters — параметры конструктора
type UserParams = ConstructorParameters<typeof User>;
// [id: number, name: string]
// InstanceType — тип экземпляра класса
type UserInstance = InstanceType<typeof User>; // User
// Awaited — распаковка Promise (добавлено в TypeScript 4.5)
type PromiseResult = Awaited<Promise<Promise<string>>>; // string
Сложный пример: Строго типизированный fetch
// Функция, которая вытаскивает ожидаемый тип ответа
type ExtractResponse<T> = T extends { response: infer R } ? R : never;
interface ApiEndpoint {
request: { id: number };
response: { user: string };
}
// Теперь нашу функцию
function request<T extends ApiEndpoint>(
endpoint: T,
data: T['request']
): Promise<ExtractResponse<T>> {
// ...
return fetch('/api', { body: JSON.stringify(data) }).then(r => r.json());
}
// Использование: TypeScript знает точный тип ответа
const userApi: ApiEndpoint = {
request: { id: 1 },
response: { user: 'John' }
};
const result = await request(userApi, { id: 1 });
// result имеет тип { user: string }
Когда НЕ использовать Infer
// СЛИШКОМ СЛОЖНО: если код становится нечитаемым
type TooComplex<T> = T extends {
a: infer A extends { b: infer B extends { c: infer C } }
} ? [A, B, C] : never;
// ЛУЧШЕ: разбить на части
type GetA<T> = T extends { a: infer A } ? A : never;
type GetB<T> = T extends { b: infer B } ? B : never;
type GetC<T> = T extends { c: infer C } ? C : never;
Отладка Infer типов
// Если ты не уверен, что вытащит infer, используй
type Debug<T> = T;
type MyResult = Debug<ExtractResponse<ApiEndpoint>>;
// Наведи мышку в IDE и увидишь точный тип
// Или используй console trick (работает в VS Code Intellisense)
type Unwrap<T> = T extends Promise<infer U> ? U : T;
type Test = Unwrap<Promise<string>>;
// Используй "Go to Definition" чтобы видеть результат
Вывод
Infer нужен для:
- Извлечения типов из сложных структур (массивы, Promise, функции)
- Условных типов — создания типов на основе других типов
- Обобщенного кода — функции, которые работают с любыми типами, но сохраняют строгость
- Утилит типов — создания удобных хелперов для типизации
Используй Infer когда:
- Нужно работать с типами функций/классов, которые варьируются
- Хочешь сделать код более универсальным без потери типобезопасности
- Работаешь с библиотеками, которые требуют извлечения вложенных типов
- Создаешь переиспользуемые типовые утилиты
Интересующимся: исследуй встроенные утилиты TypeScript (ReturnType, Parameters, Awaited) — они все используют Infer!