Расширение интерфейсов: extends
Ключевое слово
extendsв интерфейсах TypeScript создаёт новый интерфейс, наследующий все свойства родителя и добавляющий новые, что позволяет строить иерархию типов без дублирования полей.
Зачем нужно
extends позволяет строить типы от общего к частному: базовый Animal → Pet → Dog, или BaseEntity → User, Product, Order. Это исключает дублирование общих полей (id, createdAt) и устанавливает явную иерархию.
Где используется
- Иерархия доменных моделей:
BaseEntity → User,Product - Расширение DTO:
CreateUserDto → UpdateUserDto - Библиотечные типы: расширение встроенных интерфейсов (Window, Request)
- Миксин-паттерны через множественный extends
Основной контент
Базовый синтаксис
interface Animal {
name: string;
age: number;
}
interface Pet extends Animal {
owner: string;
}
interface Dog extends Pet {
breed: string;
}
// Dog содержит все поля: name, age, owner, breed
const dog: Dog = {
name: "Rex",
age: 3,
owner: "Alice",
breed: "Labrador",
};
Множественный extends
interface Serializable {
serialize: string;
}
interface Persistable {
save: Promise<void>;
delete: Promise<void>;
}
interface HasId {
id: string;
}
// Расширяем несколько интерфейсов сразу
interface Entity extends HasId, Serializable, Persistable {
createdAt: Date;
updatedAt: Date;
}
Паттерн BaseEntity
interface BaseEntity {
id: string;
createdAt: Date;
updatedAt: Date;
}
interface User extends BaseEntity {
name: string;
email: string;
role: "admin" | "user";
}
interface Product extends BaseEntity {
title: string;
price: number;
stock: number;
}
// Функция работает с любой entity
function getById<T extends BaseEntity>(
items: T,
id: string
): T | undefined {
return items.find(item => item.id === id);
}
Переопределение свойств
interface Base {
value: string | number;
label?: string;
}
// Можно сузить тип свойства (но не расширить)
interface Specialized extends Base {
value: string; // OK — string совместим с string | number
label: string; // OK — убираем опциональность (делаем строже)
}
// Нельзя расширить несовместимым типом:
// interface Wrong extends Base {
// value: boolean; // Error — boolean не совместим с string | number
// }
extends vs &
// Два способа комбинировать типы:
interface C1 extends A, B { extra: string }
type C2 = A & B & { extra: string };
// Ключевое различие:
// extends проверяет совместимость при наследовании
// & молча создаёт never при несовместимых полях
interface A2 { value: string }
interface B2 { value: number }
// interface C3 extends A2, B2 {} // Error: Types of property 'value' are incompatible
type C3 = A2 & B2; // { value: string & number } = { value: never }
Declaration merging как неявный extends
// Два объявления одного interface объединяются
interface Config { host: string }
interface Config { port: number }
// Итог: { host: string; port: number }
// Используется в Module Augmentation:
declare module "express" {
interface Request {
user?: User;
}
}
Частые ошибки
- Несовместимое переопределение свойства — нельзя расширить
value: stringдоvalue: string | number; только сузить. - Circular extends —
interface A extends B {}иinterface B extends A {}— ошибка циклической зависимости. - Путать interface extends и class extends — interface extends только копирует контракт; class extends наследует реализацию.
- Думать что extends в interface = implements — interface extends создаёт тип; класс должен явно писать
implements.
Связанные темы
- interface -- объявление и использование
- implements -- реализация интерфейсов
- Intersection Types
- interface vs type -- когда что
- _MOC TypeScript