如何将动态生成的字符串转换为TypeScript代码以构建resolvers对象?
动态生成GraphQL Resolvers的可行方案
嘿,你的思路其实是可行的,但在TypeScript环境下直接通过字符串转代码的方式需要注意安全和类型问题,而且还有更稳妥的替代方案,我给你详细梳理下:
1. 直接用eval实现你的思路(不推荐)
你想通过构造字符串再转成代码的方式是可以实现的,比如用eval执行拼接后的代码字符串:
// 假设你的links数据已经定义 const links = [{ id: '1', url: 'example.com' }, /* 其他数据 */]; // 从查询结果生成的函数字符串 const strCode = `info: () => 'teeeeest', feed: () => links, get: (root, args) => { let indLink = links.findIndex((el) => el.id === args.id); return links[indLink]; }`; // 拼接成完整的resolvers对象代码 const fullResolverCode = `({ Query: { ${strCode} } })`; // 执行代码得到resolvers const resolvers = eval(fullResolverCode);
⚠️ 注意:这种方式非常不推荐,原因有两个:
- 安全风险:
eval会执行任意传入的代码,如果strCode的来源不可信(比如用户输入、第三方接口),很容易引发代码注入攻击。 - 类型丢失:TypeScript无法对
eval生成的代码做类型检查,你的resolvers会失去类型提示,不利于后续维护。
2. 推荐:用对象动态构建(安全且保留类型)
其实resolvers本质就是一个普通的JavaScript对象,完全可以通过动态添加属性的方式来构建,不需要用字符串转代码的方式。这种方法既安全,又能保留TypeScript的类型支持:
示例代码:
// 假设这是你从查询结果获取到的resolver配置(可以是任何结构化数据,不一定是函数) const resolverConfigs = [ { name: 'info', // 直接定义函数逻辑,或者根据查询结果动态生成 handler: () => 'teeeeest' }, { name: 'feed', handler: () => links }, { name: 'get', handler: (root: unknown, args: { id: string }) => { const indLink = links.findIndex(el => el.id === args.id); return links[indLink]; } } ]; // 用reduce动态构建Query对象 const Query = resolverConfigs.reduce((acc, config) => { acc[config.name] = config.handler; return acc; }, {} as Record<string, (...args: any[]) => any>); // 最终的resolvers对象 const resolvers = { Query };
如果需要更精确的类型,可以给resolverConfigs定义接口,比如:
interface ResolverConfig { name: string; handler: (root: unknown, args: Record<string, any>) => any; } const resolverConfigs: ResolverConfig[] = [/* 配置内容 */];
这样TypeScript就能自动推断Query和resolvers的类型,开发时能获得完整的类型提示。
3. 特殊场景:只能拿到函数体字符串的情况
如果你的查询结果只能拿到函数体的字符串(比如从数据库存储的字符串),可以用Function构造函数来生成函数,但同样要注意安全问题:
// 假设从查询结果拿到的是函数名和函数体字符串 const resolverFunctionData = [ { name: 'info', body: `return 'teeeeest';` }, { name: 'feed', body: `return links;` }, { name: 'get', body: `let indLink = links.findIndex(el => el.id === args.id); return links[indLink];` } ]; const Query = resolverFunctionData.reduce((acc, item) => { // 创建函数,传入需要的参数(root, args),并让函数捕获当前作用域的links acc[item.name] = (root: unknown, args: { id: string }) => { // 用Function构造函数执行函数体,传入需要的变量 return new Function('links', 'args', item.body)(links, args); }; return acc; }, {} as Record<string, (...args: any[]) => any>); const resolvers = { Query };
⚠️ 注意:这种方式依然存在安全风险,只有当item.body的来源绝对可信时才能使用。
总结
- 优先选择对象动态构建的方式,安全、高效且保留TypeScript类型支持。
- 尽量避免使用
eval或Function构造函数,除非你能完全确保字符串来源的安全性,并且接受类型丢失的代价。
内容的提问来源于stack exchange,提问作者cascadox
相关产品推荐
相关产品推荐

