import _ from "lodash" // satisfies instanceof,val is Type export type req_t = (fun: string, ...argArray: any[]) => Promise<any> export type res_t<T extends object> = { [K in keyof T]: T[K] extends (fun: string, ...argArray: any[]) => Promise<infer R> ? R : never }[keyof T] export function callObjProxy<T>(req: req_t): T { function proxy2(prevPath: string[]): any { return new Proxy(() => { }, { get(target, propKey) { if (typeof propKey !== 'string') { return undefined } const next = [...prevPath, propKey] return proxy2(next); }, apply(target, thisArg, argArray) { const fun = prevPath.join( '.') return req(fun, ...argArray); } }); } return proxy2([]) as T; } export interface callObjSend_t { catch(str: string): void } export async function callObj<T extends Record<string, any>>(binObj: T, ...args: any[]) { function sendErrmsg(str: string) { return ["catch", str] } try { const [funpath, ...funParams] = args || [] console.log("callObj", ...args) if (typeof funpath !== "string") { return sendErrmsg("callObj funpath不是string") } // 1. 解析路径 const p = funpath.replace(/[\[\]]/g, '.').split('.').filter(Boolean); if (p.length === 0) { return sendErrmsg("callObj 路径不存在") } // 2. 获取方法 const fun = _.get(binObj, p); if (typeof fun !== "function") { return sendErrmsg(`callObj 指定路径不是函数: ${funpath}`) } // 3. 获取方法的直接父对象(this 上下文) const parentPath = p.slice(0, -1); const context = parentPath.length > 0 ? _.get(binObj, parentPath) : binObj; if (context == null) { return sendErrmsg(`callObj 方法上下文不存在: ${funpath}`) } // 4. 正确执行 // console.log("callObj", { funpath, funParams }) const data = await fun.apply(context, funParams); // const data = await fun(...funParams); //Cannot read properties of undefined (reading 'broModules') return data } catch (err) { // console.log("callObj error", { funpath, funParams, err }) if (isError(err)) { return sendErrmsg(err.message) } else { return sendErrmsg("callObj未知错误".concat(String(err))) } } }

import _ from "lodash" // satisfies instanceof,val is Type export type req_t = (fun: string, ...argArray: any[]) => Promise<any> export type res_t<T extends object> = { [K in keyof T]: T[K] extends (fun: string, ...argArray: any[]) => Promise<infer R> ? R : never }[keyof T] export function callObjProxy<T>(req: req_t): T { function proxy2(prevPath: string[]): any { return new Proxy(() => { }, { get(target, propKey) { if (typeof propKey !== 'string') { return undefined } const next = [...prevPath, propKey] return proxy2(next); }, apply(target, thisArg, argArray) { const fun = prevPath.join( '.') return req(fun, ...argArray); } }); } return proxy2([]) as T; } export interface callObjSend_t { catch(str: string): void } export async function callObj<T extends Record<string, any>>(binObj: T, ...args: any[]) { function sendErrmsg(str: string) { return ["catch", str] } try { const [funpath, ...funParams] = args || [] console.log("callObj", ...args) if (typeof funpath !== "string") { return sendErrmsg("callObj funpath不是string") } // 1. 解析路径 const p = funpath.replace(/[\[\]]/g, '.').split('.').filter(Boolean); if (p.length === 0) { return sendErrmsg("callObj 路径不存在") } // 2. 获取方法 const fun = _.get(binObj, p); if (typeof fun !== "function") { return sendErrmsg(`callObj 指定路径不是函数: ${funpath}`) } // 3. 获取方法的直接父对象(this 上下文) const parentPath = p.slice(0, -1); const context = parentPath.length > 0 ? _.get(binObj, parentPath) : binObj; if (context == null) { return sendErrmsg(`callObj 方法上下文不存在: ${funpath}`) } // 4. 正确执行 // console.log("callObj", { funpath, funParams }) const data = await fun.apply(context, funParams); // const data = await fun(...funParams); //Cannot read properties of undefined (reading 'broModules') return data } catch (err) { // console.log("callObj error", { funpath, funParams, err }) if (isError(err)) { return sendErrmsg(err.message) } else { return sendErrmsg("callObj未知错误".concat(String(err))) } } }
用户359687679
12月29日
|
浏览 48

这段代码实现了一个动态代理调用机制,允许通过字符串路径调用对象的方法。以下是关键点分析:

核心功能

  1. callObjProxy<T>(req) - 创建类型安全的代理对象

    • 使用 Proxy 拦截属性和方法调用
    • 将链式调用转换为点分隔的字符串路径
    • 最终通过 req 函数执行远程调用
  2. callObj(binObj, ...args) - 执行实际的方法调用

    • 解析字符串路径(支持 a.b.ca[b].c 格式)
    • 使用 lodash 的 _.get 安全获取嵌套方法
    • 正确绑定 this 上下文到方法的父对象

主要问题与改进建议

1. 类型安全性问题

复制代码
// 当前:any 类型过多
export type req_t = (fun: string, ...argArray: any[]) => Promise<any>

// 建议:增加泛型约束
export type req_t<Args extends any[] = any[], R = any> = 
  (fun: string, ...args: Args) => Promise<R>

2. 错误处理优化

复制代码
// 当前:返回数组格式的错误
return ["catch", str]

// 建议:统一错误响应格式
export type CallResult<T = any> = 
  | { success: true; data: T }
  | { success: false; error: string }

export async function callObj<T extends Record<string, any>, R = any>(
  binObj: T,
  ...args: any[]
): Promise<CallResult<R>> {
  // ... 实现
}

3. 性能优化

  • 避免重复路径解析,可缓存解析结果
  • 考虑使用 Reflect.get 替代 _.get 减少依赖

4. 安全性增强

复制代码
// 添加路径白名单验证
const allowedPaths = ['api.', 'service.']; // 示例白名单

if (!allowedPaths.some(prefix => funpath.startsWith(prefix))) {
  return sendErrmsg("访问路径未授权");
}

使用示例

复制代码
// 定义接口
interface ApiService {
  user: {
    getInfo(id: number): Promise<UserInfo>;
    update(data: UserData): Promise<void>;
  }
}

// 创建代理
const api = callObjProxy<ApiService>(async (path, ...args) => {
  return await callObj(actualService, path, ...args);
});

// 类型安全调用
const userInfo = await api.user.getInfo(123);

注意事项

  1. Proxy 在严格模式下可能影响性能
  2. 路径解析需确保与后端约定一致
  3. 考虑添加调用超时和重试机制

如需继续排查,请点击下方【🔍追问按钮】提交补充信息

我要提问
复制内容
分享给好友
AI编程问答网 免责声明:
以上内容除特别注明外均来源于网友提问,AI编程问答网回答,权益归原著者所有;
 
下一篇:你是谁