$ sudo teach IT
МОДУЛЬ 9 · УРОК 6

@types

DefinitelyTyped, npm @types, установка и использование

📖 Введение

Представьте, что вы приходите в магазин электроники. Вы хотите купить телефон, но не знаете его характеристик. Вам нужен каталог, который описывает все модели: какие функции есть, какой размер экрана, какой объём памяти. Без каталога вы не сможете выбрать подходящий телефон.

@types — это такой каталог для JavaScript-библиотек. Это пакеты типов, которые описывают API существующих библиотек. Они хранятся в репозитории DefinitelyTyped и устанавливаются через npm.

Например, вы хотите использовать lodash в TypeScript-проекте. Сами типы уже написаны — просто установите пакет @types/lodash, и TypeScript сразу будет знать типы всех функций.

1 Что такое DefinitelyTyped — репозиторий типов

DefinitelyTyped (github.com/DefinitelyTyped/DefinitelyTyped) — это крупнейший репозиторий типов для JavaScript-библиотек. Он содержит тысячи пакетов типов для популярных библиотек:

  • @types/lodash — типы для Lodash
  • @types/express — типы для Express.js
  • @types/react — типы для React
  • @types/node — типы для Node.js API
  • @types/jest — типы для Jest
  • @types/jquery — типы для jQuery
  • @types/mocha — типы для Mocha
  • @types/chai — типы для Chai

Важно: Начиная с TypeScript 4.7, многие библиотеки включают типы внутри себя (в пакете). Например, axios уже содержит index.d.ts. В таких случаях @types/axios не нужен.

2 Установка @types — npm install

Установка пакетов типов выполняется через npm:

# Установка типов для lodash
npm install --save-dev @types/lodash

# Установка типов для Express
npm install --save-dev @types/express

# Установка типов для Node.js
npm install --save-dev @types/node

# Установка типов для Jest
npm install --save-dev @types/jest

# Установка нескольких пакетов сразу
npm install --save-dev @types/lodash @types/express @types/node

Обратите внимание: типы устанавливаются как --save-dev — это dev-зависимости, потому что они нужны только при разработке, а не в продакшене.

После установки TypeScript автоматически находит типы в node_modules/@types:

import _ from 'lodash';
import express from 'express';

// TypeScript теперь знает типы!
const numbers = [1, 2, 3, 4, 5];
const doubled = _.map(numbers, (n) => n * 2);  // number[]

const app = express();
app.get('/', (req, res) => {  // req: Request, res: Response
  res.send('Привет, мир!');
});

3 Как проверить наличие типов —три типа способа

Есть три способа проверить, есть ли типы для библиотеки:

1. Проверить пакет на npmjs.com:

Зайдите на npmjs.com/package/имя_пакета и посмотрите в поле «Types» или «typings». Если там есть путь — типы встроены.

2. Проверить в npm search:

# Искать @types пакет
npm search @types/lodash

# Или проверить, установлен ли уже
npm list @types/lodash

# Проверить, содержит ли пакет встроенные типы
npm info lodash types

3. Посмотреть в DefinitelyTyped:

Зайдите на github.com/DefinitelyTyped/DefinitelyTyped и найдите папку с именем вашей библиотеки.

4 Когда @types не нужен — встроенные типы

Многие современные библиотеки включают типы внутри себя. В таких случаях отдельный пакет @types не нужен:

  • axios — уже содержит типы
  • date-fns — уже содержит типы
  • zod — уже содержит типы
  • react — уже содержит типы
  • vue — уже содержит типы
  • typescript — уже содержит типы

Как проверить: Посмотрите в node_modules/имя_пакета. Если там есть index.d.ts или поле "types" в package.json — типы встроены.

5 Создание своего @types пакета — для внутренних библиотек

Если вы пишете собственную библиотеку на JavaScript и хотите предоставить типы для TypeScript-пользователей, создайте @types пакет:

# Создайте папку для типов
mkdir my-lib-types
cd my-lib-types

# Инициализируйте npm-проект
npm init -y

Отредактируйте package.json:

