# \*.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)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://race-timo.gitbook.io/typescript/dts.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
