Глава 7. Модули, tsconfig и компиляция проекта

84 просмотров
0 лайков
0 в избранном

Модули в TypeScript

TypeScript использует те же модули, что и современный JavaScript (ES-модули). Один файл — один модуль.

Именованный экспорт

// math.ts
export function add(a: number, b: number): number {
    return a + b;
}

export const PI = 3.14;

// Можно собрать в одном export
export { add, PI };
// main.ts
import { add, PI } from "./math";

console.log(add(2, 3));
console.log(PI);

Экспорт по умолчанию (default)

Один на модуль. Удобен для главной сущности файла:

// logger.ts
export default function log(message: string): void {
    console.log(message);
}

// Имя при импорте может быть любым
import log from "./logger";
import printMessage from "./logger"; // тоже OK

Реальный пример: структура API-клиента

// api/types.ts
export interface User {
    id: number;
    name: string;
    email: string;
}

// api/client.ts
import type { User } from "./types";

export async function fetchUser(id: number): Promise<User> {
    const res = await fetch(`/api/users/${id}`);
    return res.json();
}

// index.ts
import { fetchUser } from "./api/client";
import type { User } from "./api/types";

const user = await fetchUser(1);

import type — только для типа

Конструкция import type подчёркивает, что импорт нужен только для TypeScript и будет удалён при компиляции:

// Только тип — попадёт в финальный JS
import type { User } from "./types";

// Значение — останется в JS
import { fetchUser } from "./client";

Re-export и barrel-файлы

Чтобы упростить импорты, используют index.ts, который переэкспортирует всё наружу:

// api/index.ts
export * from "./types";
export * from "./client";
export * from "./users";

// теперь из другого места — коротко
import { fetchUser, type User } from "./api";

Файл tsconfig.json

Конфигурация компилятора. Инициализация шаблона:

tsc --init

Минимальный полезный tsconfig.json для проекта:

{
    "compilerOptions": {
        "target": "ES2020",
        "module": "ESNext",
        "moduleResolution": "node",
        "outDir": "./dist",
        "rootDir": "./src",
        "strict": true,
        "esModuleInterop": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true
    },
    "include": ["src/**/*"],
    "exclude": ["node_modules", "dist"]
}

Ключевые флаги

ФлагНазначение
targetВерсия JS, в которую компилировать (ES2020, ESNext и т. д.).
moduleСистема модулей в выводе (CommonJS, ESNext).
moduleResolutionАлгоритм разрешения импортов (node, bundler).
outDirКуда складывать .js-файлы.
rootDirКорневая папка исходников.
strictВключает все строгие проверки (рекомендуется).
noImplicitAnyЗапрещает неявный any.
strictNullChecksРазличает null/undefined от обычных типов.
noUnusedLocalsОшибка на неиспользуемые переменные.
noUnusedParametersОшибка на неиспользуемые параметры.
esModuleInteropКорректная работа с CommonJS-импортами.
skipLibCheckПропускать проверку типов в .d.ts (ускоряет сборку).

Флаг strict — что включает

Включение strict: true активирует сразу несколько проверок:

  • noImplicitAny
  • strictNullChecks
  • strictFunctionTypes
  • strictBindCallApply
  • strictPropertyInitialization
  • noImplicitThis
  • alwaysStrict

Для новых проектов всегда включайте strict: true. Для миграции существующего JS-проекта — можно начать без него и включать постепенно.

Компиляция и наблюдение

tsc                 # одноразовая компиляция в outDir
tsc --watch         # пересборка при изменениях (режим разработки)
tsc --noEmit        # только проверка типов, без выдачи JS (для CI)

В package.json удобно вынести это в скрипты:

{
    "scripts": {
        "build": "tsc",
        "dev": "tsc --watch",
        "typecheck": "tsc --noEmit",
        "start": "node dist/index.js"
    }
}

Source maps для отладки

Чтобы при отладке видеть оригинальный TypeScript, а не сгенерированный JS, включают source maps:

{
    "compilerOptions": {
        "sourceMap": true
    }
}

Сторонние библиотеки и типы

Многие библиотеки уже содержат собственные типы (например, axios, zod). Если типов нет — их ищут в репозитории @types/* (DefinitelyTyped):

npm install lodash
npm install --save-dev @types/lodash

После этого можно импортировать и получать подсказки:

import _ from "lodash";

const sorted = _.sortBy([{ x: 2 }, { x: 1 }], "x");

Если типов нет вообще

Можно создать файл декларации globals.d.ts:

// Подсказываем TypeScript про сторонний модуль без типов
declare module "untyped-lib" {
    export function doStuff(value: unknown): void;
}

Сборка проектов и project references

В монорепозиториях или крупных проектах используют project references — каждый подпроект имеет свой tsconfig.json, а корневой связывает их:

// tsconfig.json (корень)
{
    "files": [],
    "references": [
        { "path": "./packages/shared" },
        { "path": "./packages/api" },
        { "path": "./packages/frontend" }
    ]
}

Это позволяет инкрементально пересобирать только изменившиеся части и изолировать зависимости между пакетами.

Кейс: типичный backend-проект (NestJS-стиль)

project/
├── src/
│   ├── users/
│   │   ├── users.controller.ts   # обработчики HTTP
│   │   ├── users.service.ts      # бизнес-логика
│   │   ├── users.repository.ts   # работа с БД
│   │   └── dto/
│   │       ├── create-user.dto.ts
│   │       └── update-user.dto.ts
│   ├── common/
│   │   └── types.ts
│   ├── app.module.ts
│   └── main.ts
├── test/
├── package.json
└── tsconfig.json

Скрипты для такого проекта:

{
    "scripts": {
        "build": "tsc",
        "start": "node dist/main.js",
        "start:dev": "ts-node-dev --respawn src/main.ts",
        "typecheck": "tsc --noEmit",
        "test": "jest",
        "lint": "eslint src --ext .ts"
    }
}

Типичные ошибки

1. Cannot find module './types' без расширения

В новых версиях TS и ESM-режиме нужно либо указать расширение, либо правильно настроить moduleResolution:

// Современный ESM-подход
import { User } from "./types.js"; // расширение .js даже для .ts-файла

2. Не включён strict с самого начала

Включить строгий режим позже в большом проекте — больно. Лучше сразу "strict": true.

3. Компиляция тестовых файлов в production-вывод

{
    "include": ["src/**/*"],
    "exclude": ["node_modules", "dist", "**/*.test.ts"]
}

4. Забыли @types/...

Библиотека установлена, но TS ругается — обычно не хватает пакета типов. Проверяйте поле types или typings в package.json библиотеки.

Практика

  1. Создайте проект из двух файлов: math.ts с экспортируемой функцией и main.ts, который её импортирует.
  2. Инициализируйте tsconfig.json через tsc --init и настройте strict: true, outDir, rootDir.
  3. Добавьте скрипты build, dev, typecheck в package.json.
  4. Установите библиотеку без встроенных типов (например, lodash) и добавьте @types/lodash.
  5. Включите noUnusedLocals и убедитесь, что неиспользуемая переменная вызывает ошибку.

Итог

Мы разобрались со структурой модулей, импортами/экспортами и import type, изучили ключевые опции tsconfig.json и флаги компилятора, поняли, как подключать сторонние библиотеки и их типы, и рассмотрели структуру реального проекта. В следующей главе — применение TypeScript на практике: объединения, литеральные типы, type guards и utility-типы.

Комментарии 0

Для добавления комментариев необходимо войти или зарегистрироваться.

Пока нет комментариев. Станьте первым!