将TypeScript类转为Lambda Layer时遇exports未定义错误求助
问题:将TypeScript类打包为Lambda Layer后运行报错
我尝试把下面这个AthenaHelper TypeScript类做成Node.js的Lambda Layer:
import { Athena } from "aws-sdk"; export class AthenaHelper { private athena = new Athena(); public queryAthena = async ( databaseParams: Athena.StartQueryExecutionInput ) => { const { QueryExecutionId: queryExecutionId } = (await this.athena.startQueryExecution(databaseParams).promise()) ?? {}; if (!queryExecutionId) { throw new Error("No query execution ID"); } const params = { QueryExecutionId: queryExecutionId, }; // 等待查询完成 await this.waitForQueryCompletion(queryExecutionId); // 以JSON格式获取Athena查询结果 const results = await this.getQueryResultsInJsonFormat(params); return results; }; private waitForQueryCompletion = async ( queryExecutionId: string | undefined ) => { try { if (!queryExecutionId) { throw new Error("No query execution ID"); } let state: string | null | undefined = null; while ( state !== "SUCCEEDED" && state !== "FAILED" && state !== "CANCELLED" ) { await new Promise((res) => setTimeout(res, 1000)); const { QueryExecution } = await this.athena .getQueryExecution({ QueryExecutionId: queryExecutionId, }) .promise(); state = QueryExecution?.Status?.State; } return state; } catch (error) { throw error; } }; private getQueryResultsInJsonFormat = async ( params: Athena.GetQueryResultsInput ): Promise<Record<string, string | null>[]> => { const result: Athena.GetQueryResultsOutput = await this.athena .getQueryResults(params) .promise(); const columnInfo: Athena.ColumnInfo[] = result.ResultSet?.ResultSetMetadata?.ColumnInfo || []; const rows = // 第一行是列名,这里跳过 result.ResultSet?.Rows?.slice(1).map((row) => { const rowData: Record<string, string | null> = {}; row.Data?.forEach((data, index) => { rowData[columnInfo[index].Name] = data?.VarCharValue || null; }); return rowData; }) || []; return rows; }; public helloWorld() { console.log("Hello World"); } }
用tsc编译成原生JavaScript后上传到Lambda Layer,然后运行下面的Lambda函数:
exports.handler = async (event, context) => { try { const AthenaHelper = require('/opt/nodejs/AthenaHelper'); const athenaHelper = new AthenaHelper() athenaHelper.helloWorld() return { statusCode: 200, body: 'Hello, World!' }; } catch (error) { console.log('error', JSON.stringify(error)) } };
执行时出现错误:
{ "errorType": "ReferenceError", "errorMessage": "exports is not defined in ES module scope", }
我的TS Config配置如下:
{ "compilerOptions": { "lib": ["es2018", "dom"] } }
求解决建议,有没有人试过把自定义TypeScript类导出为Lambda Layer?
解决建议
这个错误的核心是编译后的JS文件是ES模块格式,但Lambda里用CommonJS的require()去加载,导致模块系统冲突。下面是具体的修复步骤:
1. 修改TypeScript配置,编译为CommonJS模块
更新你的tsconfig.json,明确指定模块格式为CommonJS,同时补充必要的编译选项:
{ "compilerOptions": { "lib": ["es2018"], "module": "CommonJS", "target": "ES2018", "outDir": "./dist", "esModuleInterop": true, "skipLibCheck": true, "strict": false }, "include": ["src/**/*"], "exclude": ["node_modules"] }
module: "CommonJS":强制编译成Lambda兼容的CommonJS格式,避免生成ES模块的export语法outDir:指定编译后的文件输出目录,方便打包上传esModuleInterop:解决CommonJS和ES模块之间的兼容性问题
2. 调整Lambda函数的导入方式
TypeScript编译CommonJS后,类会挂在exports对象上,需要修改导入语句:
// 解构导入类 const { AthenaHelper } = require('/opt/nodejs/AthenaHelper'); // 或者如果编译后是默认导出,用下面的写法: // const AthenaHelper = require('/opt/nodejs/AthenaHelper').default;
3. 正确打包Lambda Layer
- 编译TypeScript:执行
tsc命令,确保编译后的文件输出到dist目录 - 打包Layer:将
dist目录下的文件放到nodejs文件夹中(Lambda Layer的Node.js文件必须放在nodejs目录下),然后压缩成zip包上传
示例目录结构:lambda-layer.zip └── nodejs └── AthenaHelper.js
4. 备选方案:改用ES模块格式
如果想保持ES模块,需要:
- 在Lambda函数的根目录添加
package.json,声明"type": "module" - 修改Lambda代码为ES模块的
import语法:
同时确保TypeScript编译时export const handler = async (event, context) => { try { import AthenaHelper from '/opt/nodejs/AthenaHelper'; const athenaHelper = new AthenaHelper(); athenaHelper.helloWorld(); return { statusCode: 200, body: 'Hello, World!' }; } catch (error) { console.log('error', JSON.stringify(error)); } };module设置为ESNext或ES2020,并且编译后的JS文件使用export default语法。
内容的提问来源于stack exchange,提问作者Coder Noob
相关产品推荐
相关产品推荐