{
  "name": "@types/my-lib",
  "version": "1.0.0",
  "description": "Types for my-lib",
  "main": "index.d.ts",
  "types": "index.d.ts",
  "license": "MIT",
  "dependencies": {}
}

Создайте index.d.ts:

// index.d.ts
export function greet(name: string): string;
export function add(a: number, b: number): number;

export interface Config {
  apiUrl: string;
  timeout: number;
  debug: boolean;
}

export class Logger {
  constructor(prefix: string);
  log(message: string): void;
  warn(message: string): void;
  error(message: string): void;
}

6 Отправка в DefinitelyTyped — PR в репозиторий

Если вы хотите, чтобы типы стали доступны всем через npm install @types/ваша_библиотека, создайте Pull Request в DefinitelyTyped:

# Клонируйте DefinitelyTyped
git clone https://github.com/DefinitelyTyped/DefinitelyTyped.git

# Создайте папку для ваших типов
mkdir types/your-lib

# Создайте файл your-lib/index.d.ts
# Создайте файл your-lib/your-lib-tests.ts (тесты типов)
# Создайте файл your-lib/tsconfig.json

Пример тестового файла:

// your-lib-tests.ts
import { greet, add, Logger } from 'your-lib';

// Проверяем, что функции работают с правильными типами
const message: string = greet('Мир');  // ✅ string
const sum: number = add(1, 2);         // ✅ number

// Проверяем, что ошибки типов ловятся
const wrong: string = add('1', '2');   // ❌ Ошибка типа

const logger = new Logger('App');
logger.log('Привет');  // ✅

DefinitelyTyped автоматически тестирует типы. Если тесты проходят, PR будет смержен и типы станут доступны всем.

⚙️ Настройка TypeScript для @types

TypeScript автоматически находит типы в node_modules/@types. Но вы можете настроить это в tsconfig.json:

{
  "compilerOptions": {
    // Папки, где искать типы
    "typeRoots": [
      "./node_modules/@types",
      "./src/types"
    ],

    // Какие пакеты типов подключать автоматически
    // Если не указано — подключаются ВСЕ из typeRoots
    "types": [
      "node",
      "jest",
      "lodash",
      "express"
    ]
  }
}

Важно: Если вы указали types, TypeScript будет подключать только указанные пакеты. Если types не указан — подключаются все из typeRoots.

7 Решение проблем с типами — типичные ошибки и решения

Вот типичные проблемы, с которыми сталкиваются при работе с @types:

1. «Could not find a declaration file for module 'xxx'»

// Решение: установите @types пакет
npm install --save-dev @types/имя_пакета

// Или создайте свой .d.ts файл
// custom.d.ts
declare module 'имя_пакета' {
  export function someFunction(arg: string): void;
}

2. Типы не обновляются после установки @types

# Решение: очистите кэш TypeScript
rm -rf node_modules/.cache
rm -rf node_modules/@types
npm install

3. Конфликт типов — «Duplicate identifier»

# Решение: проверьте, нет ли дублирующих @types пакетов
npm list @types/имя_пакета

# Если есть — удалите дубликат
npm uninstall @types/имя_пакета

8 Best practices — лучшие практики работы с @types

Следуйте этим рекомендациям при работе с @types:

  • Всегда проверяйте, содержит ли библиотека встроенные типы перед установкой @types
  • Устанавливайте @types как dev-зависимости (--save-dev)
  • Регулярно обновляйте @types пакеты — типы могут устареть
  • Используйте skipLibCheck: true в tsconfig.json, если типы конфликтуют
  • Для кастомных библиотек создавайте собственные .d.ts файлы
  • Проверяйте наличие типов на npmjs.com перед установкой пакета

✅ Итого

  • DefinitelyTyped — крупнейший репозиторий типов для JavaScript-библиотек
  • @types — пакеты типов, устанавливаемые через npm
  • Установка: npm install --save-dev @types/имя_библиотеки
  • Многие современные библиотеки уже содержат типы внутри себя — @types не нужен
  • TypeScript автоматически находит типы в node_modules/@types
  • Настройка: typeRoots и types в tsconfig.json
  • Для собственных библиотек создавайте @types пакеты или добавляйте .d.ts файлы

