Apollo Server+TypeScript配置REST数据源时出现'any'类型不安全访问错误
看起来你遇到的问题是TypeScript编译器能正确识别上下文类型,但ESLint的@typescript-eslint插件却误报了不安全访问的错误——这种情况其实挺常见的,主要是因为ESLint的类型检查逻辑和TypeScript语言服务的推断能力有细微差异。
错误原因分析
你的ResolverCtx类型是ApplicationContext & { dataSources: ApplicationDataSources },而ApplicationContext用了Record<string, any>。虽然TypeScript编辑器的语言服务能正确解析交叉类型,识别出ctx.dataSources的具体类型,但@typescript-eslint插件在处理包含any的交叉类型时,会倾向于把整个ctx对象的属性当成any类型看待,从而触发no-unsafe-member-access和no-unsafe-call错误。
解决方案
1. 优化上下文类型定义(推荐)
避免使用Record<string, any>这种宽泛的类型,换成明确的上下文属性定义。如果你的ApplicationContext没有额外属性,直接定义为空对象即可:
// resolver-ctx.ts import MyRestDataSource from '../datasources/my-rest-datasource'; // 替换Record<string, any>为明确的类型,没有额外属性就用{} type ApplicationContext = {}; // 如果有实际需要的上下文属性,比如用户信息,可以这样定义: // type ApplicationContext = { user?: { id: string; name: string } }; type ApplicationDataSources = { myRestDataSource: MyRestDataSource; }; type ResolverCtx = ApplicationContext & { dataSources: ApplicationDataSources }; export default ResolverCtx;
这种方式从根源上消除了any类型的干扰,ESLint就能正确识别ctx的类型了。
2. 给上下文添加类型断言
如果你确实需要保留Record<string, any>,可以在 resolver 里给ctx添加类型断言,明确告诉ESLint它的真实类型:
// myResolvers.ts import { Resolvers } from 'types/codegen'; import ResolverCtx from 'types/resolver-ctx'; const myResolvers: Resolvers<ResolverCtx> = { Query: { getData: async (_, { id }, ctx) => { // 明确断言ctx的类型为ResolverCtx const typedCtx = ctx as ResolverCtx; return await typedCtx.dataSources.myRestDataSource.myGetMethod(id); } }, }; export default myResolvers;
3. 局部禁用ESLint规则(不推荐,仅应急使用)
如果以上两种方式都不适用,可以在报错的代码行局部禁用对应的ESLint规则,但这只是临时 workaround,不推荐长期使用:
// myResolvers.ts import { Resolvers } from 'types/codegen'; import ResolverCtx from 'types/resolver-ctx'; const myResolvers: Resolvers<ResolverCtx> = { Query: { getData: async (_, { id }, ctx) => { // 临时禁用这两个规则 // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call return await ctx.dataSources.myRestDataSource.myGetMethod(id); } }, }; export default myResolvers;
为什么编辑器能显示正确类型?
这是因为TypeScript的语言服务(编辑器用来提供类型提示的功能)比ESLint的@typescript-eslint插件更擅长解析复杂的交叉类型。它能忽略Record<string, any>的宽泛性,正确识别出交叉后的完整类型,但ESLint的规则检查会更严格地对待any类型的存在,从而产生误报。
内容的提问来源于stack exchange,提问作者Antfish

