如何让TypeScript为泛型ternary函数自动实现非空断言?
我写了一个泛型工具函数ternary,用来抽象三元运算符逻辑,当condition为假值时默认返回undefined,代码如下:
export const ternary = <TReturn, TDefault = undefined>( condition: unknown, returnValue: TReturn, defaultValue?: TDefault, ) => { if (!condition) return defaultValue; return returnValue; };
在使用这个函数时,比如在findAllUsers方法里传入input.cursor作为condition——明明当condition为真时input.cursor肯定是非空的,但TypeScript还是报错提示input.cursor可能为undefined。目前我只能手动加非空断言!,想知道怎么让TypeScript自动完成这个类型推导,不用手动加断言。
使用场景的报错代码:
findAllUsers = ( ctx: TRPCContext<"authenticated">, input: PaginationSchema, ) => { return ctx.db.query.users.findMany({ limit: input.limit, where: ternary(input.cursor, gt(users.id, input.cursor)), }); };
手动添加断言的代码:
findAllUsers = ( ctx: TRPCContext<"authenticated">, input: PaginationSchema, ) => { return ctx.db.query.users.findMany({ limit: input.limit, where: ternary(input.cursor, gt(users.id, input.cursor!)), }); };
要让TypeScript自动识别condition为真时的非空类型,需要给ternary函数的泛型和参数加上类型约束,把condition和returnValue的类型关联起来。最直观的方式是把returnValue改成回调函数,让TypeScript明确知道:只有当condition为真(非空)时,这个回调才会被执行,回调里的参数自然就是非空的。
修改后的ternary函数代码:
export const ternary = <TCondition, TReturn, TDefault = undefined>( condition: TCondition, returnValue: (condition: NonNullable<TCondition>) => TReturn, defaultValue?: TDefault, ) => { if (!condition) return defaultValue; return returnValue(condition as NonNullable<TCondition>); };
对应的使用方式:
findAllUsers = ( ctx: TRPCContext<"authenticated">, input: PaginationSchema, ) => { return ctx.db.query.users.findMany({ limit: input.limit, where: ternary(input.cursor, (cursor) => gt(users.id, cursor)), }); };
为什么这样可行?
通过回调的方式,我们把input.cursor的非空判断逻辑交给了ternary函数内部——TypeScript能推导出来:只有当condition为真时,回调才会被调用,所以回调里的cursor参数自动被标记为NonNullable<typeof input.cursor>,完全不需要手动加非空断言。
如果不想用回调,也可以通过条件类型来实现,但回调的方式更符合TypeScript的类型推导逻辑,代码可读性也更高。
内容的提问来源于stack exchange,提问作者Zihao Lam