9 Версионирование @types — совместимость версий

@types пакеты имеют свои версии, которые должны совпадать с версиями библиотек:

# Если библиотека установлена версии 4.17.0
npm install lodash@4.17.0

# Установите @types версии 4.17.x
npm install --save-dev @types/lodash@4.17

Если версии не совпадают, TypeScript может показывать ошибки или предупреждения. Используйте npm ls для проверки:

# Проверить установленные версии
npm ls lodash @types/lodash

# Результат:
# my-project@1.0.0
# ├── lodash@4.17.21
# └── @types/lodash@4.17.0 ✅

10 @types и Node.js — типизация Node.js API

@types/node — один из самых важных @types пакетов. Он описывает API Node.js:

// Установка
npm install --save-dev @types/node

// Теперь TypeScript знает API Node.js
import * as fs from 'fs';
import * as path from 'path';

// fs — TypeScript знает все функции
const content: string = fs.readFileSync('./file.txt', 'utf-8');

// path — TypeScript знает все функции
const fullPath: string = path.join(__dirname, 'data', 'file.txt');

// process — TypeScript знает свойства
const env: string | undefined = process.env.NODE_ENV;
const args: string[] = process.argv;

Без @types/node TypeScript не знает о Node.js API и будет показывать ошибки.

11 @types и Webpack — типизация конфигурации

Webpack также имеет @types пакет для типизации конфигурации:

# Установка
npm install --save-dev @types/webpack

// webpack.config.ts
import { Configuration } from 'webpack';

const config: Configuration = {
  entry: './src/index.ts',
  output: {
    filename: 'bundle.js',
    path: __dirname + '/dist'
  },
  resolve: {
    extensions: ['.ts', '.js']
  },
  module: {
    rules: [
      {
        test: /\.ts$/,
        use: 'ts-loader',
        exclude: /node_modules/
      }
    ]
  }
};

export default config;

12 @types и Express — типизация сервера

Express.js — популярный фреймворк для Node.js. @types/express описывает его API:

# Установка
npm install --save-dev @types/express @types/node

// server.ts
import express, { Request, Response, NextFunction } from 'express';

const app = express();

// TypeScript знает типы req и res
app.get('/api/users', (req: Request, res: Response) => {
  const users = [
    { id: 1, name: 'Алексей' },
    { id: 2, name: 'Мария' }
  ];
  res.json(users);
});

app.post('/api/users', (req: Request, res: Response) => {
  const { name } = req.body;  // ✅ req.body типизирован
  res.status(201).json({ id: 3, name });
});

// Middleware с правильными типами
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
  console.error(err.stack);
  res.status(500).json({ error: 'Что-то пошло не так' });
});

app.listen(3000, () => {
  console.log('Сервер запущен на порту 3000');
});

13 @types и Mongoose — типизация MongoDB

Mongoose — ODM для MongoDB. Его типы позволяют работать с MongoDB с полной типизацией:

# Установка
npm install mongoose @types/mongoose

// models/User.ts
import { Schema, model, Document } from 'mongoose';

interface IUser {
  name: string;
  email: string;
  age: number;
  createdAt: Date;
}

// Документ Mongoose расширяет IUser
interface IUserDocument extends IUser, Document {}

const userSchema = new Schema<IUserDocument>({
  name: { type: String, required: true },
  email: { type: String, required: true, unique: true },
  age: { type: Number, required: true },
  createdAt: { type: Date, default: Date.now }
});

export const User = model<IUserDocument>('User', userSchema);

// Использование:
async function createUser(name: string, email: string, age: number) {
  const user = new User({ name, email, age });
  await user.save();  // ✅ TypeScript знает типы
  return user;
}

14 @types и Jest — типизация тестов

Jest — популярный тестовый фреймворк. @types/jest описывает его API:

# Установка
npm install --save-dev jest ts-jest @types/jest

