Когда экспорт бочек вреден

Когда экспорт бочек вреден

18 ноября 2022 г.

Шаблон экспорта бочек описан в книге Басарата (глубокое погружение в TypeScript) и выглядит следующим образом. :

/// index.ts
export * from "./a";
export * from "./b";
// [···]
export * from "./z";

Это довольно распространено, большинство людей используют его, и я не виню их: это на самом деле очень полезно. Это избавляет нас от необходимости запоминать слишком много путей импорта и от необходимости явно экспортировать каждый символ из наших подмодулей внутри пакета.

Итак… да, я признаю, название этой статьи было чистой приманкой. Я не думаю, что эта схема всегда вредна, только иногда. Когда и почему тогда?

Почти всегда речь идет о связи

Побочные эффекты во время загрузки модуля

Хотя это и маловероятно (поскольку большинство из нас на собственном горьком опыте усвоили, что выполнять побочные эффекты во время загрузки модуля — плохая идея), это самый простой проблемный случай, который мы можем объяснить.

Когда нам «необходимо» выполнить некоторые побочные эффекты во время загрузки (какой бы ни была причина: синглтоны, инициализация ресурсов, исправление обезьян…) в одном из подмодулей, экспортированных звездочкой на нашем « бочка», мы не сможем избежать их при импорте чего-либо, совершенно не связанного с той же бочкой.

Например, если бы мы просто хотели импортировать служебную функцию, мы могли бы инициировать инициализацию некоторого ресурса, который не имел отношения к этой задаче (влияя на производительность без уважительной причины), или выполнить непреднамеренное исправление обезьяны, что может привести к серьезным проблемам. -для отладки проблем.

/// lib/index.ts
export * from "./a";
export * from "./b";
export * from "./c";

/// lib/a.ts
export const greet = (name = "stranger") => console.log(`Hello ${name}!`);
console.log("Loading module a"); // Side effect

/// lib/b.ts
export const square = (x: number) => x * x;
console.log("Loading module b"); // Side effect

/// lib/c.ts
export const cube = (x: number) => x * x * x;
console.log("Loading module c"); // Side effect

/// main.ts
import { greet } from "./lib";
greet("World");

/// main.js's output:
// --------------------------------------------------------------------
// Loading module a
// Loading module b // <- We didn't want this
// Loading module c // <- We didn't want this
// Hello World!

«Эффекты шрифта» во время проверки шрифта

Вы можете подумать, что предыдущий случай на самом деле не так уж и проблематичен, потому что в большинстве кодов, которые вы видите каждый день, не выполняются побочные эффекты (считайте, что вам повезло!), но на этом история не заканчивается.

Во-первых, я, вероятно, буду злоупотреблять термином "текстовый эффект" в следующих строках (я подозреваю, что этот термин уже имеет какое-то другое значение, но, учитывая, что у меня нет лучшего слова, я я пока придерживаюсь этого), я заранее прошу у вас прощения… и если вы знаете, как лучше описать это, пожалуйста, не стесняйтесь связаться со мной и сказать мне!

Хотя в большинстве случаев типы должны быть явно импортированы, чтобы иметь какой-либо эффект во время проверки типов, есть несколько интересных случаев, которые не работают таким образом, расширение модуля и модули окружения (оба основаны на одном и том же построить «объявить модуль»):

declare module "some_external_module" {
    // typing stuff in here
}

Это перезаписывает или дополняет типы, предоставляемые указанным модулем, и может использоваться (например) при использовании автоматически сгенерированного кода. Одним из интересных примеров этого является генерация кода GraphQL для TypeScript и то, как этот интегрирован с замечательной библиотекой Mercurius (созданной некоторыми из моих коллег на NearForm! 😜).

Я не случайно упомянул автоматически сгенерированный код и Mercurius: мне действительно приходилось иметь дело с бочкой, которая экспортировала один из этих автоматически сгенерированных файлов. Эта бочка находилась внутри якобы библиотеки универсальных типов (в контексте монорепозитория)… и из-за этого она «просачивала» типы из одного федеративного «шлюза» в другой, который должен был раскрыть совершенно несвязанный граф объектов. Распутывать это было неинтересно.

Сгенерированный код также может пострадать

Когда некоторые из наших модулей содержат только типы (но не символы среды выполнения), мы, вероятно, хотим, чтобы сгенерированный код на самом деле не выполнял импорт среды выполнения, поскольку в этом нет необходимости.

Если мы используем тип импорта вместо простого import или наш оператор импорта явно импортирует что-то, то это просто тип без аналога во время выполнения (в основном, не enum или class), то мы можем ожидать, что tsc оптимизирует этот импорт из сгенерированного кода JS.

К сожалению, tsc не так умен, когда дело доходит до «start-export», и мы не сможем его оптимизировать. Это правда, что компилятор может улучшиться в будущем, но пока я бы не стал затаить дыхание.

Косвенные проблемы

Бочкообразный шаблон облегчает введение случайных циклических ссылок. Это правда, что ошибка в данном случае не совсем в шаблоне, но мы должны предпочесть не иметь в нашем распоряжении ружья.

Я слишком много раз видел, как некоторые люди решают импортировать «символы» из «родственных» модулей через файл index.ts вместо того, чтобы напрямую обращаться к исходным модулям; ошибка здесь не в самой бочке (поскольку считается, что она потребляется «извне»), а в использовании «внутреннего» кода, как если бы это была внешняя библиотека.

Заключение

Экспорт бочек — отличный инструмент, если им правильно пользоваться, но существует большой риск злоупотребления им. Как и в случае с любым другим методом, мы должны знать о связанных с ним компромиссах и о том, при каких обстоятельствах они могут стать проблематичными, поэтому мы можем учитывать эти факторы при принятии решения о том, использовать их или нет.

:::информация Также опубликовано здесь.

:::


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE