1

So I'm trying to nest a TypeScript React component within another, but it complains about types. It would seem it wants me to add all of my props into the parent interface?

Is there a way of doing this without having to have all my types listed in the child component interface, but then also having to add the types to the parent component interface?

Note: I am using Styled Components in the example below

interface IField {
  children: React.ReactNode
}

export function Field({ children, htmlFor, label, required, ...props }: IField) {
  return (
    <FormField {...props}>
      <Label htmlFor={htmlFor} label={label} required={required}/>
      {children}
    </FormField>
  )
}

interface ILabel {
  htmlFor: string
  label: string
  required?: boolean
}

export function Label({ htmlFor, label, required }: ILabel) {
  return (
    <FormLabel htmlFor={htmlFor}>
      {label}
      {required && (
        <Required />
      )}
    </FormLabel>
  )
}

Error I get:

Type '{}' is missing the following properties from type 'ILabel': htmlFor, label

Thanks for any help in advance!

2
  • Is it complaining on the Field's return? It looks like you're not passing in a required parameter htmlFor and you're also not passing in required correctly: props.required vs required -- as you're not destructuring that key directly. Commented Feb 14, 2020 at 16:42
  • Oops, I just fixed the required prop. It's complaining on the Label component inside of the FormField. Commented Feb 14, 2020 at 18:33

1 Answer 1

1

To avoid adding the properties from the child interface back to the parent component interface you can use extends (see TypeScript inheritance documentation).

After that, you can pass the props from the parent to the child component by using {...props} deconstructor on the child component.

And finally, if you are using TypeScript might as well use React.FunctionComponent typing to avoid having to manually type children.

You can check at this simplified working example:
https://stackblitz.com/edit/react-stackoverflow-60228406

I tried to adapt your snippet below...

import React from 'react';

interface IField extends ILabel {
}

export const Field: React.FunctionComponent<IField> = (props) => {
  return (
    <FormField>
      <Label {...props} />
      {props.children}
    </FormField>
  )
};

interface ILabel {
  htmlFor: string
  label: string
  required?: boolean
}

export const Label: React.FunctionComponent<ILabel> = (props) => {
  return (
    <FormLabel htmlFor={props.htmlFor}>
      {props.label}
      {props.required && (<Required />)}
    </FormLabel>
  )
};
Sign up to request clarification or add additional context in comments.

5 Comments

This makes things a lot more clear now, a very concise answer indeed! :) I do have a question however, any particular reason you just use props instead of destructuring them like I had already done in my above example?
Glad it helped you, don't forget to accept the answer ;) Regarding the deconstruction of props it's really a matter of preference I guess, I chose to use props.myProperty because I find it easier to maintain so that if I add any new prop I don't have to add it to the deconstructed props but that's me, it will end up working both ways.
I thought it could be a matter of preference but just wanted to check :) One last question, if I wanted to extend multiple interfaces, is it as simple as just comma separating them? Example: interface IField extends ILabel, ISomeInterface {}
Yup you got it!
Awesome stuff! I've made a follow up question here if you'd like to take a crack at it, I really enjoy your explanation of things - stackoverflow.com/questions/60241936/…

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.