Бесплатный курс по TypeScript. Зарегистрируйтесь для отслеживания прогресса →

TypeScript: Сопоставление типов (Mapped Types)

Чтобы при работе с объектными типами избежать дублирования полей и переиспользовать существующие, мы можем использовать механизм поиска типов — Lookup Types:

interface Person {
  name: string;
  age: number;
  location?: {
    country: string;
    city: string;
  };
}

interface PersonDetails {
  location: Person['location'];
}

Конструкция Type[Key] выглядит и работает так же, как получение значения объекта по ключу в JavaScript. Но доступ через точку тут не сработает.

Lookup Types позволяет также получить объединение типов из объекта по нескольким известным ключам, объединенным с помощью вертикальной черты |:

type User = {
  id: number;
  name: string;
  email: string;
}

type UserFields = User['id' | 'name' | 'email']; // string | number

Чтобы получить объединение всех ключей из объекта, мы можем использовать оператор keyof.

Упростим наш пример:

type User = {
  id: number;
  name: string;
  email: string;
}

type UserFields = User[keyof User]; // string | number

Чтобы не дублировать полностью все поля одного объектного типа, в другом используются вспомогательные типы:

  • Pick<Type, Keys> — создает объектный тип с ключами Keys из Type
  • Omit<Type, Keys> — создает объектный тип, из которого исключаются ключи Keys из Type
interface Person {
  name: string;
  age: number;
  location?: string;
}

const details: Pick<Person, 'name' | 'age'> = {
  name: 'John',
  age: 42,
};

const details2: Omit<Person, 'location'> = {
  name: 'John',
  age: 42,
};

В этом примере тип, полученный в результате Pick<Person, 'name' | 'age'> и Omit<Person, 'location'>, будет одним и тем же.

Все Utility Types в TypeScript написаны при помощи встроенных конструкций. Мы уже изучили достаточно концепций TypeScript, чтобы начать с ними разбираться. Поэтому мы можем задаться вопросом, как они реализованы.

Рассмотрим, как реализован тип Pick:

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

Pick<T, K> — дженерик тип с двумя параметрами: T и K. На K мы также наложили ограничение extends keyof T. Это означает, что K должно содержать перечисление ключей из T.

Оператор in здесь работает таким же образом, как в обычных циклах: выполняется перебор по всем элементам перечисления. В примере выше происходит перебор всех элементов в K. На каждой итерации P будет содержать текущий элемент из K. Каждый такой элемент P становится ключом, а для значения мы ищем подходящий тип в объектном типе T[P].

Операторы keyof (Lookup Types) и in (Mapped Types) часто идут рядом. С помощью keyof мы получаем доступ ко всем именам свойств объектного типа, а благодаря in можем циклически пройти по всем свойствам. Эти операции являются ключевыми при создании своих вспомогательных типов при работе с объектными типами данных.

Задание

Реализуйте функцию sanitize(), которая принимает на вход объект и массив ключей. Также она должна возвращать новый объект, но уже без указанных полей.

const user = sanitize({
  name: 'John',
  password: '1q2w3e',
  token: 'test',
}, ['password', 'token']); // { name: string }

console.log(user); // => { name: 'John' }

Обратите внимание, что в выходном типе также не должно быть этих полей.

Упражнение не проходит проверку — что делать? 😶

Если вы зашли в тупик, то самое время задать вопрос в «Обсуждениях». Как правильно задать вопрос:

  • Обязательно приложите вывод тестов, без него практически невозможно понять что не так, даже если вы покажете свой код. Программисты плохо исполняют код в голове, но по полученной ошибке почти всегда понятно, куда смотреть.
В моей среде код работает, а здесь нет 🤨

Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.

Мой код отличается от решения учителя 🤔

Это нормально 🙆, в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи.

В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.

Прочитал урок — ничего не понятно 🙄

Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в «Обсуждениях». Идеально, если вы сформулируете непонятные моменты в виде вопросов. Обычно нам нужно несколько дней для внесения правок.

Кстати, вы тоже можете участвовать в улучшении курсов: внизу есть ссылка на исходный код уроков, который можно править прямо из браузера.

Полезное


Нашли ошибку? Есть что добавить? Пулреквесты приветствуются https://github.com/hexlet-basics
Если вы столкнулись с трудностями и не знаете, что делать, задайте вопрос в нашем большом и дружном сообществе