【发布时间】:2019-03-07 16:43:42
【问题描述】:
我试图在 TypeScript 中表达一个现有的 API,它有一个函数可以将两个参数组合成与第三个参数相同的形状。到目前为止,我的输入尝试如下所示(简化):
type Difference<A, B> = Pick<A, Exclude<keyof A, keyof B>>
export const foo = <Props, A extends Difference<Props, B>, B extends Difference<Props, A>>(
props: Props,
a: A,
b: B,
): Props => ({ ...a, ...b }); // Type 'A & B' is not assignable to type 'Props'.
Props 是 A 和 B 应该组合成的所需形状。我的想法是缩小A 的范围以扩展其与B 的差异,反之亦然,但它会产生评论中看到的错误,尽管它似乎表现得如预期:
const props = { x: 1, y: 1 };
foo(props, { x: 1, z: 1 }, { y: 1 }); // Valid
foo(props, { x: 1 }, {}); // Error
foo(props, { x: 1, y: 1 }, {}); // Valid
我应该在返回值中添加一个as unknown as Props 断言吗?
【问题讨论】:
-
如果你有理由相信
foo()的实现是类型安全的,那么可以肯定,断言是一个很好的解决方案,因为它只影响实现者(你)而不是调用者(谁可以成为任何人)。 -
这是问题的症结所在——它是类型安全的吗? :)
-
我认为是这样吗?假设
Props、A和B是没有索引签名的普通对象类型,似乎Props的任何键都必须是A或B的键,所以交集A & B必须包含Props的所有键,并且由于A和B的属性值必须是来自Props的同名属性值的子类型,因此A & B应该是Props本身的子类型。编译器无法弄清楚这一点,我并不感到惊讶。 -
如果函数的类型签名按预期工作,如果将结果转换为
any可能只会让编译器关闭。