You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 11:56:18