Declaration files содержат **только типы** — без исполняемого кода. Они говорят TypeScript «это JavaScript-модуль с такими-то типами»:
// utils.d.ts — описание типов для utils.js
declare function formatDate(date: Date, format: string): string
declare function parseDate(str: string): Date | null
declare const DEFAULT_FORMAT: string
declare interface DateOptions {
locale?: string
timezone?: string
}declare говорит TS: «эта вещь существует, но её реализация где-то ещё»:
// Глобальная переменная (например, injected build tool)
declare const __DEV__: boolean
declare const __VERSION__: string
// Глобальная функция (из CDN-скрипта)
declare function analytics(event: string, data?: object): void
// Класс без реализации
declare class EventEmitter {
on(event: string, listener: Function): this
off(event: string, listener: Function): this
emit(event: string, ...args: any[]): boolean
}Позволяет добавлять типы к существующим модулям без изменения их исходников:
// Расширяем типы express Request
import 'express'
declare module 'express' {
interface Request {
user?: { id: number; role: string }
requestId: string
}
}
// Теперь в обработчиках req.user и req.requestId — типизированы
app.get('/profile', (req, res) => {
console.log(req.user?.id) // TypeScript знает тип
})// Добавляем типы к window в браузере
declare global {
interface Window {
analytics: (event: string) => void
__APP_CONFIG__: { apiUrl: string; version: string }
}
// Расширяем Array прототип
interface Array<T> {
last(): T | undefined
}
}
// Теперь window.__APP_CONFIG__ типизированДля популярных JS библиотек типы хранятся в DefinitelyTyped:
npm install lodash # сама библиотека (JS)
npm install @types/lodash # типы для неё (.d.ts файлы)Современные библиотеки часто включают типы сами:
// axios, zod, prisma — типы встроены, @types не нужен
import axios from 'axios' // TypeScript сразу знает все типы/// <reference types="node" />
// Говорит TS включить типы из @types/node
// В tsconfig.json:
// "typeRoots": ["./node_modules/@types", "./custom-types"]
// "types": ["node", "jest"] — только эти @types// Типы для гипотетической библиотеки my-lib
declare module 'my-lib' {
export function createClient(options: ClientOptions): Client
export interface ClientOptions {
baseUrl: string
timeout?: number
headers?: Record<string, string>
}
export interface Client {
get<T>(path: string): Promise<T>
post<T>(path: string, data: unknown): Promise<T>
}
export default createClient
}Симуляция module augmentation в JS: base объект с расширениями через Object.assign и метод createPlugin для declaration merging
// В TypeScript declaration merging позволяет расширять
// существующие модули и интерфейсы без изменения исходников.
// В JavaScript симулируем это через Object.assign и паттерн плагинов.
// Базовый объект utils (имитирует JS-библиотеку без типов)
const utils = {
version: '1.0.0',
log(message) {
console.log(`[utils] ${message}`)
},
formatDate(date) {
return date.toISOString().split('T')[0]
}
}
// "Module augmentation" — расширяем utils новыми методами
// (аналог declare module 'utils' { ... } в TypeScript)
Object.assign(utils, {
logWithTimestamp(message) {
const time = new Date().toLocaleTimeString()
console.log(`[utils ${time}] ${message}`)
},
formatCurrency(amount, currency = 'RUB') {
return `${amount.toFixed(2)} ${currency}`
}
})
// createPlugin — объединяет базовый объект с расширением
// Если метод с таким именем уже есть — вызывает оба
function createPlugin(base, extension) {
const plugin = Object.assign({}, base)
Object.keys(extension).forEach(key => {
if (typeof extension[key] === 'function' && typeof base[key] === 'function') {
// Оба метода существуют — создаём composed функцию
const originalFn = base[key]
const extensionFn = extension[key]
plugin[key] = function(...args) {
const result = originalFn.apply(this, args)
extensionFn.apply(this, args)
return result
}
} else {
plugin[key] = extension[key]
}
})
return plugin
}
// Array.prototype augmentation (добавляем метод last)
// Аналог declare global { interface Array<T> { last(): T } }
Array.prototype.last = function() {
return this[this.length - 1]
}
// --- Демонстрация ---
console.log('=== utils (после augmentation) ===')
utils.log('базовый метод работает')
utils.logWithTimestamp('расширенный метод')
console.log(utils.formatCurrency(1234.5)) // '1234.50 RUB'
console.log(utils.formatDate(new Date('2024-01-15'))) // '2024-01-15'
console.log('\n=== createPlugin ===')
const logger = {
log(msg) { console.log(`LOG: ${msg}`) },
prefix: 'app'
}
const formattingExtension = {
log(msg) { console.log(`(extension also received: ${msg})`) },
format(msg) { return `[${this.prefix.toUpperCase()}] ${msg}` }
}
const enhancedLogger = createPlugin(logger, formattingExtension)
enhancedLogger.log('hello') // LOG: hello + (extension also received: hello)
console.log(enhancedLogger.format('test')) // '[APP] test'
console.log('\n=== Array.prototype.last ===')
const nums = [1, 2, 3, 4, 5]
console.log(nums.last()) // 5
console.log([].last()) // undefinedDeclaration files содержат **только типы** — без исполняемого кода. Они говорят TypeScript «это JavaScript-модуль с такими-то типами»:
// utils.d.ts — описание типов для utils.js
declare function formatDate(date: Date, format: string): string
declare function parseDate(str: string): Date | null
declare const DEFAULT_FORMAT: string
declare interface DateOptions {
locale?: string
timezone?: string
}declare говорит TS: «эта вещь существует, но её реализация где-то ещё»:
// Глобальная переменная (например, injected build tool)
declare const __DEV__: boolean
declare const __VERSION__: string
// Глобальная функция (из CDN-скрипта)
declare function analytics(event: string, data?: object): void
// Класс без реализации
declare class EventEmitter {
on(event: string, listener: Function): this
off(event: string, listener: Function): this
emit(event: string, ...args: any[]): boolean
}Позволяет добавлять типы к существующим модулям без изменения их исходников:
// Расширяем типы express Request
import 'express'
declare module 'express' {
interface Request {
user?: { id: number; role: string }
requestId: string
}
}
// Теперь в обработчиках req.user и req.requestId — типизированы
app.get('/profile', (req, res) => {
console.log(req.user?.id) // TypeScript знает тип
})// Добавляем типы к window в браузере
declare global {
interface Window {
analytics: (event: string) => void
__APP_CONFIG__: { apiUrl: string; version: string }
}
// Расширяем Array прототип
interface Array<T> {
last(): T | undefined
}
}
// Теперь window.__APP_CONFIG__ типизированДля популярных JS библиотек типы хранятся в DefinitelyTyped:
npm install lodash # сама библиотека (JS)
npm install @types/lodash # типы для неё (.d.ts файлы)Современные библиотеки часто включают типы сами:
// axios, zod, prisma — типы встроены, @types не нужен
import axios from 'axios' // TypeScript сразу знает все типы/// <reference types="node" />
// Говорит TS включить типы из @types/node
// В tsconfig.json:
// "typeRoots": ["./node_modules/@types", "./custom-types"]
// "types": ["node", "jest"] — только эти @types// Типы для гипотетической библиотеки my-lib
declare module 'my-lib' {
export function createClient(options: ClientOptions): Client
export interface ClientOptions {
baseUrl: string
timeout?: number
headers?: Record<string, string>
}
export interface Client {
get<T>(path: string): Promise<T>
post<T>(path: string, data: unknown): Promise<T>
}
export default createClient
}Симуляция module augmentation в JS: base объект с расширениями через Object.assign и метод createPlugin для declaration merging
// В TypeScript declaration merging позволяет расширять
// существующие модули и интерфейсы без изменения исходников.
// В JavaScript симулируем это через Object.assign и паттерн плагинов.
// Базовый объект utils (имитирует JS-библиотеку без типов)
const utils = {
version: '1.0.0',
log(message) {
console.log(`[utils] ${message}`)
},
formatDate(date) {
return date.toISOString().split('T')[0]
}
}
// "Module augmentation" — расширяем utils новыми методами
// (аналог declare module 'utils' { ... } в TypeScript)
Object.assign(utils, {
logWithTimestamp(message) {
const time = new Date().toLocaleTimeString()
console.log(`[utils ${time}] ${message}`)
},
formatCurrency(amount, currency = 'RUB') {
return `${amount.toFixed(2)} ${currency}`
}
})
// createPlugin — объединяет базовый объект с расширением
// Если метод с таким именем уже есть — вызывает оба
function createPlugin(base, extension) {
const plugin = Object.assign({}, base)
Object.keys(extension).forEach(key => {
if (typeof extension[key] === 'function' && typeof base[key] === 'function') {
// Оба метода существуют — создаём composed функцию
const originalFn = base[key]
const extensionFn = extension[key]
plugin[key] = function(...args) {
const result = originalFn.apply(this, args)
extensionFn.apply(this, args)
return result
}
} else {
plugin[key] = extension[key]
}
})
return plugin
}
// Array.prototype augmentation (добавляем метод last)
// Аналог declare global { interface Array<T> { last(): T } }
Array.prototype.last = function() {
return this[this.length - 1]
}
// --- Демонстрация ---
console.log('=== utils (после augmentation) ===')
utils.log('базовый метод работает')
utils.logWithTimestamp('расширенный метод')
console.log(utils.formatCurrency(1234.5)) // '1234.50 RUB'
console.log(utils.formatDate(new Date('2024-01-15'))) // '2024-01-15'
console.log('\n=== createPlugin ===')
const logger = {
log(msg) { console.log(`LOG: ${msg}`) },
prefix: 'app'
}
const formattingExtension = {
log(msg) { console.log(`(extension also received: ${msg})`) },
format(msg) { return `[${this.prefix.toUpperCase()}] ${msg}` }
}
const enhancedLogger = createPlugin(logger, formattingExtension)
enhancedLogger.log('hello') // LOG: hello + (extension also received: hello)
console.log(enhancedLogger.format('test')) // '[APP] test'
console.log('\n=== Array.prototype.last ===')
const nums = [1, 2, 3, 4, 5]
console.log(nums.last()) // 5
console.log([].last()) // undefinedРеализуй `createPlugin(base, extension)` — принимает базовый объект и объект-расширение, возвращает новый объект с объединёнными методами. Если метод с таким именем есть в обоих — вызывает сначала оригинальный, потом новый (оба должны выполниться). Тест: базовый logger + extension с форматированием.
Скопируй base через Object.assign({}, base). Для каждого ключа extension: если оба — функции, сохрани originalFn = base[key] и extensionFn = extension[key], создай новую функцию которая вызывает оба через .apply(this, args). Иначе просто plugin[key] = extension[key].
Токены для AI-помощника закончились
Купи токены чтобы задавать вопросы AI прямо в уроке