TypeScript无法识别type-graphql useMiddleware的类型校验问题
你遇到的是TypeScript无法识别中间件已确保req.session.userId存在的类型安全问题,除了if判断、//@ts-ignore和类型断言,还有以下几种更优雅的解决方案:
1. 使用自定义参数装饰器封装用户ID获取逻辑
创建一个@UserId()装饰器,既封装从session中提取用户ID的逻辑,又通过运行时检查确保ID存在,同时让TypeScript自动推导类型为非可选值。
// 自定义装饰器 import { createParamDecorator } from 'type-graphql'; import { MyContext } from 'src/types'; export const UserId = createParamDecorator<MyContext>(({ context }) => { const userId = context.req.session.userId; if (!userId) { throw new Error('Not authenticated'); } return userId; });
在resolver中使用:
@Mutation(() => PostType) @UseMiddleware(isAuth) async createPost( @Arg('title') title: string, @Arg('text') text: string, @Ctx() { prisma }: MyContext, @UserId() authorId: string // 类型自动为string,无undefined提示 ): Promise<PostType> { return await prisma.post.create({ data: { title, text, authorId, }, }); }
2. 拆分Context类型,区分已认证与未认证场景
定义一个AuthenticatedContext类型,继承基础MyContext并将req.session.userId设为非可选,然后在中间件中转换上下文类型,确保经过认证的resolver使用正确的类型。
// 定义已认证上下文类型 import { MyContext } from 'src/types'; export type AuthenticatedContext = Omit<MyContext, 'req'> & { req: MyContext['req'] & { session: { userId: string } }; }; // 修改isAuth中间件,返回已认证上下文 export const isAuth: MiddlewareFn<MyContext, AuthenticatedContext> = ({ context }, next) => { if (context.req.session.userId === undefined) { throw new Error('Not authenticated'); } // 转换上下文类型 return next({ ...context, req: context.req as AuthenticatedContext['req'] }); };
在resolver中指定上下文类型:
@Mutation(() => PostType) @UseMiddleware(isAuth) async createPost( @Arg('title') title: string, @Arg('text') text: string, @Ctx() { prisma, req }: AuthenticatedContext // 使用已认证上下文类型 ): Promise<PostType> { return await prisma.post.create({ data: { title, text, authorId: req.session.userId, // TS不再提示undefined }, }); }
3. 编写类型断言函数增强类型安全
创建一个断言函数,在运行时验证用户ID存在的同时,告诉TypeScriptreq.session.userId是非可选的,比单纯的类型断言多一层运行时保障。
// 类型断言函数 import { MyContext } from 'src/types'; export function assertAuthenticated(req: MyContext['req']): asserts req is typeof req & { session: { userId: string } } { if (req.session.userId === undefined) { throw new Error('Not authenticated'); } }
在resolver中调用:
@Mutation(() => PostType) @UseMiddleware(isAuth) async createPost( @Arg('title') title: string, @Arg('text') text: string, @Ctx() { prisma, req }: MyContext ): Promise<PostType> { assertAuthenticated(req); // 调用后TS识别userId为非可选 return await prisma.post.create({ data: { title, text, authorId: req.session.userId, }, }); }
内容的提问来源于stack exchange,提问作者blackEagle
相关产品推荐
相关产品推荐

