34

Given a component that takes custom props as well as html attribute props, how should the interface for such a component be created? Ideally, the interface would also handle react-specific html props such as using className instead of class.

This is the usage example for which I am trying to find the right interface:

<MyComponent customProp='value' style={{textAlign: 'center'}}  />
aryzing
  • 4,982
  • 7
  • 39
  • 42

2 Answers2

54
interface IMyComponentProps extends React.HTMLAttributes<HTMLElement> {
  customProp: string;
}

UPD: @ddek mentioned intersections &.

I would like to warn you about the following issue with that approach.


interface A {
  onClick: () => void;
}

interface B {
  onClick: (event: React.MouseEvent<HTMLElement>) => void;
}

// Typescript does not complain. This is not good
type AB = A & B;
const a: AB = {
  onClick: () => {}
};


// TS2320: Interface 'AB2' cannot simultaneously extend types 'A' and 'B'.
// Named property 'onClick' of types 'A' and 'B' are not identical.

interface AB2 extends A, B {

}

// TS2430: Interface 'AC' incorrectly extends interface 'A'.
//   Types of property 'onClick' are incompatible.  
//   Type '(event: MouseEvent<HTMLElement, MouseEvent>) => void' is not
// assignable to type '() => void'.
interface AC extends A {
  onClick: (event: React.MouseEvent<HTMLElement>) => void;
}

Yozi
  • 11,435
  • 1
  • 22
  • 25
6

Yozi is right but there is another way, which demonstrates a typescript (and general FP) feature which you may not be familiar with if you're coming from something like C# or Java.

interface MyCustomProps {
    customProp: string;
}

const MyComponent = (props: MyCustomProps & React.HTMLAttributes<...>) 
    => (...)

In typescript, an & in a type declaration refers to an intersection type. You can read more in the typescript docs. The props object now combines the properties of MyCustomProps and the HTML attributes. (It's also worth learning about discriminated unions, or or types, which are notated with |. I find these more useful than intersections).

If you want to clean up your method signature, you could declare the type as follows:

interface MyCustomProps {...}
type ComponentProps = MyCustomProps & React.HTMLAtributes<...>;

However, this notation has now lost the conciseness of both the previous approaches - the extends syntax, and the & notation.

ddek
  • 111
  • 1
  • 4
  • 1
    I would recommend use `extend` instead of `&` – Yozi Mar 18 '20 at 07:36
  • Why do you recommend that approach @yozi ? – sunpietro Aug 20 '20 at 12:18
  • 1
    @sunpietro it has different semantic. One does _extend_ and another _intersection_. See my answer to this question. At the time I wrote it Typescript was not accurate enough with `&` I heard Typescript team had plans to fix it but what's the reason to rely on the "fix" if you simply can use `extend` – Yozi Aug 21 '20 at 14:21