Как экспортировать значение из замыкания в TypeScript

Короткий ответ: из замыкания в TypeScript лучше экспортировать не саму скрытую переменную, а функцию, фабрику или объект методов, которые получают доступ к этой переменной. Замыкание остается приватным, а наружу выходит типизированный API

Плохая формулировка задачи звучит так: “хочу экспортировать переменную изнутри функции”. Правильная формулировка: “хочу дать внешнему коду управляемый доступ к состоянию, которое хранится внутри замыкания”

Пример с приватным счетчиком

export function createCounter(initialValue = 0) {
  let value = initialValue;

  return {
    increment(): number {
      value += 1;
      return value;
    },
    getValue(): number {
      return value;
    },
    reset(): void {
      value = initialValue;
    },
  };
}

Использование:

import { createCounter } from "./counter";

const counter = createCounter(10);

console.log(counter.increment());
console.log(counter.getValue());

Переменная value не экспортируется напрямую. Ее нельзя случайно переписать из другого файла, но методы increment, getValue и reset имеют к ней доступ

Типизируйте публичный API

Чтобы внешний контракт был явным, вынесите тип:

export type Counter = {
  increment(): number;
  getValue(): number;
  reset(): void;
};

export function createCounter(initialValue = 0): Counter {
  let value = initialValue;

  return {
    increment() {
      value += 1;
      return value;
    },
    getValue() {
      return value;
    },
    reset() {
      value = initialValue;
    },
  };
}

Теперь другой файл может импортировать и фабрику, и тип:

import { createCounter, type Counter } from "./counter";

const counter: Counter = createCounter();

Если нужно экспортировать готовый экземпляр

Иногда нужен один общий объект на весь модуль:

function createStore() {
  let token: string | null = null;

  return {
    setToken(value: string) {
      token = value;
    },
    getToken() {
      return token;
    },
    clear() {
      token = null;
    },
  };
}

export const authStore = createStore();

Так работает модульный singleton: состояние закрыто внутри createStore, а наружу экспортируется только управляемый объект

Если нужно экспортировать только функцию доступа

Иногда не нужен целый объект методов. Достаточно экспортировать функцию, которая сама держит замыкание:

let cachedConfig: { apiUrl: string } | null = null;

export function getConfig() {
  if (!cachedConfig) {
    cachedConfig = { apiUrl: "https://api.example.com" };
  }

  return cachedConfig;
}

Это тоже замыкание на уровне модуля: переменная cachedConfig не экспортируется, но функция getConfig управляет доступом к ней. Такой вариант удобен для lazy-init, кеширования настроек и простых сервисов

Если наружный код не должен менять объект, возвращайте копию:

return { ...cachedConfig };

Так вы снижаете риск, что другой модуль случайно испортит внутреннее состояние

Частые ошибки

Первая ошибка — пытаться написать export внутри функции. В TypeScript/ES modules экспорт делается на верхнем уровне модуля, а не внутри блока или функции

Вторая ошибка — вернуть ссылку на изменяемый внутренний объект без защиты. Если нужно сохранить инварианты, отдавайте копию или методы

Третья ошибка — использовать any, когда можно описать тип возвращаемого API. Тогда весь смысл TypeScript в этом месте теряется

Четвертая ошибка — путать export и return. return возвращает значение из функции, а export делает сущность доступной из другого модуля. Для замыкания обычно нужны оба механизма: export function createStore(), а внутри этой функции return { ... }

Самопроверка

Создайте createCounter, импортируйте его в другой файл и попробуйте обратиться к value напрямую. Это не должно быть возможно. Затем вызовите increment() и getValue() — эти методы должны работать. Значит, замыкание сохранено, а экспорт сделан правильно

Что почитать дальше по TypeScript

Если нужен общий маршрут по теме, откройте рубрику TypeScript. Для соседних задач пригодятся эти разборы:

Оцените статью
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x