> For the complete documentation index, see [llms.txt](https://race-timo.gitbook.io/typescript/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://race-timo.gitbook.io/typescript/dts.md).

# \*.d.ts

## Что такое \*.d.ts файлы и когда их нужно писать

\*.d.ts файлы - это файлы декларации типов. Они содержат описание типов без реализации. Например, сигнатуры функций без тела, название и тип глобальных переменных без значений. В основном файлы декларации типов нужны для написания библиотек. Они нужны тогда, когда:

* вы написали библиотеку и хотите, чтобы ее можно было использовать не только в TypeScript проектах
* вы хотите использовать библиотеку, которая написана на JavaScript.

Рассмотрим ситуацию. Вы написали библиотеку для работы с математическим представлением вектора.

```typescript
/* .ts */
class Vector {
  static readonly zero = new Vector(0, 0);

  constructor(public readonly x: number, public readonly y: number) {
  }

  public add(vector: Vector): Vector {
    return new Vector(this.x + vector.x, this.y + vector.y);
  }
}
```

Если выложить этот код в качестве библиотеки (например, сделать и опубликовать npm-пакет), то этим кодом смогут пользоваться только проекты с настроенным TypeScript. Более того, возможно потребуется дополнительная настройка TypeScript, чтобы при компиляции проекта он компилировал и файлы нашей библиотеки.

Другой способ публикации библиотеки - скомпилировать TypeScript в JavaScript и выложить уже JavaScript файлы. В таком случаи этой библиотекой могут пользоваться любые проекты, как обычной библиотекой на JavaScript. Однако, если эту библиотеку планируется использовать в TypeScript проекте - хотелось бы сохранить типизацию. Здесь и появляются *.d.ts* файлы. Перед публикацией библиотеки мы компилируем TypeScript в JavaScript, но конфигурируем компилятор так, чтобы рядом с получившимися *.js* файлами, он создал *.d.ts* файлы по каждому файлу JavaScript-а. Далее мы публикуем и *.js* файлы и *.d.ts* файлы. Теперь, если использовать эту библиотеку в JavaScript проекте, то будут просто использоваться *.js* файлы, если использовать в TypeScript проекте, то при компиляции будут использоваться *.d.ts,* а runtime пред компилированные *.js*.

## Синтаксис декларации типов

\*.d.ts файлы можно получить автоматически или написать вручную. Для автоматической генерации нужно, чтобы библиотека изначально был на TypeScript и компилятор был настроен на генерацию файлов декларации. Например, для кода выше компилятор сгенерирует такие декларации:

```typescript
/* .d.ts */
declare class Vector {
    readonly x: number;
    readonly y: number;
    static readonly zero: Vector;
    constructor(x: number, y: number);
    add(vector: Vector): Vector;
}
```

Если библиотека изначально написана на JavaScript (или на другом языке, но не на TypeScript) или автоматическая генерация деклараций типов по TypeScript-файлам по каким-то причинам не устраивает, то файлы деклараций можно написать вручную. Возможно есть сторонние генераторы \*.d.ts файлов по JavaScript, TypeScript и другим языкам, но их мы рассматривать не будем.

Одно из главных ключевых слов в файлах деклараций - это слово `declare`. Используя это слово в какой-либо синтаксической конструкции (например, в объявлении класса), мы указываем TypeScript-у, что ее нужно интерпретировать, как декларацию. Рассмотрим два блока кода выше. При описании класса в \*.ts файле мы описывали всю сигнатуру класса, правила инициализации полей, тела конструктора и методов и модификаторы доступа. В файле декларации класс помечен словом `declare` и у него описан лишь публичный интерфейс взаимодействия с этим классом без реализации - что и с какими параметрами можно вызывать, что в итоге получится, а также к каким полям и методам есть доступ.

Рассмотрим автоматическую генерацию файлов декларации и то, как ее можно изменять. Если объявлены глобальные переменные

```typescript
/* .ts */
const globalVariable1: string = "lalaka";
let globalVariable2: number = 48;
var globalVariable3: 15 = 15;
```

то в их декларациях используется `declare` и сохраняется способ объявления, но теряется значение - оно с точки зрения проверки на типы не важно:

```typescript
/* .d.ts */
declare const globalVariable1: string;
declare let globalVariable2: number;
declare var globalVariable3: 15;
```

Однако, если явно не указывать тип переменных, то файл декларации немного изменится:

```typescript
/* .ts */
const globalVariable1 = "lalaka";
let globalVariable2 = 48;
var globalVariable3 = 15;

/* .d.ts */
declare const globalVariable1 = "lalaka";
declare let globalVariable2: number;
declare var globalVariable3: number;
```

Для переменной объявленной с использованием `const` автоматический вывод типов может вывести литеральный тип.

Для декларации функций нужно указать их сигнатуры - название, параметры и их типы и возвращаемое значение.

```typescript
/* .ts */
function foo(a: number, b: string): boolean {
    return a.toString() === b;
}

/* .d.ts */
declare function foo(a: number, b: string): boolean;
```

Если функция использует generic-и, то в декларации нужно указать и generic-параметры и их ограничения.

```typescript
/* .ts */
function foo<T extends number, R>(a: T): R {
    ...
}

/* .d.ts */
declare function foo<T extends number, R>(a: T): R;
```

Для объявления интерфейсов и type alias-ов ключевое слово `declare` можно опускать.

```typescript
/* .d.ts */
interface A {
    foo: () => void;
}

type B = {
    bar: number
}
```

Для декларации классов нужно использовать особенности декларации переменных и функций. По их подобию сделана декларация полей, методов и конструктора. Ключевое слово `declare` используется только у самого класса. Поля декларируются со всеми модификаторами, но без инициализации. Методы также декларируются со всеми модификаторами и описанной сигнатурой, но без тела.

```typescript
/* .d.ts */
declare class Foo {
    public a: number;
    private b: string;
    private e: boolean;
    constructor(q: [number, string, boolean]);
    getAll(): [number, string, boolean];
    static create(a: number): Foo;
}
```

## Публикация типов

В современном мире существует 2 способа публикации TypeScript типов.

Первый способ - включить *.d.ts ф*айлы прямо в библиотеку с *.js* файлами. Это может быть сделано по разному: можно каждый *.d.ts* файл хранить рядом с каждым соответствующим *.js* файлом, а можно внести все *.d.ts* файлы в отдельную директорию или "сбандлить" в один файл. Преимущество такого подхода в том, что пользоваться этой библиотекой в TypeScript проекте можно "из коробки". Недостаток - *.d.ts* файлы добавляют размер библиотеке. Если проект не использует TypeScript, то *.d.ts* файлы ему не нужно. Но из-за того что они встроены в библиотеку, они всегда будут приходить вместе с ней.

Второй способ - выделить типы в отдельную библиотеку. В современном TypeScript сообществе существует подход вынесения типов библиотек в отдельный пакет. Эти пакеты типов хранятся в специальном репозитории: <https://github.com/DefinitelyTyped/DefinitelyTyped>. В этом репозитории хранятся типы для многих популярных библиотек. Например, здесь хранятся типы для `React`. Типы из этой библиотеки публикуются в npm с префиксом `@types/`. Например, типы для `React` находятся в npm-пакете `@types/react`. Не обязательно выносить типы своей библиотеки в это репозиторий. Это лишь договоренность. Можно выносить свои типы в отдельный пакет с другим именем. В таком случаи нужно четко объяснить пользователю библиотеки в каком пакете они лежат. Преимущество этого подхода в том, что можно подключать типы тогда, когда они нужны. Недостаток - нужно управлять двумя (или более) пакетами. Если обновили саму библиотеку, нужно не забыть обновить типы. Типы могут не актуальными или обновляться с задержкой.

## Задания

Предположим мы написали библиотеку для работы с векторами на JavaScript. Теперь нужно написать TypeScript-типы для этой библиотеки. Библиотека состоит из одного файла: [vector.js](https://gist.github.com/byTimo/c51bc16d8c48e676da840f3f892b14c6).

[Ответ - vector.d.ts](https://gist.github.com/byTimo/128b7d45bceee2e885acaf117268c1ef)
