Форматы и подключение TypeScript
Дальше: Компиляция · Рекомендации · Справочник — tsconfig
TypeScript не подключают в браузер напрямую: исходники .ts / .tsx проходят компиляцию (tsc) или сборку (Vite, esbuild), на выходе — JavaScript. Настройки живут в tsconfig.json и скриптах package.json.
Маршрут: Первая программа → подключение → Экосистема → Компиляция.
Три типичных сценария
| Сценарий | Инструменты | Результат |
|---|---|---|
| Node.js backend | tsc, @types/node | dist/*.js + node dist/index.js |
| Frontend SPA | Vite + react-ts / vue-ts | dev-server, bundle для браузера |
| Монорепозиторий | tsc -b, project references | несколько пакетов с общим base config |
Минимальный Node.js-проект
mkdir my-api && cd my-api
npm init -y
npm i -D typescript @types/node
npx tsc --init
package.json (скрипты):
{
"scripts": {
"build": "tsc",
"typecheck": "tsc --noEmit",
"start": "node dist/index.js",
"dev": "tsc --watch"
}
}
tsconfig.json (стартовый вариант для Node 20+):
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"outDir": "dist",
"rootDir": "src",
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src"]
}
Разбор:
module: NodeNext— согласованность с"type": "module"вpackage.json(ESM) или.cjs(CommonJS).typecheckбез emit удобен в CI, когда сборку делает другой инструмент.
Подробный разбор первого запуска — 4.md.
Frontend: Vite + TypeScript
npm create vite@latest my-app -- --template react-ts
cd my-app && npm i && npm run dev
Vite даёт:
- готовый
tsconfig.jsonиtsconfig.node.json; - hot reload;
- проверку типов через
tscв скриптеbuild(tsc -b && vite build).
Для Vue: --template vue-ts. Связка с React — 21.md.
tsconfig.json: что настроить в первую очередь
| Опция | Назначение |
|---|---|
include / exclude | Какие файлы входят в проект |
rootDir / outDir | Откуда и куда кладётся JS |
target | Версия JS на выходе (ES2022, …) |
module | Формат модулей (NodeNext, ESNext, CommonJS) |
lib | Доступные API (DOM, ES2022 — для браузера) |
strict | Строгие проверки — 6.md |
jsx | react-jsx для React в .tsx |
paths / baseUrl | Алиасы импортов |
Полная таблица флагов — Справочник — tsconfig и официальный TSConfig Reference.
ESM и CommonJS
Node.js и bundler по-разному трактуют import / require.
| ESM | CommonJS | |
|---|---|---|
| Синтаксис | import / export | require / module.exports |
В package.json | "type": "module" | по умолчанию или "type": "commonjs" |
| Расширения | .mjs или .js при "type": "module" | .cjs при смешении |
Рекомендация для новых Node-проектов: ESM + "module": "NodeNext" + "moduleResolution": "NodeNext".
Типичная ошибка: Cannot use import statement outside a module — не совпали package.json, расширение файла и module в tsconfig.
Алиасы путей (paths)
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@shared/*": ["../shared/src/*"]
}
}
}
import { User } from "@/types/user";
Разбор:
- TypeScript понимает алиасы при проверке типов.
- Runtime (Node/Vite) должен знать те же пути: в Vite —
resolve.aliasвvite.config.ts; в Node 20+ — иногдаimportsвpackage.jsonили сборщик.
Типы для npm-пакетов (@types)
JavaScript-библиотека без встроенных .d.ts типизируется через DefinitelyTyped:
npm i -D @types/react @types/node @types/express
| Ситуация | Действие |
|---|---|
Пакет уже поставляет .d.ts | @types/* не нужен |
| Есть только JS | npm i -D @types/имя-пакета |
| Нет типов нигде | свой declare module "lib" — Справочник — декларации |
Ошибка Could not find a declaration file for module 'X' — сигнал установить или написать декларацию.
Файлы .ts и .tsx
| Расширение | Когда |
|---|---|
.ts | TypeScript без JSX |
.tsx | TypeScript + JSX (React и др.) |
.d.ts | Только типы, без JS на выходе |
import type — импорт только типов (удаляется при компиляции):
import type { User } from "./types";
Монорепозиторий
В корне — tsconfig.base.json с strict и общими путями. В пакетах:
{
"extends": "../../tsconfig.base.json",
"compilerOptions": { "outDir": "dist", "rootDir": "src", "composite": true },
"references": [{ "path": "../shared" }],
"include": ["src"]
}
Сборка графа: tsc -b. Подробнее — Экосистема, Справочник — project references.
Модули ES и пространства имён
Современный TypeScript опирается на ES-модули (import / export). Имена файлов и module / moduleResolution в tsconfig должны совпадать с тем, как код запускается (Node ESM, bundler).
// user.ts
export type User = { id: string; name: string };
export function createUser(name: string): User {
return { id: crypto.randomUUID(), name };
}
// index.ts
import { createUser, type User } from "./user.js";
Разбор:
- В Node с
module: NodeNextв импорте указывают.js, хотя исходник —.ts— 4.md. export type/import type— только типы, не попадают в runtime emit — 8.md.
Пространства имён (namespace)
namespace Geometry {
export type Point = { x: number; y: number };
export function distance(a: Point, b: Point): number {
return Math.hypot(a.x - b.x, a.y - b.y);
}
}
const p: Geometry.Point = { x: 0, y: 0 };
| ES-модули | namespace | |
|---|---|---|
| Рекомендация в новом коде | да | legacy, declaration merging, некоторые .d.ts |
| Файловая граница | один модуль = файл(ы) | логическая группа внутри файла |
| Tree-shaking | хорошо с bundler | хуже |
В прикладных проектах предпочитайте файлы-модули; namespace встречается в старых библиотеках и при слиянии деклараций. Таблицы — Справочник — модули.
Декларации типов (.d.ts)
Декларация описывает форму API без реализации — для JS-библиотек, глобальных переменных и расширения типов.
Файл global.d.ts в проекте
declare const __APP_VERSION__: string;
declare global {
interface Window {
analytics?: { track(event: string): void };
}
}
export {};
Разбор:
declare const— переменная есть в runtime (задаёт bundler), TS знает тип.declare globalрасширяетWindow;export {}делает файл модулем.
Модуль без типов
// types/legacy-lib.d.ts
declare module "legacy-lib" {
export function connect(url: string): Promise<void>;
}
После этого import { connect } from "legacy-lib" типизируется. Источники типов:
| Источник | Пример |
|---|---|
Пакет с встроенными .d.ts | современный react |
| DefinitelyTyped | @types/lodash |
Свой declare module | внутренний legacy JS |
Слияние деклараций (declaration merging)
interface User {
id: string;
}
interface User {
name: string;
}
// User = { id: string; name: string }
Работает для interface и namespace — удобно дополнять типы библиотек. Для union и mapped types используйте type, не interface. Подробнее — Справочник — декларации, emit .d.ts — 15.md.
tsc и bundler: кто за что отвечает
| Задача | Кто |
|---|---|
| Проверка типов | tsc или tsc --noEmit |
| Транспиляция TS → JS | tsc или esbuild/swc внутри Vite |
| Сборка бандла для браузера | Vite / Webpack |
| Source maps | sourceMap: true в tsconfig + настройки bundler |
В CI часто выносят отдельный шаг npm run typecheck (tsc --noEmit): так сборка не пройдёт мимо ошибок типов, даже если bundler не остановился на предупреждениях.
Частые ошибки подключения
| Симптом | Причина | Решение |
|---|---|---|
Cannot find module | Путь или moduleResolution | Проверить paths, NodeNext, установлен ли пакет |
| Нет типов у пакета | Нет .d.ts | @types/* или declare module |
| Старый JS в браузере | Завышен target / нет полифилов | Понизить синтаксис или настроить bundler |
strict выключен | Наследие шаблона | Включить в новом коде — 6.md |
| Смешение ESM/CJS | Разные настройки Node и TS | Выровнять package.json и module |
Практика
- Создайте Node-проект с
src/index.ts,outDir: dist, скриптамиbuildиtypecheck. - Добавьте алиас
@/наsrc/и импортируйте тип изsrc/types. - Установите
@types/nodeи вызовитеprocess.envс подсказкой IDE. - Создайте Vite
react-tsи найдите дваtsconfig— объясните разницу. - Включите
strict: trueи исправьте все новые ошибки в одном файле.