Typescript 推断泛型对象类型的剩余类型

huangapple go评论63阅读模式
英文:

Typescript infer rest type of generic object type

问题

我正在为类似于React的函数组件编写一个部分函数。它接受一个名为 props 的参数,然后填充props的特定键。例如,部分函数 foo 将属性 bar 的值填充到新组件中。

然而,TypeScript 编译器报错:

'Omit<T, "bar"> & { bar: number; }' 可分配给类型 'T' 的约束,但 'T' 可能会实例化为具有不同约束的不同子类型 '{ bar: number; }'

问题与这个问题非常相似,它使用infer来处理参数列表。但是,我没有找到对象版本,而是找到了这个GitHub问题,其中建议使用Omit。然而,Omit 对于泛型不起作用。

一个小例子来说明问题:

function func<T extends {foo: number}>(param: Omit<T, 'foo'>): T {
  return {...param, foo: 3};
}

'Omit<T, "foo"> & { foo: number; }' 可分配给类型 'T' 的约束,但 'T' 可能会实例化为具有不同约束的不同子类型 '{ foo: number; }'

我还检查了一个解释这个错误的答案,但仍然无法理解。

如何使部分函数的类型签名正常工作?

英文:

I am writing a partial function for react-alike functional components. It accepts a single parameter props, then fill a specific key of the props. For example, the partial function foo fills value of prop bar to the new component.

function foo&lt;T extends {bar: number}&gt;(functionalComponent: (props: T) =&gt; JSX.Element) {
  return (props: Omit&lt;T, &#39;bar&#39;&gt;) =&gt; {
    return functionalComponent({...props, bar: 3});
  }
}

However, typescript compiler complains:
> 'Omit<T, "bar"> & { bar: number; }' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{ bar: number; }'

The problem is very similar to this one, which uses infer for a parameter list. However, I didn't find an object version, instead I found this github issue, which suggests using Omit. However, Omit does not work for the generic.

A tiny example to illustrate the problem:

function func&lt;T extends {foo: number}&gt;(param: Omit&lt;T, &#39;foo&#39;&gt;): T {
  return {...param, foo: 3};
}

> 'Omit<T, "foo"> & { foo: number; }' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{ foo: number; }'

I also checked an answer that explains this error, but still can't get the point.

How can I make the partial function type signature work?

答案1

得分: 1

Typescript 无法确定在 Omit 中还有未解析的泛型类型参数时会发生什么。要使此类型检查通过,我们可以在 functionalComponentprops 中添加一个与 Omit<T, 'bar'> & {bar: number} 的联合类型。虽然这似乎与 T 相同,但它的包含将允许 TypeScript 将包装组件的属性分配给功能组件的属性。当 T 解析时,TOmit<T, 'bar'> & {bar: number} 将解析为相同的有效类型,因此它不应对调用站点产生影响:

type BaseProps = { bar: number }

function foo<T extends BaseProps>(
  functionalComponent: (props: T | Omit<T, 'bar'> & BaseProps) => JSX.Element
) {
  return (props: Omit<T, 'bar'>) => {
    return functionalComponent({ ...props, bar: 3 });
  }
}

Playground Link

英文:

Typescript can't follow what will happen with the Omit as long as it still has an unresolved generic type parameter in it. What we could do to get this to type check is to add a union with Omit&lt;T, &#39;bar&#39;&gt; &amp; {bar: number} to props of functionalComponent. While this seems the same as T, it's inclusion will let typescript assign the wrapped component props to the functional component props. When T is resolved, T and Omit&lt;T, &#39;bar&#39;&gt; &amp; {bar: number} will resolve to the same effective type, so it should have no impact to the call site:


type BaseProps ={bar: number}
function foo&lt;T extends BaseProps&gt;(functionalComponent: (props: 
    | T 
    | Omit&lt;T, &#39;bar&#39;&gt; &amp; BaseProps ) =&gt; JSX.Element) {
  return (props: Omit&lt;T, &#39;bar&#39;&gt;) =&gt; {
    return functionalComponent({...props, bar: 3});
  }
}

Playground Link

答案2

得分: 0

我找到了一个暴力解决方法:使用as关键字。不确定是否存在更好的解决方案,所以我将它保留为未解决的。

function func<T extends {foo: number}>(param: Omit<T, 'foo'>): T {
  return {...param, foo: 3} as T;
}
英文:

I found a brute-force workaround: using the as keyword. Not sure if there exists a better solution, so I'll leave it unresolved.

function func&lt;T extends {foo: number}&gt;(param: Omit&lt;T, &#39;foo&#39;&gt;): T {
  return {...param, foo: 3} as T;
}

huangapple
  • 本文由 发表于 2023年1月6日 14:13:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/75027562.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定