这是错误还是我误解了 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) Beta
和 Alpha
:
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 A
和 B
具有可相互分配的属性类型,通过指定 A extends B
那B extends Pick<A, keyof A & keyof B>
.对于任何键 K
在 keyof 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/