tallman
tallman

Reputation: 129

What is the correct way to implement '...rest' in a TSX component?

I want to implement ...rest in TSX components. According to TypeScript workaround for rest props in React, it should work. In the following app.tsx, a compiler warning occurs with the '<Container style=...' line:

import { ReactNode } from "react";

interface IParent {
  children?: ReactNode;
}

function Container({ children, ...rest }: IParent) {
  return <div {...rest}>{children}</div>;
}

export default function App() {
  return (
    <Container style={{ border: "1px solid red" }}>
      <h1>Hello World!</h1>
    </Container>
  );
}

Error:

Type '{ children: Element; style: { border: string; }; }' is not assignable to type 'IntrinsicAttributes & IParent'.
  Property 'style' does not exist on type 'IntrinsicAttributes & IParent'.ts(2322)

Sample: https://codesandbox.io/s/exciting-shirley-c0dwz5?file=/src/App.tsx

Is there a correct way to create components with ...rest? Thanks!

Upvotes: 0

Views: 156

Answers (2)

Nicholas Tower
Nicholas Tower

Reputation: 84982

The issue is that your IParent type says that only the children prop is allowed. If you want to allow all the props that divs allow, then you should make the type on your props like this:

import { HTMLAttributes } from 'react';

interface IParent extends HTMLAttributes<HTMLDivElement>{
  children?: ReactNode;
}

In fact, since div's already allow children of type ReactNode, you could remove that, and if there are no other props you can just do:

interface IParent extends HTMLAttributes<HTMLDivElement>{}

Or:

type IParent = HTMLAttributes<HTMLDivElement>

Upvotes: 1

Nick Vu
Nick Vu

Reputation: 15520

The primary reason for using Typescript is to restrict types on your code. You still can use ...rest, but make sure you declare types on the interface properly like below

import type { ReactNode, CSSProperties } from "react";

interface IParent {
  children?: ReactNode;
  style?: CSSProperties; //declare style type
  title?: string; //fix your above error
}

function Container({ children, ...rest }: IParent) {
  return <div {...rest}>{children}</div>;
}

export default function App() {
  return (
    <Container style={{ border: "1px solid red" }} title="test">
      <h1>Hello World!</h1>
    </Container>
  );
}

Sandbox

Upvotes: 1

Related Questions