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

TypeScript条件类型函数实现困惑及联合类型疑问求助

问题解决与解析

一、函数实现修复

你的deserializeObj函数报错核心有两个原因:

  1. 类型推导逻辑错误:Omit<Obj, 'id'>的结果并非预期的Omit<ObjA, 'id'> | Omit<ObjB, 'id'>,而是{ common: string }——因为keyof (ObjA | ObjB)仅返回联合类型所有分支的公共键(id和common),去掉id后只剩common属性。
  2. 条件类型无法自动收窄:即便修正类型定义,TS也无法自动将函数返回值与DeserializedObj<T>的具体分支匹配,需通过函数重载或类型断言明确类型关系。

修复后代码(推荐函数重载)

import { ObjectID } from "mongodb"; // 假设使用MongoDB的ObjectID

type ObjCommon = {
  id: ObjectID;
  common: string;
};

type ObjA = ObjCommon & {
  A: number;
};

type ObjB = ObjCommon & {
  B: boolean;
};

// 明确序列化分支类型,避开Omit联合类型的坑
type SerializedObjA = Omit<ObjA, 'id'> & { id: string };
type SerializedObjB = Omit<ObjB, 'id'> & { id: string };
type SerializedObj = SerializedObjA | SerializedObjB;

// 基于明确分支的条件类型
type DeserializedObj<T> = 
  T extends SerializedObjA ? ObjA :
  T extends SerializedObjB ? ObjB :
  never;

// 函数重载实现自动类型推导
function deserializeObj(serializedObj: SerializedObjA): ObjA;
function deserializeObj(serializedObj: SerializedObjB): ObjB;
function deserializeObj(serializedObj: SerializedObj): Obj {
  const { id, ...objData } = serializedObj;
  return {
    ...objData,
    id: new ObjectID(id),
  } as Obj;
}

// 使用示例:自动推导返回类型
const objA = deserializeObj({ id: "123", common: "test", A: 123 }); // 类型为ObjA
const objB = deserializeObj({ id: "456", common: "test", B: true }); // 类型为ObjB

若不想用重载,也可通过类型断言修正原函数:

function deserializeObj<T extends SerializedObj>(serializedObj: T): DeserializedObj<T> {
  const { id, ...objData } = serializedObj;
  return {
    ...objData,
    id: new ObjectID(id),
  } as DeserializedObj<T>;
}

二、联合类型示例解析

Ex1:同时含A、B属性的对象属于Obj,但无法赋值给ObjA

  • 符合Obj类型的原因:TS联合类型规则是「满足任意一个分支即可」。你的t对象同时拥有ObjA和ObjB的所有属性,自然满足ObjA | ObjB的要求。
  • 无法赋值给ObjA的原因:变量t的类型被推断为Obj(联合类型),而非具体的ObjA。联合类型赋值给单个分支时,TS需要明确类型归属,可通过类型缩小解决:
    if ("A" in t) {
      const u: ObjA = t; // 此时TS确定t是ObjA,无报错
    }
    

Ex2:缺失A、B属性的对象符合SerializedObj类型

核心原因是keyof (ObjA | ObjB)的特性:联合类型的keyof仅返回所有分支的公共键(即id和common)。因此:

type OmitObjId = Omit<Obj, 'id'>; // 等价于 { common: string }
type SerializedObj = OmitObjId & SerializedId; // 等价于 { common: string; id: string }

仅含common和id的对象完全符合SerializedObj的类型定义。若要强制SerializedObj必须包含A或B,需像修复后的代码那样,明确定义SerializedObjA和SerializedObjB的联合类型,而非依赖Omit<Obj, 'id'>。


内容的提问来源于stack exchange,提问作者Isaac Torres

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 19:32:30