【问题标题】:How to type the intersection of two parameters to be assignable to third如何键入两个参数的交集以分配给第三个
【发布时间】: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'.

PropsAB 应该组合成的所需形状。我的想法是缩小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() 的实现是类型安全的,那么可以肯定,断言是一个很好的解决方案,因为它只影响实现者(你)而不是调用者(谁可以成为任何人)。
  • 这是问题的症结所在——它是类型安全的吗? :)
  • 认为是这样吗?假设PropsAB 是没有索引签名的普通对象类型,似乎Props 的任何键都必须是AB 的键,所以交集A &amp; B 必须包含Props 的所有键,并且由于AB 的属性值必须是来自Props 的同名属性值的子类型,因此A &amp; B 应该是Props 本身的子类型。编译器无法弄清楚这一点,我并不感到惊讶。
  • 如果函数的类型签名按预期工作,如果将结果转换为 any 可能只会让编译器关闭。

标签: typescript static-typing


【解决方案1】:

这是另一种方法,它可能会让函数的用户有点困惑,因为错误出现在 props 输入上。但它确保如果交集不起作用,则无法调用该函数。

A &amp; B 作为返回类型应该始终是Props 的子类,因此这不应该成为问题(并且导致编译器不会抱怨)。

export const foo2 = <Props, A, B>(
  props: A & B extends Props ? Props : never,
  a: A,
  b: B,
): A & B => ({ ...a, ...b });

const props = { x: 1, y: 1 };
foo2(props, { x: 1, z: 1 }, { y: 1 }); // Valid
foo2(props, { x: 1 }, {}); // Error
foo2(props, { x: 1, y: 1 }, {}); // Valid
foo2(props, {}, { x: 1, y: 1 }); // Valid

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-02-17
    • 2019-09-24
    • 1970-01-01
    • 2015-01-17
    • 1970-01-01
    • 2020-07-05
    • 2021-10-19
    • 2020-09-06
    相关资源
    最近更新 更多