// utils.test.ts
import { add, formatDate } from './utils';

describe('Math functions', () => {
  it('should add two numbers correctly', () => {
    expect(add(1, 2)).toBe(3);
    expect(add(-1, 1)).toBe(0);
    expect(add(0, 0)).toBe(0);
  });
});

describe('Date functions', () => {
  it('should format date correctly', () => {
    const date = new Date('2024-01-15');
    expect(formatDate(date)).toBe('15.01.2024');
  });
});

// Мокирование
describe('API calls', () => {
  it('should fetch data', async () => {
    const mockFetch = jest.fn().mockResolvedValue({ data: 'test' });
    global.fetch = mockFetch;

    const result = await fetch('/api/data');
    expect(result).toEqual({ data: 'test' });
    expect(mockFetch).toHaveBeenCalledWith('/api/data');
  });
});

15 @types и Lodash — типизация утилит

Lodash — популярная утилитарная библиотека. @types/lodash описывает её API:

# Установка
npm install lodash @types/lodash

// Использование
import _ from 'lodash';

const numbers = [1, 2, 3, 4, 5];

// TypeScript знает типы всех функций
const doubled = _.map(numbers, (n) => n * 2);  // number[]
const filtered = _.filter(numbers, (n) => n > 2);  // number[]
const sum = _.reduce(numbers, (acc, n) => acc + n, 0);  // number

// Удобные утилиты
const unique = _.uniq([1, 1, 2, 3, 3]);  // number[]
const chunked = _.chunk([1, 2, 3, 4, 5], 2);  // number[][]
const flattened = _.flatten([[1, 2], [3, 4], [5]]);  // number[]

// Объекты
const user = { name: 'Алексей', age: 25, email: 'alex@example.com' };
const picked = _.pick(user, ['name', 'email']);  // { name: string; email: string }
const omitted = _.omit(user, ['age']);  // { name: string; email: string }

16 @types и GraphQL — типизация API

GraphQL — язык запросов для API. @types/graphql описывает его типы:

# Установка
npm install graphql @types/graphql

// schema.ts
import { 
  GraphQLSchema, 
  GraphQLObjectType, 
  GraphQLString, 
  GraphQLInt,
  GraphQLList 
} from 'graphql';

const UserType = new GraphQLObjectType({
  name: 'User',
  fields: {
    id: { type: GraphQLInt },
    name: { type: GraphQLString },
    email: { type: GraphQLString }
  }
});

const QueryType = new GraphQLObjectType({
  name: 'Query',
  fields: {
    user: {
      type: UserType,
      args: {
        id: { type: GraphQLInt }
      },
      resolve: (_, { id }) => {
        return { id, name: 'Алексей', email: 'alex@example.com' };
      }
    }
  }
});

const schema = new GraphQLSchema({
  query: QueryType
});

17 @types и Socket.IO — типизация WebSocket

Socket.IO — библиотека для WebSocket. @types/socket.io описывает его API:

# Установка
npm install socket.io @types/socket.io

// server.ts
import { Server } from 'socket.io';

const io = new Server(3000, {
  cors: {
    origin: 'http://localhost:5173'
  }
});

// TypeScript знает типы событий
io.on('connection', (socket) => {
  console.log('Пользователь подключился:', socket.id);

  // Обработка событий
  socket.on('message', (data: { text: string; user: string }) => {
    console.log(`${data.user}: ${data.text}`);
    io.emit('message', data);
  });

  socket.on('disconnect', () => {
    console.log('Пользователь отключился:', socket.id);
  });
});

Клиентская часть:

// client.ts
import { io } from 'socket.io-client';

const socket = io('http://localhost:3000');

// Отправка сообщения
socket.emit('message', { text: 'Привет!', user: 'Алексей' });

// Получение сообщения
socket.on('message', (data: { text: string; user: string }) => {
  console.log(`${data.user}: ${data.text}`);
});

Socket.IO обеспечивает надёжную связь между клиентом и сервером в реальном времени.

Проверяем понимание

5 вопросов

@types и типизированное использование Lodash

Premium