0

I have a TS problem where the JS-version of the code works, but I can't get the types to compile.

I've tried to simplify it for this question. Hopefully I didn't remove too much that'll have an effect on the answer.

So, simplified, I'm trying to write a component, <Set> that takes another component as a prop, and wraps that around the children of <Set>. Additionally I want all props passed to <Set> to be passed along to the component passed as a prop.

This is the code

import React, { ReactElement, ReactNode } from "react";

interface WtProps {
  children: ReactNode;
}

type WrapperType<WTProps extends WtProps> = (
  props: WTProps
) => ReactElement | null;

interface SetProps<P extends WtProps> {
  wrap: WrapperType<P>;
  children: ReactNode;
  [x: string]: unknown;
}
export function Set<WProps extends WtProps>(props: SetProps<WProps>) {
  const { wrap, children, ...rest } = props;

  return React.createElement(wrap, rest, children);
}

interface WrapperProps {
  children: ReactNode;
  foo: string;
}
const ChildWrapper: React.FC<WrapperProps> = ({ foo, children }) => {
  return (
    <div>
      <h1>ChildWrapper</h1>
      <p>{foo}</p>
      {children}
    </div>
  );
};

export default function App() {
  return (
    <Set<WrapperProps> wrap={ChildWrapper} foo="value">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some happen!</h2>
    </Set>
  );
}

Here's a codesandbox link: https://codesandbox.io/s/musing-feynman-3euuu?file=/src/App.tsx

I've tried a bunch of different solutions, but this is what I have now. And the error I'm getting is on, the rest parameter

  return React.createElement(wrap, rest, children);

It says

No overload matches this call.
  The last overload gave the following error.
    Argument of type '{ [x: string]: unknown; }' is not assignable to parameter of type 'Attributes & WProps'.
      Type '{ [x: string]: unknown; }' is not assignable to type 'WProps'.
        'WProps' could be instantiated with an arbitrary type which could be unrelated to '{ [x: string]: unknown; }'.ts(2769)
index.d.ts(292, 14): The last overload is declared here.

I've read this question+answer How to fix TS2322: "could be instantiated with a different subtype of constraint 'object'"? so I think I know why I'm getting the error, but I don't know what to do about it.

What do I need to change to make it compile?

EDIT: I can "fix" it by making the wrap prop have type any, but I don't want to do that. I want stricter types. But anyway, here it is with any https://codesandbox.io/s/pedantic-nobel-x790n

Tobbe
  • 3,282
  • 6
  • 41
  • 53

1 Answers1

0

I haven't used TS with react but have a lot of experience in React by itself.

So i think you could try this:

export function Set<WProps extends WtProps>(props: SetProps<WProps>) {
  const { wrap, children, ...rest } = props;
  return (
    <>
      <wrap {...rest} />
      {children}
    </>
  );
}

I hope this helps, let me know if this works!

  • Maybe I misunderstood, but when I try that I get `Type '{ children: ReactNode; }' is not assignable to type 'WProps'. '{ children: ReactNode; }' is assignable to the constraint of type 'WProps', but 'WProps' could be instantiated with a different subtype of constraint 'WtProps'` – Tobbe Mar 26 '21 at 17:41
  • Basically as I undestood there's a problem with your types, correct me if im wrong but honestly i don't have that much experience with ts, so here's this post, take a look and see if it makes sense to you [is not assignable to type](https://stackoverflow.com/questions/62066421/typescript-throws-is-not-assignable-to-type-intrinsicattributes-children) – Ezequiel S. Sandoval Mar 26 '21 at 18:16
  • See https://stackoverflow.com/questions/56505560/how-to-fix-ts2322-could-be-instantiated-with-a-different-subtype-of-constraint. There is something subtlety off with the typing. – wheredidthatnamecomefrom Sep 28 '22 at 02:44