TypeScript为何标记联合类型属性不存在?TS3.7.2严格模式求优化方案
我懂你现在的处境——用graphql-codegen生成的这个联合类型,直接访问feedbacksCount就触发类型错误,虽然类型断言能绕过去,但总觉得不够严谨也不够优雅。下面给你几个更贴合TypeScript类型安全原则的方案,比硬断言靠谱多了:
1. 利用__typename做类型收窄(最推荐)
GraphQL生成的联合类型都会自带__typename字段,这可是个天然的类型标识。你直接用它做判断,TypeScript会自动帮你把类型收窄到对应的分支,完全不需要断言:
// 假设你已经拿到了查询结果里的topic const topic = queryResult.topic; if (topic && topic.__typename === 'TopicModelAsEditor') { // 到这里TS已经100%确定topic是TopicModelAsEditor类型 const feedbacksCount = topic.feedbacksCount; // 放心用,不会有类型错误 }
这种方式既安全(有运行时的类型校验),又符合TS的类型推导逻辑,比硬断言靠谱太多,还不用写额外的工具函数。
2. 自定义类型守卫(适合频繁复用的场景)
如果你在很多地方都需要判断这个类型,可以封装一个自定义类型守卫函数,代码复用性更高:
function isTopicEditor(topic: GetTopicFeedbacksCountQuery['topic']): topic is NonNullable<GetTopicFeedbacksCountQuery['topic']> & { __typename: 'TopicModelAsEditor' } { return topic?.__typename === 'TopicModelAsEditor'; } // 使用的时候就很简洁: if (isTopicEditor(topic)) { const feedbacksCount = topic.feedbacksCount; }
这个守卫函数会帮你同时处理topic可能为null或undefined的情况,一次封装到处用。
3. 调整GraphQL查询或Codegen配置(从根源优化)
如果你的业务逻辑里,只有当topic是编辑器类型时才需要feedbacksCount,可以检查下你的GraphQL查询是否足够精准。比如用片段明确区分不同类型的返回字段:
query GetTopicFeedbacksCount { topic { ... on TopicModelAsMember { id } ... on TopicModelAsEditor { id generateToken feedbacksCount } } }
这样codegen生成的类型会更清晰,不过你现在的类型已经是正确的,所以这个更多是预防后续问题。另外也可以看看codegen的typescript插件配置,比如开启strictScalars或者调整maybeValue,不过这个要结合项目全局配置来调整,别随便改。
4. 可选链+空值合并(兼容无数据场景)
如果你的场景允许feedbacksCount不存在时用默认值,结合类型收窄后可以这么写:
const feedbacksCount = topic?.__typename === 'TopicModelAsEditor' ? topic.feedbacksCount : 0;
或者更简洁的:
const feedbacksCount = (topic as TopicModelAsEditor)?.feedbacksCount ?? 0;
不过这个还是用到了断言,不如前面的类型收窄安全,只适合你确定topic大概率是编辑器类型的场景。
总结
最推荐的还是用__typename直接做类型判断,这是GraphQL联合类型在TS里最标准的处理方式,安全又简洁,完全替代硬断言的需求。
内容的提问来源于stack exchange,提问作者Capaj

