TypeScript基于同函数其他参数条件约束handler参数可选性
实现结论
该条件类型约束完全可以实现,且可以扩展适配任意长度的clients元组场景,不需要手动维护固定长度的类型声明。
2个客户端上限的实现
当前验证最多2个客户端的场景,直接用TypeScript函数重载就能满足需求,分别定义入参长度为1、2时的类型规则即可:
// 对齐现有定义的基础类型 type DatabasePoolType = unknown; type DatabaseTransactionConnectionType = unknown; // 重载签名:传入2个有效client时,innerTransaction必填 export function createNestedTransactions<T>( clients: [DatabasePoolType, DatabasePoolType], handler: ( outerTransaction: DatabaseTransactionConnectionType, innerTransaction: DatabaseTransactionConnectionType ) => Promise<T> ): Promise<T>; // 重载签名:传入1个client时,innerTransaction可选 export function createNestedTransactions<T>( clients: [DatabasePoolType], handler: ( outerTransaction: DatabaseTransactionConnectionType, innerTransaction?: DatabaseTransactionConnectionType ) => Promise<T> ): Promise<T>; // 实现签名,兼容上述两种重载 export function createNestedTransactions<T>( clients: [DatabasePoolType, DatabasePoolType?], handler: ( outerTransaction: DatabaseTransactionConnectionType, innerTransaction?: DatabaseTransactionConnectionType ) => Promise<T> ): Promise<T> { // 内部实现逻辑:按clients长度开启对应数量的嵌套事务,传入handler执行 return Promise.resolve({} as T); }
这个实现完全匹配给出的两个调用场景的校验规则:传入两个已定义的client时,TS会要求handler必须声明innerTransaction参数且为必填;仅传入1个client时,innerTransaction自动标记为可选,未传该参数也不会报类型错误。
任意长度元组的通用扩展
如果要支持任意数量的客户端,不需要逐个数写重载,利用TypeScript的元组映射能力就可以自动推导handler的参数列表,核心是先捕获传入clients的精确元组类型,再逐位映射为对应的事务连接类型,同时自动保留元组每一位的可选/必填属性:
type DatabasePoolType = unknown; type DatabaseTransactionConnectionType = unknown; // 将连接池元组逐位映射为事务连接元组,保留可选性修饰符 type MapClientsToTransactions<TClients extends DatabasePoolType[]> = { [K in keyof TClients]: DatabaseTransactionConnectionType } export const createNestedTransactions = < T, // 约束clients为至少包含1个连接池的元组,禁止空数组入参 TClients extends [DatabasePoolType, ...DatabasePoolType[]] >( clients: TClients, // 映射后的事务元组直接作为handler的剩余参数,长度和类型自动对齐 handler: (...transactions: MapClientsToTransactions<TClients>) => Promise<T> ): Promise<T> => { // 内部实现:遍历clients顺序开启嵌套事务,按顺序传入handler return Promise.resolve({} as T); }
这个通用版本不需要提前约定客户端数量,传入N个有效连接池,TS就会自动要求handler接收N个必填的事务参数,比如传入3个连接池时,校验规则如下:
// 3个客户端场景,handler自动获得3个必填的事务参数 createNestedTransactions([pool1, pool2, pool3], (t1, t2, t3) => { // t1、t2、t3均为必填,不需要做可选判断 return Promise.resolve() })
如果传入的clients元组某一位是可选参数,映射后的事务参数也会自动变为可选,和入参规则完全对齐。
*注意:如果业务上要求传入的clients每一位都必须是有效连接池实例、不允许传undefined占位,只需要保持现有泛型约束即可,TS会自动拦截传undefined的非法调用,此时传入N个有效实例,handler就必须接收N个必填的事务参数,完全不需要手动维护重载规则。
内容的提问来源于stack exchange,提问作者ssukienn
相关产品推荐
相关产品推荐

