Как пройти собеседование по TypeScript: топ вопросов продвинутого уровня

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Как пройти собеседование по TypeScript: топ вопросов продвинутого уровня

Собеседование на позицию Senior Frontend или Fullstack разработчика редко обходится без глубоких вопросов по TypeScript. Если базовые типы и интерфейсы вы знаете назубок, то для прохождения technical interview на высокий грейд нужно разбираться в продвинутой системе типов. В этой статье мы собрали топ-5 вопросов и задач, которые часто задают на собеседованиях по TypeScript продвинутого уровня. Каждый пункт сопровождается разбором и примерами кода.

1. Условные типы (Conditional Types) и ключевое слово infer

Условные типы позволяют создавать типы, которые зависят от условия. В сочетании с infer вы можете "извлекать" части типов, как в Pattern Matching.

Вопрос: Напишите тип ReturnType (без использования встроенного).

Разбор: Используем условный тип с infer для захвата возвращаемого значения функции.

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

// Пример использования
function greet(name: string): string {
  return `Hello, ${name}`;
}

type GreetReturn = MyReturnType<typeof greet>; // string

Как ответить на собеседовании: Объясните, что infer R создает новую переменную типа R, которая захватывает возвращаемый тип из сигнатуры функции. Если условие не выполняется (передан не функция), тип становится never.

2. Mapped Types (Маппированные типы) и модификаторы

Mapped types позволяют создавать новые типы, преобразуя свойства существующего. Продвинутый уровень подразумевает работу с модификаторами +, -, as и ключами через keyof.

Вопрос: Создайте тип DeepReadonly, который делает все свойства объекта (включая вложенные) readonly.

type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends Record<string, unknown> 
    ? DeepReadonly<T[K]> 
    : T[K];
};

interface User {
  name: string;
  address: {
    city: string;
    zip: number;
  };
}

type ReadonlyUser = DeepReadonly<User>;
// Все свойства стали readonly, в том числе address.city

Как ответить: Покажите рекурсивный вызов DeepReadonly для вложенных объектов. Объясните, как Record<string, unknown> используется для проверки, является ли значение объектом.

3. Template Literal Types (Шаблонные строковые типы)

Template literal types позволяют манипулировать строковыми типами на уровне компиляции. Это мощный инструмент для создания строгих API.

Вопрос: Реализуйте тип EventName, который принимает строку и возвращает тип "on{Строка}" с правильным Capitalize.

type EventName<T extends string> = `on${Capitalize<T>}`;

type ClickEvent = EventName<'click'>; // "onClick"
type FocusEvent = EventName<'focus'>; // "onFocus"

// Продвинутый пример: извлечение из строки
type ExtractAction<T extends string> = 
  T extends `on${infer Action}` ? Uncapitalize<Action> : never;

type Action = ExtractAction<'onSubmit'>; // "submit"

Как ответить: Обратите внимание на встроенные типы Capitalize и Uncapitalize. Покажите, как infer работает внутри шаблонных литералов для парсинга строк.

4. Дженерики с ограничениями и вариативными кортежами (Variadic Tuple Types)

Variadic tuple types (появились в TS 4.0) позволяют работать с кортежами произвольной длины. Это незаменимо при создании типобезопасных функций с переменным числом аргументов.

Вопрос: Напишите тип для функции concat, которая объединяет два кортежа в один.

type Concat<T extends readonly unknown[], U extends readonly unknown[]> = [...T, ...U];

// Пример
type Result = Concat<[1, 2], [3, 4]>; // [1, 2, 3, 4]

// Реальная функция
function concatTuples<T extends unknown[], U extends unknown[]>(
  arr1: readonly [...T],
  arr2: readonly [...U]
): Concat<T, U> {
  return [...arr1, ...arr2] as Concat<T, U>;
}

const result = concatTuples([1, 'a'] as const, [true, 2] as const);
// Тип: readonly [1, "a", true, 2]

Как ответить: Акцентируйте, что spread в типах работает так же, как в значениях. Покажите использование as const для сохранения точных литеральных типов.

5. Типы-утилиты: Pick, Omit, Exclude и их кастомные реализации

На собеседовании часто просят не просто использовать встроенные утилиты, но и реализовать их вручную. Это проверяет понимание mapped types и условных типов.

Вопрос: Реализуйте MyPick<T, K> и MyExclude<T, U>.

// MyPick
type MyPick<T, K extends keyof T> = {
  [P in K]: T[P];
};

// MyExclude
type MyExclude<T, U> = T extends U ? never : T;

// MyOmit через комбинацию
type MyOmit<T, K extends keyof T> = MyPick<T, MyExclude<keyof T, K>>;

interface Person {
  name: string;
  age: number;
  email: string;
}

type WithoutEmail = MyOmit<Person, 'email'>; // { name: string; age: number }

Как ответить: Объясните, что MyExclude работает на уровне union types (дистрибутивность условных типов). MyPick — это классический mapped type с фильтрацией ключей через K extends keyof T.

Заключение

Продвинутый

Блоги

Рекомендаций по книгам