← Назад к вопросам

Переопределял ли типы с помощью деклараций

2.0 Middle🔥 71 комментариев
#TypeScript

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI4 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Да, я много раз переопределял и расширял типы с помощью деклараций в TypeScript. Это мощный инструмент для адаптации сторонних библиотек, исправления неточностей или добавления кастомной логики типов в проектах.

Зачем нужны декларации типов?

Декларации (.d.ts файлы) позволяют:

  • Исправлять некорректные или устаревшие типы в сторонних библиотеках.
  • Добавлять типы для библиотек, у которых их нет изначально.
  • Расширять существующие интерфейсы глобальных объектов или модулей.
  • Объявлять глобальные переменные, доступные в проекте.

Основные сценарии переопределения типов

1. Исправление типов в сторонних библиотеках

Иногда типы в @types/package могут быть неточными или неполными. Вместо создания пулл-реквеста в DefinitelyTyped можно временно исправить тип локально.

// fix-third-party.d.ts
import 'some-library';

declare module 'some-library' {
  // Переопределяем некорректный тип функции
  export function someFunction(value: string): number; // Было: any
}

2. Расширение глобальных интерфейсов

Часто требуется добавить методы к стандартным интерфейсам, например, к Window или Array.

// global.d.ts
interface Window {
  myCustomProperty: string;
  __MY_APP_STATE__: Record<string, any>;
}

interface Array<T> {
  // Добавляем метод, который может быть полифиллом
  findLast(predicate: (value: T) => boolean): T | undefined;
}

// Использование в коде
window.myCustomProperty = 'test';
const lastEven = [1, 2, 3, 4].findLast(n => n % 2 === 0); // 4

3. Объявление модулей для CSS/изображений

При использовании Webpack или других сборщиков часто нужно объявить типы для импорта не-TS файлов.

// assets.d.ts
declare module '*.css' {
  const content: { [className: string]: string };
  export default content;
}

declare module '*.png' {
  const value: string;
  export default value;
}

declare module '*.svg' {
  import React from 'react';
  const ReactComponent: React.FC<React.SVGProps<SVGSVGElement>>;
  export default ReactComponent;
}

4. Мерж (слияние) интерфейсов из разных источников

Когда библиотека предоставляет несколько точек входа с разными типами, их можно объединить.

// merge-types.d.ts
import { OriginalType } from 'original-library';

declare module 'original-library/extended' {
  interface ExtendedType extends OriginalType {
    newMethod(): void;
  }
  
  export { ExtendedType };
}

Практический пример: расширение Express Request

В Node.js с Express часто нужно добавлять кастомные поля к объекту запроса.

// types/express.d.ts
import { Request } from 'express';

declare global {
  namespace Express {
    interface Request {
      user?: {
        id: string;
        role: string;
      };
      requestId: string;
    }
  }
}

// Использование в middleware
app.use((req, res, next) => {
  req.user = { id: '123', role: 'admin' };
  req.requestId = crypto.randomUUID();
  next();
});

Важные нюансы и лучшие практики

  • Модульное объявление vs глобальное: Используйте declare module для библиотек, а глобальные объявления — для встроенных объектов.
  • Объединение (мерж) интерфейсов: TypeScript автоматически мержит интерфейсы с одинаковыми именами в одной области видимости.
  • Не злоупотребляйте any: Старайтесь указывать конкретные типы даже в декларациях.
  • Изоляция деклараций: Создавайте отдельные .d.ts файлы для разных категорий объявлений.
  • Проверка на конфликты: Убедитесь, что ваши расширения не конфликтуют с будущими обновлениями библиотек.
  • Используйте tsconfig.json: Укажите путь к декларациям в typeRoots или include.
{
  "compilerOptions": {
    "typeRoots": ["./types", "./node_modules/@types"]
  },
  "include": ["src/**/*", "types/**/*"]
}

Альтернативы полному переопределению

Иногда достаточно:

  • Тип утверждения (type assertion): (value as MyType).method()
  • Дженерики с ограничениями: <T extends BaseInterface>
  • Утилиты типов: Partial, Omit, Pick для создания вариаций существующих типов.

Переопределение типов через декларации — это продвинутая, но необходимая техника в реальных проектах на TypeScript. Она требует понимания системы типов TypeScript, но дает полный контроль над типизацией в проекте, особенно при интеграции со сторонним кодом или глобальными расширениями. Главное — документировать такие переопределения и следить за их актуальностью при обновлении зависимостей.