typescript 为什么类型'Pick〈T,K>& Omit〈T,K>'不能赋给类型'T'?[duplicate]

1hdlvixo  于 2023-02-17  发布在  TypeScript
关注(0)|答案(1)|浏览(166)
    • 此问题在此处已有答案**:

Pick<Foo, Bar> & Omit<Foo, Bar> !== Foo in Typescript?(1个答案)
12小时前关门了。
我发现了一个令人费解的错误,假设我想有一个相当无用的函数,它合并了一个对象的两个互补部分:

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'.
}

我得到了上面的错误,而下面的代码显然工作:

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 }

这个错误试图告诉我,我可以得到一个TK,其中Pick<T, K> & Omit<T, K>不是T。有人能给我一个这样的例子吗(或者对上面的错误的任何其他解释)?
编辑1:
更奇怪的是,TS本身认为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'.
}

编辑二:
显然,Pick<T, K> & Omit<T, K>T的子集,因此一定存在T中存在但Pick<T, K> & Omit<T, K>中不存在的情况:

function foo<T, K extends keyof T>(x: Pick<T, K> & Omit<T, K>): T {
    return x // TS2322: Type 'Pick<T, K> & Omit<T, K>' is not assignable to type 'T'. 'T' could be instantiated with an arbitrary type which could be unrelated to 'Pick<T, K> & Omit<T, K>'.
}

function bar<T, K extends keyof T>(x: T): Pick<T, K> & Omit<T, K> {
    return x // no issue
}
tv6aics1

tv6aics11#

这里确实存在不合理的可能性,PickOmit都实现为mapped types,其结果可能不包含源类型的所有特征。
想想这个:

interface Test {
    (arg: string): string
    a: string,
    b: string
}

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

const t: Test = { ...a, ...b }
//    ~ Type '{ b: string; a: string; }' provides no match 
//      for the signature '(arg: string): string'

我已经给Test一个调用签名,Pick<Test, 'a'>Omit<Test, 'a'>都不会产生带有调用签名的类型,从而导致Pick<Test, 'a'> & Omit<Test, 'a'>无法赋值给Test
还有一个问题:当TK是泛型类型时,编译器无法正确分析Pick<T, K>等操作的结果。类型TK的作用更像占位符。在调用函数之前,无法知道它们的具体类型。因此编译器对Pick<T, K>Omit<T, K>相交的含义只有有限的理解。您可以在#28884中看到关于类似问题的讨论。
Playground

相关问题