6

For the death of me I cannot figure this out for the past three hours and I am very frustrated so I apologize in advance if I sound a big aggressive.

All I want is to be able to define a typescript interface for my component and have it extend the default interface any component uses, to avoid having to declare every goddamn default prop such as className, onClick and the like in my own interface.

I have this component:

import React, { useEffect, useRef, FunctionComponent } from 'react'
import './style.scss'

interface ISideProps {
  wideModeEnabled: boolean
  toggleWideMode
  setFocus
}

const AppLayoutSide: FunctionComponent<ISideProps> = (props) => {
  const ref = useRef() as any
  ...
  ...
  etc.
  return <div {...props} />
}

Now there is no error in this component, at least typescript doesn't give any. But if I try to render this component in another one, for example like this:

const otherComponent = () => {
  return (
    <div className='content'>
      <Menu />
      <Main />
      <Side
        className={'whatever'}  //Typescript error happens here
        wideModeEnabled={wideMode}
        toggleWideMode={toggleWideMode}
        setFocus={setSideFocus}
      />
    </div>
  )
}

And the error says:

Type '{ className: string; wideModeEnabled: boolean; toggleWideMode: () => void; setFocus: Dispatch<SetStateAction>; }' is not assignable to type 'IntrinsicAttributes & ISideProps & { children?: ReactNode; }'.

Property 'className' does not exist on type 'IntrinsicAttributes & ISideProps & { children?: ReactNode; }'.ts(2322)

Which is obviously true, I've not defined className on ISideProps interface, but I want to extend the default react props goddamn it! And no matter what I've tried I cannot seem to get it to work. Every guide online keeps suggesting to add : FunctionalComponent<ISideProps> to the component declaration, and I did, but it doesn't solve a thing.

Help please, I am losing my mind.

3
  • Might benefit you just to use Javascript, Typescript is nice and all but static typing really doesn't provide any reduced bug benefit, you can just rename something easier. I have found with NodeJS that using Typescript can just be an unneeded pain. I have never seen someone use Typescript with React before Commented Nov 18, 2019 at 23:32
  • I could not imagine using react without typescript. JS is a pure mess without autocomplete and unexpected run time errors. Reminds me of jQuery days. TS is sometimes verbose but definetely worth the effort. Commented Nov 18, 2019 at 23:53
  • Couldn't disagree more, it makes my life a living hell and I wish it was never invented. JS on the other hand is amazing and I can understand the need for TS before the whole decorators/modules/es6. But now? It is just horrible. Opinions, right? :S Commented Nov 19, 2019 at 19:11

2 Answers 2

9

Using type (Personal preference)

After working years with react and typescript I prefer using type instead of interface for defining props:

// Can use any other html tag instead of "th"
type CustomThProps = JSX.IntrinsicElements["th"] & {
  otherProp: string;
}

const CustomTh = ({ otherProp, children, ...rest }: CustomThProps) => {
  return ( 
    <th {...rest}>{ children } </th>
}

Using JSX.IntrinsicElements unfortunately only works with type and not interface. The reason why I like this approach the most is because you can conveniently use the html tag instead of having to know that th translates to HTMLTableHeaderCellElement in JSX.

With forwardRef:

// Can use any other html tag instead of "th"
type CustomThProps = JSX.IntrinsicElements["th"] & {
  otherProp: string;
}

const CustomTh = React.forwardRef<React.ElementRef<"th">, CustomThProps>(({ otherProp, children, ...rest }, ref) => {
  return ( 
    <th ref={ref] {...rest}>{ children } </th>
    )

})

Using interface (OLD ANSWER)

If you want to use interfaces HTMLProps<T> seems like the most reliable and universal compared to HTMLAttributes<T>.

For example, when you want to extend props for a <th>-element. You can no longer use HTMLAttributes<T>, instead you have to use ThHTMLAttributes<T>:

interface IProps extends React.ThHTMLAttributes<HTMLTableHeaderCellElement>
{
...
}

With HTMLProps<T> you can always use the same signature:

interface IProps extends React.HTMLProps<HTMLTableHeaderCellElement>
{
...
}

Using interface (OLD ANSWER)

Extend your props from react's in-built interface and destructure your props like so:

interface ISideProps extends React.HTMLAttributes<HTMLDivElement>
{
...
}

const AppLayoutSide = (props: ISideProps) => { 
const { wideModeEnabled,  toggleWideMode, setFocus, ...rest } = props;

return (<div {...rest}></div>)
}
Sign up to request clarification or add additional context in comments.

4 Comments

Thank you, changed in the original post, but still doesn't solve the error though
The problem is not in the props here, its within the other component where I pass it the className. Using <div {...props}> works just fine, in terms of passing the props, problem lies in GETTING the className in the props, because it is not explicitly defined in the interface ISideProps and as such TS is refusing to compile, even though, everything in JSX has a goddamn className
Updated my post. Is this the answer you were looking for?
I struggled with this too at the very beginning. :) TypeScript is documented poorly in react.
1

All i want is to be able to define a typscript interface for my component and have it extend the default interface any component uses

In this case you can define a DefaultProps interface and then use it to extend any Component Props:

interface DefaultProps {
    onClick?: Function;
    className?:string;
}

interface ISideProps extends DefaultProps {
    wideModeEnabled: boolean;
    // ...
}

This needs to be done for custom Components which, by default, do not expect to receive any prop.

On the other hand, built-in components like <div> or <span> have already defined the props that correspond to their HTML elements. An <img> for example, will have an src prop defined.

In order to use those predefined interfaces, you can make use of them from the React module, as @oemera suggests.

5 Comments

Is it there really no way to circumevent actually defining default props. Can't I just extend something react uses internally for Functional components, or rather, any component for that matter. Is there even such a thing. If I really need to define every single prop I am baffled, just the eventHandlers are like upwards of a 100... it would also defeat the whole purpose of intellisense when I get barraged with over a hundred default things that are not specific to this particular interface.
You don't need to write your own interface, use the in-built react interface instead : interface ISideProps extends React.HTMLAttributes<HTMLDivElement>
@Dellirium. Well, you need to define them if you are going to use them. Passing onClick to a <div> doesnt need to be defined, but passing it to a Component, for example <MyDiv>, needs to be defined. Also as @oemera suggests you can use the predefined interfaces from React.
Yeh i wanted to somehow be able to extend this feature of passing onClick to a div, because to me, a person that can't stand typscript, it is absurd that I have to define it . But in accordance to oemera's suggestion, extending works fine. Thanks to both of you
@Dellirium Im glad you could solve. TypeScript can be tricky sometimes. Ill extend the answer to make it more complete.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.