Как расширить типы в TypeScript: Полное руководство
Узнайте, как правильно расширять типы в TypeScript с помощью операторов пересечения, ключевого слова extends и паттернов наследования. Руководство с примерами.
Как расширить типы в TypeScript?
У меня есть следующий тип TypeScript:
type Event = {
name: string;
dateCreated: string;
type: string;
}
Я хочу расширить этот тип, чтобы создать новый тип с дополнительными свойствами:
type UserEvent extends Event = {
UserId: string;
}
Однако такой синтаксис не работает. Как правильно расширить тип TypeScript в этом случае?
TypeScript не позволяет тип‑алиасам напрямую использовать ключевое слово extends, как это делают интерфейсы. Чтобы расширить тип Event, необходимо использовать оператор пересечения (&), чтобы объединить базовый тип с новыми свойствами.
Содержание
- Базовое расширение типов с помощью операторов пересечения
- Расширение интерфейсов с ключевым словом
extends - Наследование классов в TypeScript
- Множественное наследование и объединение деклараций
- Продвинутые шаблоны расширения типов
- Лучшие практики расширения типов
Базовое расширение типов с помощью операторов пересечения
Для тип‑алиасов правильный подход — использовать оператор пересечения (&), чтобы объединить базовый тип с вашими новыми свойствами. Вот как исправить ваш пример:
type Event = {
name: string;
dateCreated: string;
type: string;
}
// Правильный способ расширить тип‑алиас
type UserEvent = Event & {
UserId: string;
}
Это создаёт тип UserEvent, который включает все свойства из Event плюс дополнительное свойство UserId. Как объясняет bobbyhadz.com, «оператор & и ключевое слово extends позволяют копировать члены других именованных типов и добавлять новые члены для создания нового типа».
Теперь вы можете использовать этот тип как ожидается:
const userEvent: UserEvent = {
name: "Tech Conference",
dateCreated: "2024-01-15",
type: "conference",
UserId: "user123"
}
Подход с пересечением является стандартным способом достижения расширения типов с тип‑алиасами в TypeScript.
Расширение интерфейсов с ключевым словом extends
Если вы предпочитаете использовать ключевое слово extends, вы можете преобразовать ваш тип в интерфейс:
interface Event {
name: string;
dateCreated: string;
type: string;
}
// Интерфейс может напрямую использовать extends
interface UserEvent extends Event {
UserId: string;
}
Согласно документации TypeScript, интерфейсы «могут быть расширены с помощью ключевого слова extends». Это естественный способ создать поведение, похожее на наследование, в TypeScript.
Ключевое преимущество использования интерфейсов заключается в поддержке объединения деклараций — вы можете добавлять новые свойства к существующему интерфейсу в разных декларациях:
interface Event {
name: string;
dateCreated: string;
type: string;
}
// Позже в вашем коде вы можете расширить тот же интерфейс
interface Event {
organizer?: string;
}
interface UserEvent extends Event {
UserId: string;
}
Наследование классов в TypeScript
Для классов вы также можете использовать ключевое слово extends, чтобы создать наследование:
class Event {
constructor(
public name: string,
public dateCreated: string,
public type: string
) {}
}
class UserEvent extends Event {
constructor(
name: string,
dateCreated: string,
type: string,
public UserId: string
) {
super(name, dateCreated, type);
}
}
Как объясняет TypeScript Handbook, «этот пример демонстрирует базовую функцию наследования: классы наследуют свойства и методы от базовых классов. Здесь Dog является производным классом, который наследует от базового класса Animal с помощью ключевого слова extends».
При расширении классов необходимо вызывать конструктор родительского класса с помощью super() в конструкторе дочернего класса.
Множественное наследование и объединение деклараций
Интерфейсы могут наследовать несколько интерфейсов с помощью ключевого слова extends:
interface Person {
name: string;
age: number;
}
interface Address {
street: string;
city: string;
}
interface Employee extends Person, Address {
jobTitle: string;
}
const employee: Employee = {
name: "John Doe",
age: 30,
street: "123 Main St",
city: "Anytown",
jobTitle: "Developer"
};
Однако тип‑алиасы не могут напрямую использовать множественное наследование. Вам понадобится цепочка операторов пересечения:
type Person = {
name: string;
age: number;
}
type Address = {
street: string;
city: string;
}
type Employee = Person & Address & {
jobTitle: string;
}
Продвинутые шаблоны расширения типов
Обобщенные типы
Вы можете создавать обобщенные типы, которые расширяют базовые типы:
type BaseEvent<T> = {
id: string;
data: T;
timestamp: Date;
}
type UserEvent = BaseEvent<User> & {
UserId: string;
}
type User = {
username: string;
email: string;
}
Условные типы
Ключевое слово extends также используется в условных типах:
type IsString<T> = T extends string ? true : false;
type Test1 = IsString<string>; // true
type Test2 = IsString<number>; // false
Частичное расширение
Вы можете создавать частичные расширения с помощью встроенных типов:
type OptionalUserId = Partial<UserEvent>;
const partialEvent: OptionalUserId = {
name: "Event",
type: "meeting"
// UserId является необязательным
}
Лучшие практики расширения типов
-
Выбирайте подходящий инструмент: используйте интерфейсы, когда вам нужна объединение деклараций или вы хотите использовать
extends. Используйте тип‑алиасы для объединений, пересечений или когда нужна более сложная манипуляция типами. -
Предпочитайте композицию над наследованием: вместо глубоких иерархий наследования рассмотрите композицию с помощью операторов пересечения:
type EventLogger = Event & {
logger: Logger;
}
type EventValidator = Event & {
validator: Validator;
}
-
Будьте внимательны к объединению деклараций: помните, что интерфейсы могут быть расширены в нескольких местах, что может привести к неожиданному поведению, если не управлять этим аккуратно.
-
Используйте встроенные типы: TypeScript предоставляет встроенные типы, такие как
Partial,Pick,OmitиRecord, которые могут помочь при расширении типов в типичных сценариях. -
Учитывайте производительность: сложные типы пересечения иногда могут влиять на скорость проверки типов, особенно в больших кодовых базах.
Заключение
Чтобы расширить типы в TypeScript, у вас есть несколько вариантов в зависимости от конкретной задачи:
- Для тип‑алиасов: используйте оператор пересечения (
&) для объединения типов. - Для интерфейсов: используйте ключевое слово
extendsнапрямую. - Для классов: используйте ключевое слово
extendsс правильными вызовамиsuper().
Главный вывод — тип‑алиасы не могут напрямую использовать ключевое слово extends, но типы пересечения предоставляют эквивалентную функциональность. При работе с сложными иерархиями типов рассмотрите композицию вместо глубокого наследования и воспользуйтесь встроенными типами TypeScript для распространённых шаблонов расширения.
Помните, что интерфейсы и тип‑алиасы служат разным целям — интерфейсы лучше подходят для описания объектов, которые могут потребоваться расширить позже, тогда как тип‑алиасы превосходят в сложных манипуляциях типами и в сценариях объединения/пересечения.
Источники
- How to extend a Type in TypeScript | bobbyhadz
- TypeScript: Handbook - Classes
- Extending types in TypeScript | Graphite
- TypeScript Extend Interface | TypeScript Tutorial
- TypeScript Type Aliases and Interfaces | W3Schools
- Three Ways of Using “extends” in TypeScript | DEV Community
- Interfaces vs Type Aliases: what’s the difference? | YouTube