Платформа Node.js як середовище виконання

Вбудований модуль util (Utilities)

Утиліти Node.js: promisify, inspect, types, форматування, перетворення callback на Promise

Вбудований модуль util (Utilities)

🎯 Мета лекції

  • Опанувати функцію util.promisify() для перетворення callback-based API у Promise-based.
  • Освоїти механізм інспектування об'єктів через util.inspect() для налагодження складних структур даних.
  • Вивчити модуль util.types для точної перевірки типів JavaScript та вбудованих об'єктів Node.js.
  • Зрозуміти роль util.format() у форматуванні рядків та інтерполяції значень.
  • Навчитися застосовувати util.callbackify() для зворотної сумісності з legacy-кодом.

🔑 Ключові терміни

  • util: вбудований модуль Node.js із допоміжними функціями для роботи з асинхронністю, типами та форматуванням.
  • Promisify: процес перетворення функції з callback у функцію, що повертає Promise.
  • Inspect: процес глибокого перетворення об'єкта у читабельне рядкове представлення.
  • Type Guards: механізм точної перевірки типів у runtime для попередження помилок.
  • Format String: шаблонний рядок із заповнювачами (%s, %d, %j) для підстановки значень.

Архітектурний контекст: від callback до Promise

Історично Node.js розвивався у часи, коли JavaScript ще не мав нативних Promise (вони з'явилися у стандарті ES2015). Тому більшість вбудованих API — від fs до crypto — проєктувалися навколо callback-паттерну. Однак із прийняттям Promise та async/await у сучасних проєктах стало критично важливим мати єдиний механізм перетворення legacy-коду.

Саме для цього Node.js надає модуль util — набір утилітарних функцій, які вирішують типові завдання інженера: міграція callback на Promise, налагодження складних об'єктів, перевірка типів за межами можливостей typeof, форматування діагностичних повідомлень.

Модуль util є вбудованим у Node.js починаючи з версії 0.3.0, проте ключові функції на кшталт promisify() з'явилися лише у версії 8.0.0 (2017 рік), коли екосистема почала масову міграцію на async/await.

Імпорт модуля util

Модуль доступний у трьох варіантах імпорту залежно від системи модулів та стилю коду:

import util from 'node:util';
import { promisify, inspect, types } from 'node:util';

// Використання окремих функцій
const readFileAsync = promisify(fs.readFile);
Префікс node: перед назвою модуля є рекомендованою практикою у сучасному Node.js (версії 16+). Він чітко позначає вбудовані модулі та унеможливлює конфлікти з npm-пакетами, які можуть мати ідентичні назви.

Перетворення callback у Promise: util.promisify()

Функція util.promisify() приймає callback-функцію у форматі Node.js (де останній аргумент — це callback (err, result) => void) і повертає нову функцію, яка повертає Promise.

Базовий синтаксис та механіка роботи

Припустимо, у нас є legacy-функція для читання файлу з callback-based API:

import fs from 'node:fs';
import { promisify } from 'node:util';

// Оригінальна callback-функція
fs.readFile('config.json', 'utf8', (err, data) => {
  if (err) {
    console.error('Помилка читання:', err);
    return;
  }
  console.log('Дані:', data);
});

// Перетворення на Promise через promisify
const readFileAsync = promisify(fs.readFile);

// Тепер можна використовувати async/await
async function loadConfig(): Promise<string> {
  try {
    const data = await readFileAsync('config.json', 'utf8');
    return data;
  } catch (err) {
    console.error('Помилка завантаження конфігурації:', err);
    throw err;
  }
}
Функція promisify() перетворює лише функції, які дотримуються Node.js callback convention: callback(error, result). Якщо callback має іншу сигнатуру (наприклад, callback(result) без помилки), promisify() не працюватиме коректно.

Приклад міграції власної callback-функції

Розглянемо створення функції для читання конфігурації з файлу у двох варіантах: спочатку callback-based, потім перетворену через promisify.

import fs from 'node:fs';

interface Config {
  database: string;
  port: number;
}

function readConfig(
  filePath: string,
  callback: (err: Error | null, config?: Config) => void
): void {
  fs.readFile(filePath, 'utf8', (err, data) => {
    if (err) {
      callback(err);
      return;
    }
    try {
      const config = JSON.parse(data) as Config;
      callback(null, config);
    } catch (parseError) {
      callback(parseError as Error);
    }
  });
}

// Виклик з callback
readConfig('./config.json', (err, config) => {
  if (err) {
    console.error('Помилка:', err);
    return;
  }
  console.log('База даних:', config?.database);
});

::

Альтернатива: нативний fs.promises

Починаючи з Node.js 10.0.0, модуль fs надає вбудований Promise-based API через fs.promises, тому promisify(fs.readFile) не є необхідним у сучасних проєктах:

import { readFile } from 'node:fs/promises';

async function loadData(): Promise<void> {
  const data = await readFile('data.txt', 'utf8');
  console.log(data);
}

Однак promisify() залишається незамінним для:

  • Сторонніх бібліотек з callback API (наприклад, redis, mysql).
  • Власних legacy-функцій, які не мають Promise-версій.
  • Динамічного перетворення функцій у runtime.
Якщо функція вже повертає Promise, повторне застосування promisify() призведе до помилки або некоректної поведінки. Завжди перевіряйте, чи функція має вбудовану Promise-версію, перш ніж застосовувати promisify().

Кастомізація promisify через util.promisify.custom

Деякі функції мають специфічну логіку, яка не вписується у стандартний callback-паттерн. Для таких випадків promisify() підтримує символ util.promisify.custom, який дозволяє визначити власну реалізацію Promise-версії функції.

import { promisify } from 'node:util';

interface User {
  id: number;
  name: string;
}

// Функція з нестандартною сигнатурою callback
function fetchUser(id: number, callback: (user: User) => void): void {
  setTimeout(() => {
    callback({ id, name: 'Олександр' });
  }, 100);
}

// Додавання кастомної Promise-реалізації
fetchUser[promisify.custom] = (id: number): Promise<User> => {
  return new Promise((resolve) => {
    fetchUser(id, (user) => resolve(user));
  });
};

// Тепер promisify використовує кастомну реалізацію
const fetchUserAsync = promisify(fetchUser);

async function main() {
  const user = await fetchUserAsync(42);
  console.log('Користувач:', user.name);
}

main();
Символ util.promisify.custom є стандартним механізмом у Node.js для розширення promisify(). Якщо ви розробляєте бібліотеку з callback API, додайте підтримку цього символу для зручності користувачів, які працюють з async/await.

Зворотне перетворення: util.callbackify()

Функція util.callbackify() виконує зворотну операцію: приймає функцію, що повертає Promise, і перетворює її на callback-based функцію. Це корисно для забезпечення зворотної сумісності у бібліотеках, які мають підтримувати як старий, так і новий код.

import { callbackify } from 'node:util';

// Сучасна async-функція
async function fetchData(url: string): Promise<string> {
  const response = await fetch(url);
  return response.text();
}

// Перетворення на callback-версію
const fetchDataCallback = callbackify(fetchData);

// Виклик у legacy-коді
fetchDataCallback('https://api.example.com/data', (err, result) => {
  if (err) {
    console.error('Помилка:', err);
    return;
  }
  console.log('Дані:', result);
});
Функція callbackify() автоматично перетворює відхилені Promise у перший аргумент callback (помилку), а успішний результат — у другий аргумент. Це забезпечує повну сумісність із Node.js callback convention.

Інспектування об'єктів: util.inspect()

Функція util.inspect() перетворює JavaScript-об'єкти у детальне рядкове представлення, враховуючи глибоку вкладеність, циклічні посилання та спеціальні типи даних. Це незамінний інструмент для налагодження складних структур, які console.log() не може відобразити повністю.

Базове використання та відмінності від console.log

Стандартний console.log() під капотом використовує util.inspect(), але з обмеженими налаштуваннями. Для точного контролю над форматуванням краще використовувати inspect() безпосередньо.

import { inspect } from 'node:util';

const complexObject = {
  user: {
    id: 42,
    profile: {
      name: 'Марія',
      settings: {
        theme: 'dark',
        notifications: {
          email: true,
          push: false,
        },
      },
    },
  },
  metadata: new Map([
    ['created', new Date('2025-01-15')],
    ['updated', new Date('2025-02-01')],
  ]),
  buffer: Buffer.from('Node.js'),
};

// Стандартний console.log обріже глибокі об'єкти
console.log(complexObject);
// { user: { id: 42, profile: { name: 'Марія', settings: [Object] } }, ... }

// inspect з повною глибиною та кольорами
console.log(inspect(complexObject, { depth: null, colors: true }));
node inspect-demo.ts
$ node inspect-demo.ts
{
user: {
id: 42,
profile: {
name: 'Марія',
settings: { theme: 'dark', notifications: { ... } }
}
},
metadata: Map(2) { 'created' => 2025-01-15T00:00:00.000Z, ... },
buffer: <Buffer 4e 6f 64 65 2e 6a 73>
}

Налаштування InspectOptions

Функція inspect() приймає об'єкт конфігурації InspectOptions з десятками параметрів для точного керування виводом:

import { inspect } from 'node:util';
import type { InspectOptions } from 'node:util';

const data = {
  users: [
    { id: 1, name: 'Іван', roles: ['admin', 'editor'] },
    { id: 2, name: 'Олена', roles: ['viewer'] },
  ],
  circular: {} as any,
};

// Створення циклічного посилання
data.circular.self = data.circular;

const options: InspectOptions = {
  depth: 3,              // Глибина вкладеності (null = нескінченність)
  colors: true,          // ANSI-кольори у терміналі
  compact: false,        // Багаторядковий формат (не стиснутий)
  breakLength: 60,       // Максимальна довжина рядка перед переносом
  sorted: true,          // Сортування ключів об'єкта за алфавітом
  getters: true,         // Показувати значення getter-властивостей
  showHidden: false,     // Показувати неенумеровані властивості
  maxArrayLength: 100,   // Максимальна кількість елементів масиву
  maxStringLength: 1000, // Обрізати довгі рядки
};

console.log(inspect(data, options));

Перевірка типів: util.types

Стандартний оператор typeof у JavaScript має обмежені можливості: він не розрізняє масиви та об'єкти, не визначає null коректно, не може перевірити вбудовані типи на кшталт Map, Set, Promise чи Buffer. Модуль util.types надає набір точних предикатів (type guards) для runtime-перевірки типів.

Обмеження typeof та instanceof

Розглянемо типові проблеми стандартних механізмів перевірки типів:

// Проблема 1: typeof null
console.log(typeof null);          // "object" (баг у специфікації!)

// Проблема 2: typeof не розрізняє масиви
console.log(typeof [1, 2, 3]);     // "object"
console.log(typeof { a: 1 });      // "object"

// Проблема 3: instanceof не працює між контекстами
const iframe = document.createElement('iframe');
document.body.appendChild(iframe);
const iframeArray = iframe.contentWindow.Array;
const localArray = new Array();
console.log(localArray instanceof Array);        // true
console.log(localArray instanceof iframeArray);  // false (!)
Оператор instanceof перевіряє прототип об'єкта, тому він не працюватиме коректно для об'єктів, створених у різних контекстах виконання (наприклад, між різними VM-модулями або iframe у браузері). Для надійних перевірок використовуйте util.types.

Каталог util.types методів

Модуль util.types містить понад 30 предикатів для точної ідентифікації типів. Ось найважливіші з них:

import { types } from 'node:util';

// Перевірка примітивних обгорток
console.log(types.isBoxedPrimitive(new String('hello')));     // true
console.log(types.isBoxedPrimitive('hello'));                 // false

// Перевірка Promise
console.log(types.isPromise(Promise.resolve(42)));            // true
console.log(types.isPromise({ then: () => {} }));             // false (thenable, але не Promise)

// Перевірка вбудованих колекцій
console.log(types.isMap(new Map()));                          // true
console.log(types.isSet(new Set()));                          // true
console.log(types.isWeakMap(new WeakMap()));                  // true

// Перевірка типізованих масивів
console.log(types.isTypedArray(new Uint8Array(10)));          // true
console.log(types.isUint8Array(new Uint8Array(10)));          // true
console.log(types.isFloat64Array(new Float64Array(5)));       // true

// Перевірка спеціальних об'єктів Node.js
console.log(types.isDate(new Date()));                        // true
console.log(types.isRegExp(/test/));                          // true
console.log(types.isNativeError(new Error('fail')));          // true
console.log(types.isAsyncFunction(async () => {}));           // true
console.log(types.isGeneratorFunction(function* () {}));      // true

Практичний приклад: валідація вхідних даних API

Функція util.types незамінна при валідації даних, отриманих із зовнішніх джерел, де типи можуть бути непередбачуваними:

import { types } from 'node:util';

interface UserPayload {
  id: number;
  name: string;
  metadata: Map<string, unknown>;
  createdAt: Date;
}

function validateUserPayload(input: unknown): input is UserPayload {
  if (typeof input !== 'object' || input === null) {
    return false;
  }

  const payload = input as Record<string, unknown>;

  // Перевірка типів через util.types
  return (
    typeof payload.id === 'number' &&
    typeof payload.name === 'string' &&
    types.isMap(payload.metadata) &&
    types.isDate(payload.createdAt)
  );
}

// Тестування функції
const validPayload = {
  id: 42,
  name: 'Ольга',
  metadata: new Map([['role', 'admin']]),
  createdAt: new Date(),
};

const invalidPayload = {
  id: '42',  // Рядок замість числа
  name: 'Ольга',
  metadata: { role: 'admin' },  // Object замість Map
  createdAt: '2025-02-01',       // Рядок замість Date
};

console.log(validateUserPayload(validPayload));    // true
console.log(validateUserPayload(invalidPayload));  // false
У TypeScript функції з сигнатурою input is Type називаються type predicates і дозволяють компілятору звужувати тип змінної після перевірки. Це забезпечує type safety у runtime без додаткових приведень типів.

Перевірка проксі-об'єктів та WebAssembly

Модуль util.types також підтримує сучасні JavaScript-конструкції:

import { types } from 'node:util';

// Proxy
const target = { value: 42 };
const proxy = new Proxy(target, {
  get(obj, prop) {
    console.log(`Доступ до ${String(prop)}`);
    return obj[prop as keyof typeof obj];
  },
});

console.log(types.isProxy(proxy));  // true
console.log(types.isProxy(target)); // false

// WebAssembly Module
const wasmCode = new Uint8Array([0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]);
const wasmModule = new WebAssembly.Module(wasmCode);

console.log(types.isWebAssemblyCompiledModule(wasmModule)); // true
Перевірка isProxy() працює лише для об'єктів, створених через конструктор Proxy. Вона не може виявити "приховані" проксі, створені всередині сторонніх бібліотек, оскільки JavaScript не надає рефлексію для інспектування проксі-ланцюгів.

Форматування рядків: util.format()

Функція util.format() надає printf-подібний синтаксис для форматування рядків із підстановкою значень. Вона використовується всередині console.log(), але також доступна для ручного форматування.

Синтаксис заповнювачів

import { format } from 'node:util';

// %s — рядок (string)
console.log(format('Користувач: %s', 'Дмитро'));
// Користувач: Дмитро

// %d — число (decimal/integer)
console.log(format('Кількість: %d', 42));
// Кількість: 42

// %i — ціле число (integer)
console.log(format('ID: %i', 3.14));
// ID: 3 (округлення до цілого)

// %f — число з плаваючою комою (float)
console.log(format('Ціна: %f грн', 123.456));
// Ціна: 123.456 грн

// %j — JSON-серіалізація
console.log(format('Дані: %j', { user: 'Анна', age: 28 }));
// Дані: {"user":"Анна","age":28}

// %o — inspect() для об'єктів
const complexObj = { nested: { value: [1, 2, 3] } };
console.log(format('Об\'єкт: %o', complexObj));
// Об'єкт: { nested: { value: [ 1, 2, 3 ] } }

// %O — inspect() з опціями (глибше інспектування)
console.log(format('Деталі: %O', complexObj));

// %c — ігнорується (підтримка browser console.log CSS)
console.log(format('Текст %c', 'color: red'));
// Текст  (CSS ігнорується у Node.js)

// %% — літеральний символ %
console.log(format('Прогрес: 50%%'));
// Прогрес: 50%

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

import { format } from 'node:util';

interface LogContext {
  timestamp: Date;
  level: 'INFO' | 'WARN' | 'ERROR';
  userId?: number;
}

function log(context: LogContext, message: string, ...args: unknown[]): void {
  const formattedMessage = format(message, ...args);
  const contextStr = format(
    '[%s] [%s] %s',
    context.timestamp.toISOString(),
    context.level,
    context.userId ? `User ${context.userId}` : 'System'
  );

  console.log(`${contextStr} ${formattedMessage}`);
}

// Використання
log(
  { timestamp: new Date(), level: 'INFO', userId: 42 },
  'Завантажено %d файлів за %f секунд',
  15,
  2.34
);

// Вивід:
// [2026-09-02T12:00:00.000Z] [INFO] User 42 Завантажено 15 файлів за 2.34 секунд
Якщо передано більше аргументів, ніж заповнювачів, зайві аргументи додаються в кінець рядка через пробіл. Якщо заповнювачів більше, ніж аргументів, вони залишаються у рядку без змін.

Додаткові утиліти модуля util

Модуль util містить кілька менш популярних, але корисних функцій для специфічних завдань.

util.deprecate(): позначення застарілого коду

Функція deprecate() обгортає функцію та виводить попередження при першому виклику. Це стандартний механізм для позначення API, що застаріває.

import { deprecate } from 'node:util';

// Стара версія функції
function oldCalculate(x: number, y: number): number {
  return x + y;
}

// Обгортка з попередженням
const calculate = deprecate(
  oldCalculate,
  'calculate() застаріла. Використовуйте add() замість неї.',
  'DEP0001'  // Код deprecation (опціонально)
);

// Перший виклик виведе попередження у stderr
console.log(calculate(5, 3));
// DeprecationWarning: calculate() застаріла. Використовуйте add() замість неї.
// (Node.js:12345) [DEP0001] DeprecationWarning

// Наступні виклики не виводять попередження
console.log(calculate(10, 20));
Попередження про застарівання виводиться лише один раз для кожної функції, щоб не засмічувати логи. Для примусового показу всіх deprecation warnings запустіть Node.js із прапором --trace-deprecation.

util.inherits(): класичне наслідування (Legacy)

Функція inherits() забезпечувала прототипне наслідування до появи класів ES2015. У сучасному коді вона застаріла, але зустрічається у legacy-бібліотеках.

import { inherits } from 'node:util';
import { EventEmitter } from 'node:events';

// Legacy-спосіб (ES5)
function MyStream() {
  EventEmitter.call(this);
}

inherits(MyStream, EventEmitter);

MyStream.prototype.write = function (data: string) {
  this.emit('data', data);
};

// Сучасний спосіб (ES2015+)
class ModernStream extends EventEmitter {
  write(data: string): void {
    this.emit('data', data);
  }
}
Використання util.inherits() у новому коді не рекомендується. Завжди віддавайте перевагу синтаксису class та extends, який є стандартом ECMAScript та підтримується TypeScript нативно.

util.TextEncoder та util.TextDecoder: робота з Unicode

Класи TextEncoder та TextDecoder надають стандартний Web API для перетворення рядків у байтові масиви (UTF-8) та назад.

import { TextEncoder, TextDecoder } from 'node:util';

// Кодування рядка у Uint8Array
const encoder = new TextEncoder();
const encoded = encoder.encode('Привіт, Світ! 🌍');

console.log(encoded);
// Uint8Array(23) [
//   208, 159, 209, 128, 208, 184, 208, 178,
//   209, 150, 209, 130, ...
// ]

console.log('Розмір у байтах:', encoded.byteLength); // 23 байти (UTF-8)

// Декодування Uint8Array назад у рядок
const decoder = new TextDecoder('utf-8');
const decoded = decoder.decode(encoded);

console.log(decoded); // Привіт, Світ! 🌍
TextEncoder завжди кодує у UTF-8 і не підтримує інші кодування. Для декодування можна вказати кодування у конструкторі TextDecoder, включаючи utf-8, utf-16le, windows-1252, iso-8859-1 тощо.

Приклад декодування з іншим кодуванням:

import { TextDecoder } from 'node:util';

// Байтовий масив у кодуванні Windows-1251 (кирилиця)
const win1251Bytes = new Uint8Array([207, 240, 232, 226, 179, 242]);

const decoder = new TextDecoder('windows-1251');
const text = decoder.decode(win1251Bytes);

console.log(text); // Привіт (якщо байти у Windows-1251)

util.getSystemErrorName(): декодування системних помилок

Функція getSystemErrorName() перетворює числовий код помилки операційної системи у символічне ім'я (наприклад, ENOENT, EACCES).

import { getSystemErrorName } from 'node:util';
import fs from 'node:fs';

try {
  fs.readFileSync('/nonexistent/file.txt');
} catch (err: any) {
  console.log('Код помилки:', err.errno);          // -2
  console.log('Назва:', getSystemErrorName(err.errno)); // ENOENT
  console.log('Опис:', err.message);
  // ENOENT: no such file or directory, open '/nonexistent/file.txt'
}

Це корисно для логування та обробки помилок на рівні операційної системи, особливо при роботі з файлами, сокетами та процесами.

Архітектурні паттерни застосування util

Міграція legacy-проєкту на async/await

Типовий сценарій у великих кодових базах: поступова заміна callback-функцій на Promise-based API без переписування всього коду одразу.

import { promisify } from 'node:util';
import fs from 'node:fs';
import crypto from 'node:crypto';

// Legacy callback-функції
const readFile = promisify(fs.readFile);
const writeFile = promisify(fs.writeFile);
const randomBytes = promisify(crypto.randomBytes);

// Нова async-функція, що використовує старі API
async function processSecureFile(inputPath: string, outputPath: string): Promise<void> {
  // Читання файлу
  const content = await readFile(inputPath, 'utf8');

  // Генерація ключа шифрування
  const key = await randomBytes(32);

  // Шифрування (спрощений приклад)
  const encrypted = Buffer.from(content).toString('base64');

  // Запис результату
  await writeFile(outputPath, encrypted, 'utf8');

  console.log(`Файл оброблено: ${inputPath} → ${outputPath}`);
  console.log(`Ключ: ${key.toString('hex')}`);
}

// Виклик
processSecureFile('./data.txt', './data.encrypted.txt')
  .catch(err => console.error('Помилка обробки:', err));
При міграції великих проєктів створіть окремий модуль lib/promisified.ts, де зберігайте всі перетворені функції. Це дозволить уникнути дублювання викликів promisify() у різних частинах коду.

Створення діагностичного логера з inspect

Реалізація логера, що автоматично форматує складні об'єкти:

import { inspect, format } from 'node:util';
import type { InspectOptions } from 'node:util';

class Logger {
  private inspectOptions: InspectOptions = {
    depth: 5,
    colors: true,
    compact: false,
    breakLength: 80,
  };

  info(message: string, ...args: unknown[]): void {
    const formatted = format(message, ...this.formatArgs(args));
    console.log(`[INFO] ${formatted}`);
  }

  error(message: string, error: Error, context?: Record<string, unknown>): void {
    console.error(`[ERROR] ${message}`);
    console.error('  Stack:', error.stack);

    if (context) {
      console.error('  Context:', inspect(context, this.inspectOptions));
    }
  }

  debug(label: string, data: unknown): void {
    console.debug(`[DEBUG] ${label}:`);
    console.debug(inspect(data, this.inspectOptions));
  }

  private formatArgs(args: unknown[]): unknown[] {
    return args.map(arg => {
      if (typeof arg === 'object' && arg !== null) {
        return inspect(arg, { ...this.inspectOptions, colors: false });
      }
      return arg;
    });
  }
}

// Використання
const logger = new Logger();

logger.info('Сервер запущено на порту %d', 3000);

logger.debug('Конфігурація бази даних', {
  host: 'localhost',
  port: 5432,
  pool: { min: 2, max: 10 },
});

try {
  throw new Error('Втрачено з\'єднання з БД');
} catch (err) {
  logger.error('Критична помилка', err as Error, {
    timestamp: new Date(),
    userId: 42,
  });
}
node logger-demo.ts
$ node logger-demo.ts
[INFO] Сервер запущено на порту 3000
[DEBUG] Конфігурація бази даних:
{
host: 'localhost',
port: 5432,
pool: { min: 2, max: 10 }
}
[ERROR] Критична помилка
Stack: Error: Втрачено з'єднання з БД
at main (logger-demo.ts:32:9)
Context: { timestamp: 2026-09-02T..., userId: 42 }

Питання для самоконтролю

Підсумок: коли використовувати util

Модуль util — це інструментальний пояс (toolbelt) для вирішення рутинних завдань у Node.js. Основні сценарії застосування:

🔄 Міграція коду

  • Перетворення callback на Promise через promisify()
  • Поступова модернізація legacy-проєктів
  • Забезпечення зворотної сумісності через callbackify()

🐛 Налагодження

  • Інспектування складних об'єктів через inspect()
  • Візуалізація циклічних структур та глибокої вкладеності
  • Кастомний вивід для класів через [util.inspect.custom]

🔍 Перевірка типів

  • Точна runtime-ідентифікація типів через util.types
  • Валідація даних API без сторонніх бібліотек
  • Перевірка Proxy, Promise, Map, Buffer тощо

📝 Форматування

  • Printf-стиль форматування через format()
  • Створення структурованих логів
  • Інтерполяція значень у діагностичні повідомлення
Модуль util часто недооцінюється розробниками, які встановлюють сторонні пакети для завдань, які вже вирішені у стандартній бібліотеці. Перш ніж додавати залежність, перевірте, чи util не надає потрібну функціональність.

Ключовий принцип роботи з util — використовувати його як прошарок сумісності та діагностики, але не покладатися на нього для бізнес-логіки. Функції на кшталт promisify() є мостом до сучасного коду, а inspect() та types — інструментами для розуміння поведінки системи у runtime.

У наступних лекціях ми застосуємо ці знання для роботи зі змінними оточення, інструментами налагодження та оптимізації продуктивності Node.js-застосунків.

Copyright © 2026