如何在TypeScript编译ESM与CJS时为单个文件提供不同版本?
TypeScript同时编译ESM/CJS时实现文件版本自动匹配的方案
你可以通过TypeScript的paths路径映射结合双tsconfig配置,实现同一导入路径在ESM/CJS编译产物中自动对应不同版本的文件,无需额外打包工具或构建后处理。
核心思路
- 为条件导入逻辑准备两个TypeScript源文件(区分CJS/ESM版本)
- 在两个tsconfig中分别配置
paths,将统一的导入路径映射到对应版本的源文件 - 分别编译CJS和ESM产物到不同目录,确保导入逻辑自动适配环境
具体步骤
1. 源码文件结构
在源码目录下创建两个版本的条件导入文件,其余代码保持统一:
src/ ├── try-import.cjs.ts # CJS版本的条件导入逻辑 ├── try-import.esm.ts # ESM版本的条件导入逻辑 ├── try-import.types.ts # 可选:抽离共用类型定义 └── other-file.ts # 其他业务代码,统一导入try-import
CJS版本源文件(try-import.cjs.ts)
import type { SomeExportType } from './try-import.types'; let mod: { someExport: SomeExportType } | undefined; try { mod = require('something'); console.log('Imported successfully'); } catch (e) { console.error('Package not found'); } export const x = mod?.someExport ?? null;
ESM版本源文件(try-import.esm.ts)
import type { SomeExportType } from './try-import.types'; let mod: { someExport: SomeExportType } | undefined; try { mod = await import('something'); console.log('Imported!'); } catch (e) { console.error('Package not found'); } export const x = mod?.someExport ?? null;
共用类型定义(try-import.types.ts,可选)
export type SomeExportType = { /* 你的类型定义 */ };
2. 配置双tsconfig文件
CJS编译配置(tsconfig.cjs.json)
{ "compilerOptions": { "module": "CommonJS", "target": "ES2018", "outDir": "./dist/cjs", "rootDir": "./src", "baseUrl": ".", "paths": { "./try-import": ["./src/try-import.cjs.ts"] }, "declaration": true }, "include": ["src/**/*"] }
ESM编译配置(tsconfig.esm.json)
{ "compilerOptions": { "module": "ESNext", "target": "ES2022", // 需支持模块级await "outDir": "./dist/esm", "rootDir": "./src", "baseUrl": ".", "paths": { "./try-import": ["./src/try-import.esm.ts"] }, "declaration": true }, "include": ["src/**/*"] }
3. 统一导入逻辑
在所有业务代码中,直接使用统一路径导入:
// src/other-file.ts import { x } from './try-import'; // 正常使用x即可,编译时会自动匹配对应版本
4. 编译脚本
在package.json中添加编译命令,分别编译CJS和ESM产物:
{ "scripts": { "build:cjs": "tsc -p tsconfig.cjs.json", "build:esm": "tsc -p tsconfig.esm.json", "build": "npm run build:cjs && npm run build:esm" }, "main": "./dist/cjs/index.js", "module": "./dist/esm/index.js", "types": "./dist/cjs/index.d.ts" // 或合并类型到单独目录 }
关键注意事项
- 确保两个tsconfig的
rootDir一致,保证输出目录结构与源码匹配 - ESM版本需设置
module为ESNext/ES2022+,以支持模块级await - 若需要统一类型输出,可单独配置一个
tsconfig.types.json专门生成类型文件,避免重复编译 - 两个版本的导出内容需保持类型一致,建议通过共用类型文件约束
内容的提问来源于stack exchange,提问作者GregRos
相关产品推荐
相关产品推荐

