Интерфейс описывает **контракт** — структуру объекта или класса:
interface User {
id: number
name: string
email: string
age?: number // опциональное поле
readonly createdAt: string // только чтение
}
const user: User = {
id: 1,
name: 'Алексей',
email: 'alex@mail.ru',
createdAt: '2024-01-01',
}Интерфейс может расширять другой, добавляя новые поля:
interface Animal {
name: string
age: number
}
interface Dog extends Animal {
breed: string
bark(): void
}
interface GuideDog extends Dog {
owner: string
isWorking: boolean
}
const buddy: GuideDog = {
name: 'Buddy',
age: 3,
breed: 'Labrador',
bark() { console.log('Woof!') },
owner: 'Алексей',
isWorking: true,
}Класс может **реализовывать** интерфейс — обязан иметь все его методы и свойства:
interface Printable {
print(): void
toString(): string
}
class Invoice implements Printable {
constructor(private amount: number, private client: string) {}
print() {
console.log(`Invoice for ${this.client}: ${this.amount}₽`)
}
toString() {
return `Invoice(${this.client}, ${this.amount})`
}
}Позволяют описать объект с динамическими ключами:
interface StringMap {
[key: string]: string // любой строковый ключ → строковое значение
}
interface NumberMap {
[key: string]: number
}
const translations: StringMap = {
hello: 'привет',
world: 'мир',
// можно добавлять любые ключи
}| Возможность | interface | type |
|---|---|---|
| Объектный тип | ✓ | ✓ |
| extends / implements | ✓ | с & (intersection) |
| Declaration merging | ✓ | ✗ |
| Union типы | ✗ | ✓ |
| Mapped types | ✗ | ✓ |
**Declaration merging** — можно дополнить интерфейс в другом месте кода (используется в библиотеках):
// В одном файле:
interface Request {
url: string
}
// В другом файле (расширяем):
interface Request {
user?: User // добавляем поле
}
// Итоговый Request = { url: string; user?: User }**Правило выбора**: interface для объектов которые могут наследоваться или реализовываться классами; type для union, intersection, примитивных псевдонимов и сложных mapped types.
Паттерн Repository — InMemoryUserRepo и InMemoryProductRepo реализуют единый "интерфейс" с методами getById, getAll, save, delete
// TypeScript позволяет явно объявить interface Repository<T>.
// В JavaScript симулируем контракт через документацию и runtime-проверки.
//
// interface Repository<T> {
// getById(id: number): T | undefined
// getAll(): T[]
// save(item: T): T
// delete(id: number): boolean
// }
class InMemoryUserRepo {
#store = new Map()
#nextId = 1
save(user) {
if (!user || typeof user.name !== 'string') {
throw new TypeError('User должен иметь поле name: string')
}
if (user.id) {
// update
this.#store.set(user.id, { ...user })
return this.#store.get(user.id)
}
// create
const newUser = { ...user, id: this.#nextId++ }
this.#store.set(newUser.id, newUser)
return newUser
}
getById(id) {
return this.#store.get(id)
}
getAll() {
return Array.from(this.#store.values())
}
delete(id) {
return this.#store.delete(id)
}
// Дополнительный метод (сверх интерфейса)
findByEmail(email) {
return this.getAll().find(u => u.email === email)
}
}
class InMemoryProductRepo {
#store = new Map()
#nextId = 1
save(product) {
if (!product || typeof product.name !== 'string' || typeof product.price !== 'number') {
throw new TypeError('Product должен иметь name: string и price: number')
}
if (product.id) {
this.#store.set(product.id, { ...product })
return this.#store.get(product.id)
}
const newProduct = { ...product, id: this.#nextId++ }
this.#store.set(newProduct.id, newProduct)
return newProduct
}
getById(id) {
return this.#store.get(id)
}
getAll() {
return Array.from(this.#store.values())
}
delete(id) {
return this.#store.delete(id)
}
// Дополнительный метод
findByMaxPrice(maxPrice) {
return this.getAll().filter(p => p.price <= maxPrice)
}
}
// Функция работающая с ЛЮБЫМ репозиторием через общий "интерфейс"
// В TypeScript: function printAll<T>(repo: Repository<T>): void
function printAll(repo, label) {
const items = repo.getAll()
console.log(`${label} (${items.length} шт.):`)
items.forEach(item => console.log(' ', JSON.stringify(item)))
}
// --- Демонстрация ---
const users = new InMemoryUserRepo()
const products = new InMemoryProductRepo()
console.log('=== Users Repository ===')
const alice = users.save({ name: 'Алиса', email: 'alice@mail.ru' })
const bob = users.save({ name: 'Боб', email: 'bob@mail.ru' })
console.log('Создано:', alice, bob)
users.save({ ...alice, name: 'Алиса Обновлённая' })
printAll(users, 'Пользователи')
console.log('findByEmail:', users.findByEmail('bob@mail.ru'))
users.delete(bob.id)
printAll(users, 'После удаления Боба')
console.log('\n=== Products Repository ===')
products.save({ name: 'Ноутбук', price: 75000 })
products.save({ name: 'Мышь', price: 1500 })
products.save({ name: 'Клавиатура', price: 3000 })
printAll(products, 'Продукты')
console.log('До 5000₽:', products.findByMaxPrice(5000))Интерфейс описывает **контракт** — структуру объекта или класса:
interface User {
id: number
name: string
email: string
age?: number // опциональное поле
readonly createdAt: string // только чтение
}
const user: User = {
id: 1,
name: 'Алексей',
email: 'alex@mail.ru',
createdAt: '2024-01-01',
}Интерфейс может расширять другой, добавляя новые поля:
interface Animal {
name: string
age: number
}
interface Dog extends Animal {
breed: string
bark(): void
}
interface GuideDog extends Dog {
owner: string
isWorking: boolean
}
const buddy: GuideDog = {
name: 'Buddy',
age: 3,
breed: 'Labrador',
bark() { console.log('Woof!') },
owner: 'Алексей',
isWorking: true,
}Класс может **реализовывать** интерфейс — обязан иметь все его методы и свойства:
interface Printable {
print(): void
toString(): string
}
class Invoice implements Printable {
constructor(private amount: number, private client: string) {}
print() {
console.log(`Invoice for ${this.client}: ${this.amount}₽`)
}
toString() {
return `Invoice(${this.client}, ${this.amount})`
}
}Позволяют описать объект с динамическими ключами:
interface StringMap {
[key: string]: string // любой строковый ключ → строковое значение
}
interface NumberMap {
[key: string]: number
}
const translations: StringMap = {
hello: 'привет',
world: 'мир',
// можно добавлять любые ключи
}| Возможность | interface | type |
|---|---|---|
| Объектный тип | ✓ | ✓ |
| extends / implements | ✓ | с & (intersection) |
| Declaration merging | ✓ | ✗ |
| Union типы | ✗ | ✓ |
| Mapped types | ✗ | ✓ |
**Declaration merging** — можно дополнить интерфейс в другом месте кода (используется в библиотеках):
// В одном файле:
interface Request {
url: string
}
// В другом файле (расширяем):
interface Request {
user?: User // добавляем поле
}
// Итоговый Request = { url: string; user?: User }**Правило выбора**: interface для объектов которые могут наследоваться или реализовываться классами; type для union, intersection, примитивных псевдонимов и сложных mapped types.
Паттерн Repository — InMemoryUserRepo и InMemoryProductRepo реализуют единый "интерфейс" с методами getById, getAll, save, delete
// TypeScript позволяет явно объявить interface Repository<T>.
// В JavaScript симулируем контракт через документацию и runtime-проверки.
//
// interface Repository<T> {
// getById(id: number): T | undefined
// getAll(): T[]
// save(item: T): T
// delete(id: number): boolean
// }
class InMemoryUserRepo {
#store = new Map()
#nextId = 1
save(user) {
if (!user || typeof user.name !== 'string') {
throw new TypeError('User должен иметь поле name: string')
}
if (user.id) {
// update
this.#store.set(user.id, { ...user })
return this.#store.get(user.id)
}
// create
const newUser = { ...user, id: this.#nextId++ }
this.#store.set(newUser.id, newUser)
return newUser
}
getById(id) {
return this.#store.get(id)
}
getAll() {
return Array.from(this.#store.values())
}
delete(id) {
return this.#store.delete(id)
}
// Дополнительный метод (сверх интерфейса)
findByEmail(email) {
return this.getAll().find(u => u.email === email)
}
}
class InMemoryProductRepo {
#store = new Map()
#nextId = 1
save(product) {
if (!product || typeof product.name !== 'string' || typeof product.price !== 'number') {
throw new TypeError('Product должен иметь name: string и price: number')
}
if (product.id) {
this.#store.set(product.id, { ...product })
return this.#store.get(product.id)
}
const newProduct = { ...product, id: this.#nextId++ }
this.#store.set(newProduct.id, newProduct)
return newProduct
}
getById(id) {
return this.#store.get(id)
}
getAll() {
return Array.from(this.#store.values())
}
delete(id) {
return this.#store.delete(id)
}
// Дополнительный метод
findByMaxPrice(maxPrice) {
return this.getAll().filter(p => p.price <= maxPrice)
}
}
// Функция работающая с ЛЮБЫМ репозиторием через общий "интерфейс"
// В TypeScript: function printAll<T>(repo: Repository<T>): void
function printAll(repo, label) {
const items = repo.getAll()
console.log(`${label} (${items.length} шт.):`)
items.forEach(item => console.log(' ', JSON.stringify(item)))
}
// --- Демонстрация ---
const users = new InMemoryUserRepo()
const products = new InMemoryProductRepo()
console.log('=== Users Repository ===')
const alice = users.save({ name: 'Алиса', email: 'alice@mail.ru' })
const bob = users.save({ name: 'Боб', email: 'bob@mail.ru' })
console.log('Создано:', alice, bob)
users.save({ ...alice, name: 'Алиса Обновлённая' })
printAll(users, 'Пользователи')
console.log('findByEmail:', users.findByEmail('bob@mail.ru'))
users.delete(bob.id)
printAll(users, 'После удаления Боба')
console.log('\n=== Products Repository ===')
products.save({ name: 'Ноутбук', price: 75000 })
products.save({ name: 'Мышь', price: 1500 })
products.save({ name: 'Клавиатура', price: 3000 })
printAll(products, 'Продукты')
console.log('До 5000₽:', products.findByMaxPrice(5000))Реализуй класс `Config` с методами `serialize()` (возвращает JSON строку всех настроек) и статическим методом `deserialize(json)` (создаёт новый Config из JSON строки). Класс должен хранить настройки и поддерживать методы `get(key)` и `set(key, value)`.
Используй #data = {} (обычный объект) для простоты. В get: return this.#data[key]. В set: this.#data[key] = value. В serialize: JSON.stringify(this.#data). В deserialize: JSON.parse(json) и передай в new Config(data).
Токены для AI-помощника закончились
Купи токены чтобы задавать вопросы AI прямо в уроке