1
votes

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.

2
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 - Brian Ogden
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. - oemera
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 - Dellirium

2 Answers

1
votes

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>)
}

UPDATE

It seems like HTMLProps<T> is more reliable and universal than 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>
{
...
}
1
votes

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.