Express中Prisma结合条件返回类型的TypeScript错误排查
问题描述
我在Express应用中定义了如下条件类型:
type SerializedFunction<Serialized, Unserialized> = <B extends boolean>(serialize: B) => Promise<B extends true ? (Serialized | null) : (Unserialized | null) >;
将其绑定到操作Prisma的函数,代码如下:
import type { examples } from "@prisma/client"; interface DataSerialized{ foo: string; bar: number; } const data: SerializedFunction<DataSerialized, examples> = async serialize => { // 返回未序列化的数据 if(!serialize) return await prisma.examples.findFirst({ // ... }); const example = await prisma.examples.findFirst({ // ... select: { foo: true, bar: true, } }); if(!example) return null; return { ...example, bar: await doSomethingWithBar(bar) }; }
这个SerializedFunction的作用是根据参数serialize的布尔值,让函数返回DataSerialized或examples类型(无结果时返回null)。但实现时出现TypeScript错误:
Type examples | { // 第一个"findFirst"调用返回的对象... } is not assignable to type 'B extends true ? DataSerialized : examples'. Type 'examples' is not assignable to type 'B extends true ? DataSerialized : examples'
请问我哪里出错了?
错误原因
TypeScript 无法在函数实现内部将 if 分支逻辑与泛型条件类型的分支做精确关联。你的函数返回类型依赖泛型参数 B,但编译器没法判断:当 serialize 为 false 时返回的 examples 正好对应 B extends false 的分支,当 serialize 为 true 时返回的 DataSerialized 对应 B extends true 的分支。它只会把所有可能的返回值合并成联合类型,而这个联合类型无法匹配条件类型的约束。
另外你原代码里还有个小问题:doSomethingWithBar(bar) 应该是 doSomethingWithBar(example.bar),不然会找不到 bar 变量。
解决方案
方案一:用类型断言明确分支类型
在每个返回分支处,用类型断言告诉编译器当前返回值符合对应分支的类型要求:
const data: SerializedFunction<DataSerialized, examples> = async serialize => { if(!serialize) { const result = await prisma.examples.findFirst({ // ... }); return result as Promise<examples | null>; } const example = await prisma.examples.findFirst({ // ... select: { foo: true, bar: true, } }); if(!example) return null as Promise<DataSerialized | null>; return { ...example, bar: await doSomethingWithBar(example.bar) } as Promise<DataSerialized | null>; }
方案二:改用函数重载(更推荐)
相比泛型条件类型,函数重载能让编译器更清晰地匹配参数和返回类型的对应关系,代码可读性也更好:
import type { examples } from "@prisma/client"; interface DataSerialized{ foo: string; bar: number; } // 定义重载签名 async function data(serialize: true): Promise<DataSerialized | null>; async function data(serialize: false): Promise<examples | null>; // 实现签名 async function data(serialize: boolean) { if(!serialize) return await prisma.examples.findFirst({ // ... }); const example = await prisma.examples.findFirst({ // ... select: { foo: true, bar: true, } }); if(!example) return null; return { ...example, bar: await doSomethingWithBar(example.bar) }; }
内容的提问来源于stack exchange,提问作者Grover
相关产品推荐
相关产品推荐

