简体   繁体   中英

Typescript infer rest type of generic object type

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<T extends {bar: number}>(functionalComponent: (props: T) => JSX.Element) {
  return (props: Omit<T, 'bar'>) => {
    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<T extends {foo: number}>(param: Omit<T, 'foo'>): 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?

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<T, 'bar'> & {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<T, 'bar'> & {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<T extends BaseProps>(functionalComponent: (props: 
    | T 
    | Omit<T, 'bar'> & BaseProps ) => JSX.Element) {
  return (props: Omit<T, 'bar'>) => {
    return functionalComponent({...props, bar: 3});
  }
}

Playground Link

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<T extends {foo: number}>(param: Omit<T, 'foo'>): T {
  return {...param, foo: 3} as T;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM