Typescript: Использование интерфейсов
В этом уроке мы разберем использование интерфейсов. Их работа похожа на работу типов в TypeScript, однако есть и свои особенности.
Расширение интерфейса дополнительными полями
Если интерфейс необходимо расширить дополнительными полями после его инициализации, мы можем повторно объявить интерфейс с новыми свойствами. Такой способ называется слиянием деклараций:
interface IUser {
rating: number;
}
interface IUser {
nickname: string;
birthdate: number;
}
const sergey: IUser = {
nickname: "Sergey",
birthdate: 1990,
rating: 1102,
};Здесь мы создали интерфейс IUser, а затем для демонстрационных целей расширили его новыми свойствами. После этого создали на его основе объект Sergey.
Рассмотрим другой способ работы с интерфейсом.
Расширение интерфейса с помощью другого интерфейса
Также мы можем расширить интерфейс с помощью создания другого интерфейса, который наследуется от него:
interface IStudent extends IUser {
group: number;
}
const sergey: IStudent = {
nickname: "Sergey",
birthdate: 1990,
rating: 1102,
group: 2,
};В этом примере мы создали на основе нашего предыдущего интерфейса IUser еще один — IStudent, в который добавили свойство group. Так интерфейс IStudent имеет все свойства IUser и все свойства, которые мы указали при его расширении от IUser, то есть дополнительно group.
Теперь рассмотрим, как работать с несколькими интерфейсами.
Расширение нескольких интерфейсов
Еще интерфейсы могут расширять сразу несколько других интерфейсов:
interface IUser {
nickname: string;
rating: number;
}
interface IEditor {
courses: string[];
canEdit: boolean;
}
interface IAuthor extends IUser, IEditor {
team: string;
}
const sergey: IAuthor = {
nickname: "Sergey",
rating: 20,
courses: ["typescript"],
canEdit: true,
team: "Hexlet College",
};В примере выше мы создали экземпляр на основе интерфейса IAuthor, который был создан путем расширения интерфейсов IUser и IEditor. Этот экземпляр взял в себя все свойства данных интерфейсов и свойство, которое мы указали при создании самого интерфейса IAuthor.
Создание intersection types
Также TypeScript позволяет нам создавать перекрестные типы (intersection types) из нескольких интерфейсов с помощью литерала &:
interface IOneWay {
one: string;
}
interface IOrAnother {
another: string;
}
type OneWayOrAnother = IOneWay & IOrAnother;
const example: OneWayOrAnother = {
one: "A",
another: "B",
};Здесь мы создали тип OneWayOrAnother на основе двух интерфейсов при помощи литерала &. Данный тип включил в себя все свойства указанных интерфейсов.
Между созданием перекрестных типов и расширением интерфейсов нет существенных отличий. Почти всегда эти действия будут взаимозаменяемыми, поэтому это скорее вопрос удобства. Но существуют исключения: иногда расширение интерфейса ведет себя не так, как создание перекрестного типа.
Иногда мы не знаем заранее всех свойств, которые будут содержаться в нашем интерфейсе, но нам известно их возможное содержание. В таком случае удобно использовать специальную индексную сигнатуру, которая позволяет описать типы возможных значений:
interface IPhoneBook {
[index: string]: number;
}
const myNotePad: IPhoneBook = {
ivan: 55531311,
sergey: 55500110,
mom: 55522111,
};В примере выше мы создали телефонную книгу с помощью индексной сигнатуры. Это позволило нам не указывать множество свойств с именами, а всего лишь один раз указать тип ключа и тип его значения.
Так интерфейсы — это еще один мощный инструмент в TypeScript наряду с типами. Он позволяет гибко описать наши данные. Интерфейс удобно поддается расширению и объединению с другими типами или интерфейсами.
Задание
Вам даны несколько интерфейсов. На их основе создайте интерфейс ISuperman. Он должен иметь метод guessWho, принимающий и возвращающий строку.
На основе интерфейса ISuperman создайте объект superMan. Метод guessWho должен работать следующим образом: если в качестве строки в аргументе приходит любое значение кроме superman (в любом регистре), то следует вернуть предположение "It's a ${value}?", иначе "It's a ${value}!".
console.log(superMan.guessWho("bird")); // "It's a bird?";
console.log(superMan.guessWho("plane"));
("It's a plane?");
console.log(superMan.guessWho("superman"));
("It's a superman!");Полезное
Typescript: Использование интерфейсов
В этом уроке мы разберем использование интерфейсов. Их работа похожа на работу типов в TypeScript, однако есть и свои особенности.
Расширение интерфейса дополнительными полями
Если интерфейс необходимо расширить дополнительными полями после его инициализации, мы можем повторно объявить интерфейс с новыми свойствами. Такой способ называется слиянием деклараций:
interface IUser {
rating: number;
}
interface IUser {
nickname: string;
birthdate: number;
}
const sergey: IUser = {
nickname: "Sergey",
birthdate: 1990,
rating: 1102,
};Здесь мы создали интерфейс IUser, а затем для демонстрационных целей расширили его новыми свойствами. После этого создали на его основе объект Sergey.
Рассмотрим другой способ работы с интерфейсом.
Расширение интерфейса с помощью другого интерфейса
Также мы можем расширить интерфейс с помощью создания другого интерфейса, который наследуется от него:
interface IStudent extends IUser {
group: number;
}
const sergey: IStudent = {
nickname: "Sergey",
birthdate: 1990,
rating: 1102,
group: 2,
};В этом примере мы создали на основе нашего предыдущего интерфейса IUser еще один — IStudent, в который добавили свойство group. Так интерфейс IStudent имеет все свойства IUser и все свойства, которые мы указали при его расширении от IUser, то есть дополнительно group.
Теперь рассмотрим, как работать с несколькими интерфейсами.
Расширение нескольких интерфейсов
Еще интерфейсы могут расширять сразу несколько других интерфейсов:
interface IUser {
nickname: string;
rating: number;
}
interface IEditor {
courses: string[];
canEdit: boolean;
}
interface IAuthor extends IUser, IEditor {
team: string;
}
const sergey: IAuthor = {
nickname: "Sergey",
rating: 20,
courses: ["typescript"],
canEdit: true,
team: "Hexlet College",
};В примере выше мы создали экземпляр на основе интерфейса IAuthor, который был создан путем расширения интерфейсов IUser и IEditor. Этот экземпляр взял в себя все свойства данных интерфейсов и свойство, которое мы указали при создании самого интерфейса IAuthor.
Создание intersection types
Также TypeScript позволяет нам создавать перекрестные типы (intersection types) из нескольких интерфейсов с помощью литерала &:
interface IOneWay {
one: string;
}
interface IOrAnother {
another: string;
}
type OneWayOrAnother = IOneWay & IOrAnother;
const example: OneWayOrAnother = {
one: "A",
another: "B",
};Здесь мы создали тип OneWayOrAnother на основе двух интерфейсов при помощи литерала &. Данный тип включил в себя все свойства указанных интерфейсов.
Между созданием перекрестных типов и расширением интерфейсов нет существенных отличий. Почти всегда эти действия будут взаимозаменяемыми, поэтому это скорее вопрос удобства. Но существуют исключения: иногда расширение интерфейса ведет себя не так, как создание перекрестного типа.
Иногда мы не знаем заранее всех свойств, которые будут содержаться в нашем интерфейсе, но нам известно их возможное содержание. В таком случае удобно использовать специальную индексную сигнатуру, которая позволяет описать типы возможных значений:
interface IPhoneBook {
[index: string]: number;
}
const myNotePad: IPhoneBook = {
ivan: 55531311,
sergey: 55500110,
mom: 55522111,
};В примере выше мы создали телефонную книгу с помощью индексной сигнатуры. Это позволило нам не указывать множество свойств с именами, а всего лишь один раз указать тип ключа и тип его значения.
Так интерфейсы — это еще один мощный инструмент в TypeScript наряду с типами. Он позволяет гибко описать наши данные. Интерфейс удобно поддается расширению и объединению с другими типами или интерфейсами.
Задание
Вам даны несколько интерфейсов. На их основе создайте интерфейс ISuperman. Он должен иметь метод guessWho, принимающий и возвращающий строку.
На основе интерфейса ISuperman создайте объект superMan. Метод guessWho должен работать следующим образом: если в качестве строки в аргументе приходит любое значение кроме superman (в любом регистре), то следует вернуть предположение "It's a ${value}?", иначе "It's a ${value}!".
console.log(superMan.guessWho("bird")); // "It's a bird?";
console.log(superMan.guessWho("plane"));
("It's a plane?");
console.log(superMan.guessWho("superman"));
("It's a superman!");Полезное
Ваше упражнение проверяется по этим тестам
import { expect, expectTypeOf, test } from "vitest";
import { type IBird, type IPlane, type ISuperman, superMan } from "./index";
test("guess who", () => {
expect(superMan.guessWho("bird")).toBe("It's a bird?");
expect(superMan.guessWho("plane")).toBe("It's a plane?");
expect(superMan.guessWho("SupErMan")).toBe("It's a SupErMan!");
});
test("Types check", () => {
expectTypeOf<ISuperman>().toExtend<IBird>();
expectTypeOf<ISuperman>().toExtend<IPlane>();
expectTypeOf(superMan).toExtend<ISuperman>();
expect(superMan).toHaveProperty("canFly");
expect(superMan).toHaveProperty("isLiving");
expect(superMan).toHaveProperty("canCarryPeople");
});Решение учителя откроется через:
20:00
