如何为含变量的GraphQL查询标签生成TypeScript定义?
解决Apollo CLI无法解析带模板字符串变量的GraphQL查询类型生成问题
我之前也碰到过一模一样的情况——apollo-cli的默认解析逻辑不会执行JavaScript模板字符串,所以当你的$gql.tag里出现${docType.PRODUCT}这种动态插值时,它没法解析出完整有效的GraphQL查询,直接就跳过这个文件了。下面给你几个可行的解决方案:
方案一:自定义文档加载器(对应你提到的类似Apollo Client自定义加载器的需求)
Apollo Tooling确实支持自定义文档加载器,你可以写一个加载器脚本,先执行目标文件里的模板字符串,把动态值替换成实际内容,再把完整的GraphQL查询传给apollo-cli处理。
具体步骤:
- 新建一个加载器文件,比如
custom-gql-loader.js,用Babel来处理TypeScript文件并执行模板字符串:const { loadSync } = require('@babel/core'); const fs = require('fs'); module.exports = function loadDocumentSync(filePath) { const code = fs.readFileSync(filePath, 'utf8'); // 用Babel编译并执行文件,提取$gql.tag的调用结果 const result = loadSync(filePath, { presets: ['@babel/preset-typescript', '@babel/preset-env'], plugins: [ { visitor: { TaggedTemplateExpression(path) { if (path.node.tag.object.name === '$gql' && path.node.tag.property.name === 'tag') { // 拼接模板字符串和表达式,生成完整查询 const quasis = path.node.quasi.quasis.map(q => q.value.cooked); const expressions = path.node.quasi.expressions.map(exp => { // 这里根据你的代码上下文解析表达式值,示例中假设docType是全局可访问的 return docType[exp.property.name]; }); let fullQuery = ''; for (let i = 0; i < quasis.length; i++) { fullQuery += quasis[i]; if (expressions[i]) fullQuery += expressions[i]; } // 通过抛出错误传递查询内容给外部捕获 throw new Error(JSON.stringify({ query: fullQuery })); } } } } ] }); // 捕获查询内容并返回 try { require(filePath); } catch (e) { const { query } = JSON.parse(e.message); return { document: query }; } }; - 然后在apollo-cli命令中指定这个加载器:
apollo client:codegen --loader ./custom-gql-loader.js ...其他参数
这个方案需要你根据自己的代码结构调整加载器里的表达式解析逻辑,适合必须保留模板字符串插值的场景。
方案二:改用GraphQL变量替代模板字符串插值(更推荐)
其实更符合GraphQL最佳实践的方式是把动态值改成查询变量,这样apollo-cli能正常解析,同时代码也更规范:
修改你的查询代码:
const docType = { PRODUCT: 'Product' }; const query = $gql.tag` query searchProducts( $categoryId: IntID, $docType: String! # 添加这个变量定义 ){ searchProducts(categoryId: $categoryId, docType: $docType) { totalCount nodes { id, name } } } `;
之后在调用这个查询的时候,把docType.PRODUCT作为变量传入即可。这个方案不需要额外编写加载器,而且让查询更具可复用性,apollo-cli也能顺利生成对应的TypeScript类型定义。
总结
如果你的动态值是固定的业务常量,方案二更简洁可靠;如果必须用模板字符串动态生成查询内容,方案一的自定义加载器可以解决问题。
内容的提问来源于stack exchange,提问作者Rohit
相关产品推荐
相关产品推荐

