24
votes

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'}}  />
2

2 Answers

40
votes
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;
}

5
votes

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.