typescript - A延伸B;类型 'Pick<A, Exclude<keyof A, keyof B>> & B' 不可分配给类型 'A' .ts(2322)

标签 typescript

这是错误还是我误解了 typescript 的东西?

示例代码如下:

type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;

const func = <A extends B, B>() => {
  const aWithoutB: Omit<A, keyof B> = {} as any; // The {} assignment isn't important here.

  const b: B = {} as any;

  const a: A = {
    ...aWithoutB,
    ...b
  }; // warning here
};

TypeScript 3.4.5 的 IDE(VS 代码)警告:

Type 'Pick<A, Exclude<keyof A, keyof B>> & B' is not assignable to type 'A'.ts(2322)

我认为这两种类型是相等的,但不是。这是什么原因?

最佳答案

主要原因是编译器当前doesn't perform理解未解析的通用对象类型与该类型的互补属性集的交集之间的等价性所必需的高阶类型分析类型。从相关问题的状态来看,尚不清楚是否会实现此类分析;让编译器考虑这些事情无疑是可能的,但谁知道是否可以在不降低编译器性能的情况下做到这一点?


第二个原因是这两种类型实际上是等价的。考虑以下接口(interface) BetaAlpha :

interface Beta {
    x: string | number,
    y: boolean,
    z?: object  
}

interface Alpha extends Beta {
    x: string,
    y: true,
    z: undefined
}

func<Alpha, Beta>(); // no error, but uh oh

declare const beta: Beta;
declare const alpha: Alpha;
const notAlpha: Alpha = { ...alpha, ...beta }; // error!

请注意 Alpha extends Beta , 但如果你传播 Alpha其次是 Beta进入一个新对象,你不会得到一个Alpha .所以你可以调用func<Alpha, Beta>()但您可能不应该这样做。

您无疑专注于通过向其添加更多属性来扩展类型,但没有注意通过缩小现有属性的类型来扩展类型。


可以收紧对 A 的约束和 B使两者共享的任何 key AB具有可相互分配的属性类型,通过指定 A extends BB extends Pick<A, keyof A & keyof B> .对于任何键 Kkeyof A & keyof B , A extends B暗示 A[K] extends B[K] , 和 B extends Pick<A, keyof A & keyof B>暗示 B[K] extends A[K] .

在实现过程中,一旦您完全相信自己卓越的人类推理能力,Omit<A, keyof B> & B真的和A一样(我希望),但编译器不相信,你可以通过使用 type assertion 来控制编译器。 .

让我们看看它的实际效果:

// more strict constraint
const func = <A extends B, B extends Pick<A, keyof A & keyof B>>() => {
    const aWithoutB: Omit<A, keyof B> = {} as any;
    const b: B = {} as any;    
    const a = {
        ...aWithoutB,
        ...b
    } as A; // I'm smarter than the compiler 🤓
};

func<Alpha, Beta>(); // error now, as desired
func<Beta & { extraProp: string }, Beta>(); // okay

好的,希望对你有帮助。祝你好运!

关于typescript - A延伸B;类型 'Pick<A, Exclude<keyof A, keyof B>> & B' 不可分配给类型 'A' .ts(2322),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56177506/

相关文章:

typescript - ES6/ typescript 导入 : import * and names on a single line

typescript - 将通用 typescript 类型限制为单个字符串文字值,不允许联合

typescript - 从 onCreate 的 Firestore snap.data 检索数据但出现未定义的错误

javascript - 当互联网关闭时启用离线功能

typescript - 如何使函数处理 (A | B)[] 和 A[] | 的返回值B[] 在 typescript 中等效

javascript - 如何正确地将具有多个元素的新对象推送到数组中?

npm - Windows 上 npm typescript@1.4.1 的编译问题

javascript - 找不到别名。也许你忘了加入它

typescript - 如何在同一命名空间中的多个文件上合并 typescript 接口(interface)

typescript - Jest 错误: "preset @vue/cli-plugin-unit-jest/presets/typescript-and-babel not found"