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изTypeOmit<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 выполняется перебор по всем элементам перечисления. Каждый полученный элемент становится ключом. Для его значения мы ищем подходящий тип в объектном типе 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' }Обратите внимание, что в выходном типе также не должно быть этих полей.
Полезное
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изTypeOmit<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 выполняется перебор по всем элементам перечисления. Каждый полученный элемент становится ключом. Для его значения мы ищем подходящий тип в объектном типе 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' }Обратите внимание, что в выходном типе также не должно быть этих полей.
Полезное
Ваше упражнение проверяется по этим тестам
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).toEqualTypeOf<{ 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).toEqualTypeOf<{ page: number; limit: number }>();
});Решение учителя откроется через:
20:00
