Pick и Omit
Гильдия ведёт список своих участников. У каждого участника есть id — число, которое гильдия выдаёт ему при вступлении:
type HeroClass = "warrior" | "mage" | "ranger";
interface Hero {
id: number;
name: string;
heroClass: HeroClass;
level: number;
}Функция, которая добавляет нового участника, получает героя без id, потому что id выдаёт гильдия. Можно написать второй интерфейс с остальными тремя свойствами, но тогда придётся согласовывать два типа вручную. Вместо этого напишите Omit<Hero, "id">: тип Hero без свойства id. Второй аргумент типа — имя свойства, литеральный тип в кавычках. Чтобы исключить несколько свойств, запишите объединение: Omit<Hero, "id" | "level">.
type HeroClass = "warrior" | "mage" | "ranger";
interface Hero {
id: number;
name: string;
heroClass: HeroClass;
level: number;
}
let nextId = 1;
function addMember(newHero: Omit<Hero, "id">): Hero {
const hero: Hero = { id: nextId, ...newHero };
nextId++;
return hero;
}
console.log(addMember({ name: "Кира", heroClass: "ranger", level: 3 }));
console.log(addMember({ name: "Бруно", heroClass: "warrior", level: 5 }));Программа выводит:
{ id: 1, name: "Кира", heroClass: "ranger", level: 3 }
{ id: 2, name: "Бруно", heroClass: "warrior", level: 5 }Нового героя собирает spread из предыдущего упражнения: сначала id, потом все свойства newHero. В результате получаются все четыре свойства, значит, это Hero. А при вызове функции TypeScript проверяет аргумент на соответствие Omit<Hero, "id">: теперь id — ошибка, и недостающее свойство — тоже:
addMember({ id: 7, name: "Кира", heroClass: "ranger", level: 3 });
addMember({ name: "Лена", heroClass: "mage" });main.ts(18,13): error TS2353: Object literal may only specify known properties, and 'id' does not exist in type 'Omit<Hero, "id">'.
main.ts(19,11): error TS2741: Property 'level' is missing in type '{ name: string; heroClass: "mage"; }' but required in type 'Omit<Hero, "id">'.Pick работает наоборот: Pick<Hero, "name" | "level"> оставляет только перечисленные свойства, то есть это { name: string; level: number }. Он подходит функции, которой нужна только часть объекта:
function badge(hero: Pick<Hero, "name" | "level">): string {
return `${hero.name}, уровень ${hero.level}`;
}Ей можно передать целый Hero: в нём есть имя и уровень, а остального функция не замечает. Или объект поменьше, например { name: "Лена", level: 1 }. Но внутри функции есть только name и level. hero.heroClass было бы ошибкой: Property 'heroClass' does not exist on type 'Pick<Hero, "level" | "name">'. (TypeScript сортирует тексты в таком объединении по алфавиту, поэтому они могут идти в другом порядке, чем вы их написали.)
Типичные ошибки:
- Опечатка в
Pickловится, потому что имена должны быть ключамиHero. Это ограничение сkeyof, как в уроке про обобщения (generics):main.ts(10,33): error TS2344: Type '"levle" | "name"' does not satisfy the constraint 'keyof Hero'. Type '"levle"' is not assignable to type 'keyof Hero'. Did you mean '"level"'? - Опечатка в
Omitне ловится!Omit<Hero, "idd">ничего не исключает,idостаётся, и ошибки нет. Внимательно проверяйте имена вOmit. - Несколько имён — это один аргумент типа, объединение:
Pick<Hero, "name" | "level">. Если написать через запятую,Pick<Hero, "name", "level">, TypeScript сообщитGeneric type 'Pick' requires 2 type argument(s).
Задание
- 1
Под комментарием
// Добавляет нового участниканапишите функциюaddMember(newHero: Omit<Hero, "id">): Hero. Она создаёт героя сidизnextId, увеличиваетnextIdна единицу, добавляет героя вguildс помощьюpushи возвращает его. - 2
Под функцией добавьте двух участников и выведите то, что возвращает
addMember: Киру (класс "ranger", уровень 3) и Бруно (класс "warrior", уровень 5):{ id: 1, name: "Кира", heroClass: "ranger", level: 3 } { id: 2, name: "Бруно", heroClass: "warrior", level: 5 } - 3
Под ним напишите функцию
describeMember(hero: Pick<Hero, "name" | "level">): string, которая возвращает имя и уровень в таком виде:Кира (уровень 3). - 4
Под ней в цикле
for...ofвыведите то, чтоdescribeMember()возвращает для каждого участникаguild. Затем выведите ещё иdescribeMember({ name: "Лена", level: 1 }): Лены в гильдии пока нет, но функции нужны только имя и уровень. Программа заканчивается так:Кира (уровень 3) Бруно (уровень 5) Лена (уровень 1)