Declaration files (.d.ts)

Файлы деклараций -- описание типов для JS-библиотек без исходников на TS.

Зачем нужно

  • Типизация сторонних JS-библиотек
  • Автодополнение и проверка типов при использовании npm-пакетов
  • Публикация типов для своей библиотеки

Где используется

  • Подключение JS-библиотек в TS-проект, написание npm-пакетов

Как работает

TypeScript ищет типы в таком порядке:

  1. .ts файл рядом с импортом
  2. "types" поле в package.json библиотеки
  3. @types/имя-пакета из DefinitelyTyped
  4. Ручной .d.ts файл

Установка типов

npm i -D @types/lodash
npm i -D @types/express
npm i -D @types/node

Написание своего .d.ts

// global.d.ts -- расширение глобальных типов
declare global {
  interface Window {
    __APP_VERSION__: string;
  }
}
export {};

// module.d.ts -- декларация модуля
declare module 'my-lib' {
  export function doSomething(input: string): number;
  export interface Config {
    debug: boolean;
    timeout: number;
  }
}

// ambient declaration для CSS modules
declare module '*.module.css' {
  const classes: Record<string, string>;
  export default classes;
}

Генерация .d.ts из TS

{
  "compilerOptions": {
    "declaration": true,
    "declarationDir": "./dist/types",
    "emitDeclarationOnly": true
  }
}
npx tsc --emitDeclarationOnly

Альтернативная позиция: JS-файл + рядом .d.ts

"Программируйте на TypeScript как на C++: код — отдельно, заголовки (.d.ts) — отдельно."

Параллель с Delphi (PAS+DFM) и C++ (.cpp+.h). Автор TS — Андерс Хейлсберг — создал и Delphi, и C#, но почему-то не принёс культуру разделения в TS.

// units.js — реализация на чистом JS
export const bytesToSize = (bytes) => { /* ... */ };
// units.d.ts — контракт рядом
export function bytesToSize(bytes: number): string;

Почему это лучше для больших проектов

  • Контракт компактно — весь API EventEmitter помещается на один экран в .d.ts. Без .d.ts нужно листать десятки страниц кода.
  • Читая типы — видишь контракт. Реализация не отвлекает.
  • Менее точная типизация — это ок. В TS-файле типы должны быть максимально точны (они привязаны к коду). В .d.ts можно описать только то, что вызывающий должен знать.
  • JS работает без сборки. TS-проверка — отдельный шаг пайплайна.

Обязательные настройки

{
  "compilerOptions": {
    "allowJs": true,
    "checkJs": true,
    "noEmit": true,     // КРИТИЧНО — иначе перезапишет ваш JS
    "declaration": false
  }
}

Подводные камни

  • Конфликты имён. Если у вас есть и units.ts, и units.js + units.d.ts — TS запутается. Выбирайте один путь на модуль.
  • Гранулярность. .d.ts можно делать на файл, на папку (через index.d.ts) или на весь проект (один глобальный).
  • Достаточно типизировать 2-3 ключевых идентификатора. Не нужно покрывать всё.

Связанные темы

Источники

  • 🎓 TypeScript vs JavaScript: как лучше писать типы · 2025-11-08 · YouTube
    • Позиция автора: для больших проектов — JS + .d.ts, по образцу .h в C++.

Ресурсы