Logo
Книга для начинающих
ВходРегистрация
/
Программирование
/
Курс Typescript
/

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

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' }

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

Коммерческий опыт и Трудоустройство

Полезное

  • Официальная документация Mapped Types

  • Официальная документация keyof

  • Understanding "keyof typeof"

  • Официальная документация Pick

  • Официальная документация Omit

Команда проекта находится в телеграм-сообществе. Там можно задать любой вопрос и повлиять на проект

Если вы зашли в тупик, то самое время поговорить с нашим асситентом Тота во вкладке "ИИ-помощник":

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

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

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

Нашли ошибку? Есть что добавить? Пулреквесты приветствуются
/
Программирование
/
Курс Typescript
/

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

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' }

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

Коммерческий опыт и Трудоустройство

Полезное

  • Официальная документация Mapped Types

  • Официальная документация keyof

  • Understanding "keyof typeof"

  • Официальная документация Pick

  • Официальная документация Omit

Команда проекта находится в телеграм-сообществе. Там можно задать любой вопрос и повлиять на проект

Если вы зашли в тупик, то самое время поговорить с нашим асситентом Тота во вкладке "ИИ-помощник":

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

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

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

Нашли ошибку? Есть что добавить? Пулреквесты приветствуются
← ПредыдущийСледующий →
← ПредыдущийСледующий →
← ПредыдущийСледующий →

Ваше упражнение проверяется по этим тестам

import { expect, expectTypeOf, test } from 'vitest';

import sanitize from './index';

test('sanitize', () => {
  const obj = {
    name: 'John',
    age: 30,
    password: '123456',
  };

  expect(sanitize(obj, ['name', 'age'])).toEqual({
    password: '123456',
  });

  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  const user = sanitize(obj, ['password']);

  expectTypeOf(user).toExtend<{ name: string; age: number }>();

  const params = {
    page: 1,
    limit: 10,
    filter: {
      name: 'John',
    },
  };

  const query = sanitize(params, ['filter']);
  expect(query).toEqual({
    page: 1,
    limit: 10,
  });

  expectTypeOf(query).toExtend<{ page: number; limit: number }>();
});
← ПредыдущийСледующий →

Решение учителя откроется через:

20:00

waiting_clock
← ПредыдущийСледующий →