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

为何类型'Pick<T, K> & Omit<T, K>'无法赋值给类型'T'?

TypeScript泛型合并对象的类型错误解析

我编写了一个用于合并对象两个互补部分的泛型函数,却遇到了TypeScript类型错误,代码如下:

function foo<T extends object, K extends keyof T>(a: Pick<T, K>, b: Omit<T, K>): T {
    return { ...a, ...b } // TS2322: Type 'Pick<T, K> & Omit<T, K>' is not assignable to type 'T'. 'Pick<T, K> & Omit<T, K>' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'object'.
}

错误提示指出,存在某些T和K的实例,使得Pick<T, K> & Omit<T, K>不等于T。但下面的具体实例却能正常编译:

interface Test {
    a: string,
    b: string
}

const a: Pick<Test, 'a'> = { a: 'a' }
const b: Omit<Test, 'a'> = { b: 'b' }

const t: Test = { ...a, ...b }

反例演示:Pick<T, K> & Omit<T, K>≠T的场景

场景1:带有只读属性的类型

当T包含只读属性时,合并后的对象会丢失只读约束:

interface ReadonlyTest {
    readonly a: string;
    b: string;
}

const partialA: Pick<ReadonlyTest, 'a'> = { a: 'a' };
const partialB: Omit<ReadonlyTest, 'a'> = { b: 'b' };

// 此处会报错:合并后的对象中a属性是可写的,无法赋值给ReadonlyTest
const result: ReadonlyTest = { ...partialA, ...partialB };

此时Pick<ReadonlyTest, 'a'> & Omit<ReadonlyTest, 'a'>的实际类型是{ a: string; b: string },和ReadonlyTest(带有readonly约束)并不等价。

场景2:带有额外约束的子类型

如果T是一个带有额外属性约束的子类型,合并后的对象无法满足该约束:

// 要求对象不能有c属性(c的类型为never)
type ExactTest = { a: string; b: string } & { readonly c?: never };

const partialA: Pick<ExactTest, 'a'> = { a: 'a' };
const partialB: Omit<ExactTest, 'a'> = { b: 'b' };

// 合并后的对象没有c属性相关约束,无法赋值给ExactTest
const result: ExactTest = { ...partialA, ...partialB };

编辑1:条件返回类型仍报错的原因

即使TypeScript认可T extends Pick<T, K> & Omit<T, K>的关系,将返回类型改为条件类型后依然报错:

function foo<T extends object, K extends keyof T>(a: Pick<T, K>, b: Omit<T, K>): T extends Pick<T, K> & Omit<T, K> ? T : any {
    const res: Pick<T, K> & Omit<T, K> = { ...a, ...b }
    return res // TS2322: Type 'Pick<T, K> & Omit<T, K>' is not assignable to type 'T extends Pick<T, K> & Omit<T, K> ? T : any'.
}

这是因为泛型上下文里的条件类型是延迟解析的,TypeScript无法在编译期确认Pick<T, K> & Omit<T, K>能直接匹配条件类型的结果,即使逻辑上两者存在包含关系。

编辑2:类型关系的本质

从以下两个函数可以明确T和Pick<T, K> & Omit<T, K>的关系:

function foo<T, K extends keyof T>(x: Pick<T, K> & Omit<T, K>): T {
    return x // 报错:Pick<T, K> & Omit<T, K>无法赋值给T
}

function bar<T, K extends keyof T>(x: T): Pick<T, K> & Omit<T, K> {
    return x // 无报错:T可以赋值给Pick<T, K> & Omit<T, K>
}

这说明Pick<T, K> & Omit<T, K>是T的超类型:所有T的实例都属于Pick<T, K> & Omit<T, K>,但存在Pick<T, K> & Omit<T, K>的实例不属于T(比如前面的只读属性例子)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 23:50:35