TypeScript - 条件模块导入/导出
Posted
技术标签:
【中文标题】TypeScript - 条件模块导入/导出【英文标题】:TypeScript - conditional module import/export 【发布时间】:2012-11-06 19:12:14 【问题描述】:TypeScripts 以某种“声明性”的方式抽象出模块导入/导出。
但是如果我想根据一些运行时计算的条件导入或导出一些东西呢?
最常见的用例是在 Node.js 和 Windows Script Host 等平台之间共享代码。
TypeScript 自己的 io.ts 在 TSC 编译器中抽象输入/输出,手动破解内置的 TypeScript 自己的模块语法。这是唯一的方法吗?
附:仅将 import fs = module("fs") 粘贴到 if 语句中的问题在于 TypeScript 仅允许在顶层使用 import 语句。这意味着在 WSH 中 require("fs") 将被执行并且显然会失败,因为 require 是未定义的。
【问题讨论】:
【参考方案1】:TypeScript 中有一种动态导入机制,尽管实现方式因模块类型而异。
以下示例(针对 AMD)将有条件地加载模块:
declare function require(moduleNames: string[], onLoad: (...args: any[]) => void): void;
import * as ModuleAlias from './mod';
const someCondition = true;
if (someCondition)
require(["./mod"], (module: typeof ModuleAlias) =>
console.log(module.go());
);
文件顶部的import
语句是惰性的,除非if (someCondition)
条件为真,否则模块的实际加载不会发生。
您可以通过更改someCondition
并查看对网络选项卡的影响来测试这一点,或者您可以查看生成的代码...在动态版本中,"./mod"
不会出现在define
调用中。在非动态的情况下,它确实如此。
动态加载
define(["require", "exports"], function (require, exports)
"use strict";
Object.defineProperty(exports, "__esModule", value: true );
const someCondition = true;
if (someCondition)
require(["./mod"], (module) =>
console.log(module.go());
);
);
没有动态加载
define(["require", "exports", "./mod"], function (require, exports, ModuleAlias)
"use strict";
Object.defineProperty(exports, "__esModule", value: true );
const someCondition = true;
if (someCondition)
console.log(ModuleAlias.go());
);
【讨论】:
【参考方案2】:我同意他们只能拥有***范围这一事实充其量是次优的。除了您所说的问题外,这还意味着软件的初始加载时间较慢。例如,在 nodejs 中,如果该函数很少使用,我现在有时会在函数中加载一个模块。所以我的应用程序启动得更快,因为它还没有加载那个模块。
当然你也可以直接使用 require 或 AMD,但是你会错过一些打字的好处。
然而,我认为真正的问题在于 Harmony/es6 将模块定义为顶层,而 TS 似乎正在遵循该提议。所以不确定 TS 团队在不偏离标准的情况下能做多少。
【讨论】:
【参考方案3】:我有一个稍微笨拙但非常有效的解决方案,特别是如果您使用条件导入/导出进行单元测试。
具有始终发出的导出,但根据运行时值使内容发生变化。例如:
// outputModule.ts
export const priv = (process.env.BUILD_MODE === 'test')
? hydrateRecords, fillBlanks, extractHeaders
: null
然后在消费文件中,导入导出,检查导入的值是否存在,如果存在,则将您要单独导入的所有值分配给一组变量:
// importingModule.spec.ts
import priv from './outputModule';
const hydrateRecords, fillBlanks, extractHeaders = priv as any;
// these will exist if environment var BUILD_MODE==='test'
限制:
-
遗憾的是,您必须将导入设置为“任意”才能使编译器满意。
您需要检查是否定义了特定的导入(但随地区提供)。
导入文件需要定义这些值。因此,您必须确保导入文件确实需要模块(如果您正在处理仅在测试期间运行的文件,这很好),或者您必须为它们实际上没有被导出的情况定义替代值。
不过,就我的目的而言,它对我来说非常有效,希望它也适用于你。它对于单元测试私有方法特别有用。
【讨论】:
【参考方案4】:从TypeScript v2.4可以使用dynamic import实现条件导入
一个异步示例:
async function importModule(moduleName: string):Promise<any>
console.log("importing ", moduleName);
const importedModule = await import(moduleName);
console.log("\timported ...");
return importedModule;
let moduleName:string = "module-a";
let importedModule = await importModule(moduleName);
console.log("importedModule", importedModule);
【讨论】:
动态导入文档在 mdn developer.mozilla.org/en-US/docs/Web/javascript/Reference/…【参考方案5】:找不到将条件导出作为条件导入的直接方法。但 我发现 Andrew Faulkner 的回答很有用,但我对这些限制不满意。
遗憾的是,您必须将导入设置为“任意”才能使编译器满意。
我想出了一个解决上述限制的方法。这是我的步骤。
-
按照 Andrew 的回答将条件导出写为一个对象。
将导出的对象导入另一个模块。
解构它。
通过分配所有具有正确类型的解构项来定义新常量。
这是示例。
//CryptoUtil.ts
function encryptData(data : Buffer, key : Buffer) : Buffer
// My encryption mechanism.
// I return a Buffer here.
function decryptData(data : Buffer, key : Buffer) : Buffer
// My decryption mechanism.
// I return a Buffer here.
// Step 1
// Exporting things conditionally
export const _private = (process.env.NODE_ENV === "test") ?
__encryptData : encryptData,
__decryptData : decryptData,
: null;
注意我如何将encryptData
导出为__encryptData
,而不是直接导出为encryptData
。我这样做只是因为我可以认为__encryptData
在导入器模块中解构时是一个私有函数。这完全是我的喜好。
那么在导入东西的时候……
// CryptoUtil.test.ts
// Step 2
// import the exported object.
import _private from "./CryptoUtil";
// Step 3. De-structuring.
const
__encryptData,
__decryptData,
= _private as any;
// Step 4. Define new constants giving proper type.
const _encryptData : (data : string, password : Buffer) => Buffer = __encryptData;
const _decryptData : (encryptedData : Buffer, password : Buffer) => Buffer = __decryptData;
// Now I can use _encryptData, _decryptData having the proper type.
尽管我提出这种方式来解决安德鲁的第一个限制,但我的方法引入了一个新的限制。也就是说,您必须在两个地方定义类型。当您更改导出函数的类型时,它不会神奇地更改导入函数的类型。您必须手动更改它。
【讨论】:
【参考方案6】:import something from '../here';
import anything from '../there';
export const conditionalExport =
process.env.NODE_ENV === 'production' ? something : anything;
灵感来自Andrew answer。
【讨论】:
以上是关于TypeScript - 条件模块导入/导出的主要内容,如果未能解决你的问题,请参考以下文章