Нативная поддержка TypeScript, встроенный SQLite и гранулярная модель разрешений — всё в Node.js 25+.
Это руководство охватывает всё, что нужно знать об этих трёх революционных возможностях:
от запуска .ts файлов напрямую без tsc до нуль-конфигурационного
хранения данных с node:sqlite и защиты приложений с --permission.
Node.js 25+ привносит три революционных нативных возможности в JavaScript-рантайм:
нативную поддержку TypeScript через встроенный type stripping,
встроенный SQLite через модуль node:sqlite и
модель разрешений (Permission Model) для гранулярной безопасности
во время выполнения. В этом руководстве подробно рассматриваются все три функции.
| Версия | Дата релиза | TypeScript | SQLite | Permission Model |
|---|---|---|---|---|
| v20 | Апр 2024 | — | — | Экспериментальный (--experimental-permission) |
| v22 | Апр 2024 | Экспериментальный type stripping | Добавлен node:sqlite (v22.5.0) | Экспериментальный |
| v23 | Окт 2024 | Доработки, .tsx не поддерживается | — | Экспериментальный |
| v24 | Май 2025 | Функционально завершён | Функционально завершён | Флаг --permission (без experimental-) |
| v25 | Окт 2025 | Стабильный (Stability: 2), V8 14.1 | Стабильный | Добавлен --allow-net |
| v26 | Май 2026 | Стабильный, в документации как "Modules: TypeScript" | Стабильный | Стабильный, поддержка конфиг-файлов |
Главный вывод: Начиная с Node.js 25+ (Current) и Node.js 24+ (LTS), все три функции стабильны и готовы к production-использованию.
Есть два способа запускать TypeScript-код в Node.js:
| Подход | Описание | Для кого |
|---|---|---|
| Встроенный type stripping (рекомендуется) | Node.js удаляет TypeScript-синтаксис во время выполнения, оставляя только JavaScript | Простые TypeScript-проекты, скрипты, прототипы |
Полная поддержка через сторонние инструменты (tsx) | Полная поддержка TypeScript, включая enums, декораторы, возможности tsconfig.json | Сложные TypeScript-проекты с продвинутыми возможностями |
Встроенный type stripping работает по умолчанию — просто запустите:
# Запустить TypeScript-файл напрямую (Node.js 22+)
node app.ts
# Флаги не нужны — type stripping включён по умолчанию в v22+
Чтобы отключить type stripping:
node --no-strip-types app.ts
Node.js выполняет TypeScript-файлы, заменяя TypeScript-специфичный синтаксис на пробелы (сохраняя номера строк и столбцов в stack trace). Проверка типов не выполняется — это по-прежнему задача tsc в редакторе или CI.
Ключевые принципы:
tsconfig.json не читается — Node.js полностью игнорирует файлы tsconfig.json// Этот код выполняется нативно в Node.js:
function greet(name: string): string {
return `Hello, ${name}!`;
}
// Аннотации типа `: string` заменяются на пробелы
// Node.js видит: function greet(name) { return `Hello, ${name}!`; }
| Расширение | Модульная система | Аналог в JS |
|---|---|---|
.ts | Определяется ближайшим package.json | .js |
.mts | Всегда ES-модуль | .mjs |
.cts | Всегда CommonJS | .cjs |
.tsx | Не поддерживается | — |
Правила модульной системы для .ts-файлов:
"type": "module" в ваш package.json"type": "module" файлы .ts по умолчанию используют CommonJS// Правильно:
import './helper.ts';
import { config } from './config.ts';
// Неправильно — будет ошибка:
import './helper';
import { config } from './config';
// Также правильно для CommonJS:
const helper = require('./helper.ts');
| Возможность | Поддерживается? | Примечания |
|---|---|---|
Аннотации типов (: string) | ✅ Да | Заменяются на пробелы |
| Interfaces | ✅ Да | Удаляются |
| Type aliases | ✅ Да | Удаляются |
| Generics | ✅ Да | Удаляются |
typeof, keyof, условные типы | ✅ Да | Чисто типовой уровень |
const assertions | ✅ Да | Удаляются |
as-приведения | ✅ Да | Удаляются |
| Enums | ❌ Нет | ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX |
| Namespaces с runtime-кодом | ❌ Нет | Например, namespace A { export let x = 1 } |
| Parameter properties | ❌ Нет | constructor(private x: number) |
| Декораторы | ❌ Нет | Всё ещё Stage 3 TC39 proposal |
Пути из tsconfig.json | ❌ Нет | Используйте subpath imports (#) |
type)Из-за того, как работает type stripping, ключевое слово type обязательно в импортах:
// ✅ Правильно — будет работать:
import type { User, Config } from './types.ts';
import { createUser, type UserInput } from './user.ts';
// ❌ Неправильно — будет runtime-ошибка:
import { User, Config } from './types.ts'; // User — тип, а не значение
Для проектов, нацеленных на Node.js 25+ со встроенным type stripping:
{
"compilerOptions": {
"noEmit": true,
"target": "esnext",
"module": "nodenext",
"rewriteRelativeImportExtensions": true,
"erasableSyntaxOnly": true,
"verbatimModuleSyntax": true
}
}
# Установка как dev-зависимости
npm install --save-dev tsx
# Запуск напрямую
npx tsx your-file.ts
# Или через node --import
node --import=tsx your-file.ts
Зависимости внутри node_modules: Node.js отказывается обрабатывать TypeScript-файлы внутри node_modules.
Не-файловые входы:
--eval и STDIN: type stripping работает--check: Не поддерживаетсяПутевые алиасы: paths из tsconfig.json не поддерживаются. Используйте subpath imports.
node:sqlite)node:sqliteimport { DatabaseSync } from 'node:sqlite';
const db = new DatabaseSync(':memory:');
db.exec(`
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE
);
`);
new DatabaseSync(filename[, options])
| Параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
filename | string | Buffer | URL | — | Путь к файлу БД или ':memory:' |
options.readonly | boolean | false | Только чтение |
options.create | boolean | true | Создать файл, если не существует |
options.bigInt | boolean | false | Возвращать bigint для INTEGER |
Методы: db.prepare(sql), db.exec(sql), db.export(), db.backup(), db.close(), db.serialize(), db.transaction(), db.aggregate(), db.loadExtension(), db.enableDefensive().
Методы: stmt.run(values) → {lastInsertRowid, changes}, stmt.get(values) → первая строка, stmt.all(values) → все строки, stmt.iterate(values) → итератор.
// INSERT
const insert = db.prepare('INSERT INTO users (name, email) VALUES (?, ?)');
const result = insert.run('Alice', '[email protected]');
console.log(result.lastInsertRowid); // 1
// SELECT одной строки
const user = db.prepare('SELECT * FROM users WHERE id = ?').get(1);
// SELECT всех строк
const allUsers = db.prepare('SELECT * FROM users').all();
// Именованные параметры (префиксы $, @ или :)
const byEmail = db.prepare('SELECT * FROM users WHERE email = $email');
const user = byEmail.get({ $email: '[email protected]' });
// Транзакция (атомарная, автооткат при ошибке)
const insertUser = db.prepare('INSERT INTO users (name, email) VALUES (?, ?)');
const insertUsers = db.transaction((users) => {
for (const user of users) {
insertUser.run(user.name, user.email);
}
});
insertUsers([
{ name: 'Charlie', email: '[email protected]' },
{ name: 'Diana', email: '[email protected]' },
]);
// Сериализация
db.serialize(() => {
db.exec('INSERT INTO users VALUES (1, "Alice")');
db.exec('INSERT INTO users VALUES (2, "Bob")');
});
// Бэкап с прогрессом
const db = new DatabaseSync('source.db');
db.backup('backup.db', {
progress: (remaining, total) => {
const pct = ((total - remaining) / total * 100).toFixed(2);
console.log(`Backup: ${pct}%`);
}
});
// Экспорт всей БД как Uint8Array
const data = db.export();
import { DatabaseSync } from 'node:sqlite';
const db = new DatabaseSync(':memory:');
db.exec(`
CREATE TABLE projects (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
created_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
project_id INTEGER REFERENCES projects(id),
title TEXT NOT NULL,
done INTEGER DEFAULT 0
);
`);
const insertProject = db.prepare('INSERT INTO projects (name) VALUES (?)');
const projectResult = insertProject.run('My Project');
const projectId = projectResult.lastInsertRowid;
const addTasks = db.transaction(() => {
const insertTask = db.prepare('INSERT INTO tasks (project_id, title) VALUES (?, ?)');
insertTask.run(projectId, 'Спроектировать схему БД');
insertTask.run(projectId, 'Написать API-эндпоинты');
insertTask.run(projectId, 'Протестировать приложение');
});
addTasks();
const tasks = db.prepare(`
SELECT t.id, t.title, t.done, p.name as project
FROM tasks t
JOIN projects p ON t.project_id = p.id
WHERE p.id = ?
`).all(projectId);
console.log(tasks);
db.close();
Модель разрешений Node.js — это механизм безопасности, ограничивающий доступ процесса Node.js к системным ресурсам.
| Версия | Веха |
|---|---|
| v20.0.0 | Экспериментальная, флаг --experimental-permission |
| v24.0.0 | Флаг переименован в --permission |
| v25.0.0 | Добавлен --allow-net |
| v26.3.0 | Стабильная, конфигурационные файлы, process.permission.drop() |
# Включение модели (ограничивает все разрешения по умолчанию)
node --permission app.js
При включении --permission ограничиваются: доступ к ФС, сеть, дочерние процессы, Worker-потоки, нативные аддоны, WASI, FFI, Inspector-протокол.
process.permission// Проверка разрешения
process.permission.has('fs.write'); // true | false
process.permission.has('fs.read', '/home/refs/protected-folder');
// Необратимый отзыв разрешения
process.permission.drop('fs.read', '/etc/myapp');
process.permission.drop('child');
# Разрешить все операции с ФС
node --permission --allow-fs-read=* --allow-fs-write=* index.js
# Разрешить конкретные пути
node --permission --allow-fs-read=/tmp/ --allow-fs-write=/tmp/ index.js
node:sqlite в production?node:sqlite и better-sqlite3?node:sqlite — встроенный модуль: не нужен npm install, не требуется нативная компиляция. better-sqlite3 — внешний npm-пакет, требующий компиляции аддона. Для новых проектов рекомендуется node:sqlite.node --permission --allow-fs-read=/data --allow-fs-write=/data app.ts. Это включает нативный TypeScript, даёт доступ к SQLite через node:sqlite и ограничивает процесс указанными путями..ts: import { config } from './config.ts' (правильно) vs import { config } from './config' (ошибка).node:sqlite?node --permission --allow-fs-read=/data --allow-fs-write=/data app.ts. Без этих разрешений node:sqlite завершится ошибкой ERR_ACCESS_DENIED.Статьи на maximov.by:
Официальная документация:
Нужно создать современное Node.js-приложение с TypeScript, SQLite и правильной безопасностью? Я — full-stack разработчик с глубоким опытом Node.js, TypeScript и production-архитектуры. Обсудим ваш проект.
Расскажите о вашем проекте — я дам экспертные рекомендации по архитектуре, технологиям и предварительную оценку. Бесплатно.