AWS DynamoDB V3 SDK 编写泛型send包装器类型不匹配问题
问题说明
在TypeScript环境下基于AWS SDK V3封装DynamoDB客户端send方法、实现统一错误日志能力时,传入具体DynamoDB命令实例会触发类型不匹配报错,但直接调用原生client.send()传入同个命令实例却可以正常通过类型校验。
复现代码
import { DynamoDBClient, UpdateItemCommand, } from '@aws-sdk/client-dynamodb'; // 错误的类型提取方式 type CommandType = Parameters<InstanceType<typeof DynamoDBClient>['send']>[0]; function fn(cmd: CommandType) { const client = new DynamoDBClient({ region: 'us-west-2' }); client.send(cmd); } const updateItemCommand = new UpdateItemCommand({ /* 业务参数 */ }); // 此处报类型错误 fn(updateItemCommand)
报错信息
Type 'UpdateItemCommand' is not assignable to type 'Command<ServiceInputTypes, ServiceInputTypes, ServiceOutputTypes, ServiceOutputTypes, SmithyResolvedConfiguration<HttpHandlerOptions>>'. The types of 'middlewareStack.add' are incompatible between these types. Type '{ (middleware: InitializeMiddleware<UpdateItemCommandInput, UpdateItemCommandOutput>, options?: (InitializeHandlerOptions & AbsoluteLocation) | undefined): void; (middleware: SerializeMiddleware<...>, options: SerializeHandlerOptions & AbsoluteLocation): void; (middleware: BuildMiddleware<...>, options: BuildHandler...' is not assignable to type '{ (middleware: InitializeMiddleware<ServiceInputTypes, ServiceOutputTypes>, options?: (InitializeHandlerOptions & AbsoluteLocation) | undefined): void; (middleware: SerializeMiddleware<...>, options: SerializeHandlerOptions & AbsoluteLocation): void; (middleware: BuildMiddleware<...>, options: BuildHandlerOptions & ...'. Types of parameters 'middleware' and 'middleware' are incompatible. Types of parameters 'next' and 'next' are incompatible. Type 'InitializeHandler<UpdateItemCommandInput, UpdateItemCommandOutput>' is not assignable to type 'InitializeHandler<ServiceInputTypes, ServiceOutputTypes>'. Type 'ServiceInputTypes' is not assignable to type 'UpdateItemCommandInput'.ts(2322)
正常运行的直接调用示例
import { DynamoDBClient, UpdateItemCommand, } from '@aws-sdk/client-dynamodb'; const updateItemCommand = new UpdateItemCommand({ /* 业务参数 */ }); const client = new DynamoDBClient({ region: 'us-west-2' }); // 无类型报错 client.send(updateItemCommand);
问题原因
直接通过Parameters<InstanceType<typeof DynamoDBClient>['send']>[0]提取到的命令类型,是绑定了DynamoDB全量操作输入/输出联合类型(ServiceInputTypes/ServiceOutputTypes)的Command基类。
具体命令实例(比如UpdateItemCommand)是携带专属输入、输出泛型的Command子类,TS对方法参数做逆变类型校验时,会检查命令实例上middlewareStack.add方法的类型兼容性,最终触发类型不匹配报错。
直接调用client.send()时,TS会自动根据传入的命令实例推断send方法的泛型参数,不需要做全量联合类型的兼容性匹配,因此不会报错。
解决方案
使用泛型约束传入的命令类型,不要直接硬编码提取到的联合类型Command,两种可落地的写法如下:
写法1:基于Smithy Client的Command类型做泛型约束
import { DynamoDBClient, UpdateItemCommand, } from '@aws-sdk/client-dynamodb'; import { Command } from '@aws-sdk/smithy-client'; // 客户端全局复用,不要每次调用都新建实例 const client = new DynamoDBClient({ region: 'us-west-2' }); async function sendDynamoCommand<Input extends object, Output extends object>( cmd: Command<Input, Output, any, any, any> ): Promise<Output> { try { return await client.send(cmd); } catch (err) { // 统一错误日志逻辑 console.error(`DynamoDB命令执行失败,命令名:${cmd.constructor.name}`, err); throw err; } } const updateItemCommand = new UpdateItemCommand({ /* 业务参数 */ }); // 类型校验正常 sendDynamoCommand(updateItemCommand);
写法2:基于客户端实例send方法做泛型约束
无需额外导入Command类型,直接基于已初始化的客户端实例定义类型:
import { DynamoDBClient, UpdateItemCommand, } from '@aws-sdk/client-dynamodb'; const client = new DynamoDBClient({ region: 'us-west-2' }); async function sendDynamoCommand<T extends Parameters<typeof client.send>[0]>( cmd: T ): Promise<Awaited<ReturnType<typeof client.send<T>>>> { try { return await client.send(cmd); } catch (err) { console.error(`DynamoDB命令执行失败,命令名:${cmd.constructor.name}`, err); throw err; } } const updateItemCommand = new UpdateItemCommand({ /* 业务参数 */ }); // 类型校验正常 sendDynamoCommand(updateItemCommand);
注意:DynamoDBClient实例需要全局复用,避免每次调用方法都新建实例带来的连接开销、性能损耗。
内容的提问来源于stack exchange,提问作者Žilvinas Rudžionis
相关产品推荐
相关产品推荐

