Home > Back-end >  Typescript: Its return type 'unknown' is not a valid JSX element
Typescript: Its return type 'unknown' is not a valid JSX element

Time:10-25

I am creating a simple app which renders a list of courses and some details about the courses.

The course details are split into the Header, Content and Total.

In the Content section, is a child component: Part, however Typescript complains that the JSX element is "unknown" and I have no idea why it is.

Can anyone make sense of this?

App.js

    import Header from './components/Header'
import Content from './components/Content'
import Total from './components/Total'
import { CoursePart } from './types'

const App = () => {
  const courseName = "Half Stack application development";
  const courseParts: CoursePart[] = [
    {
      name: "Fundamentals",
      exerciseCount: 10,
      description: "This is the easy course part",
      type: "normal"
    },
    {
      name: "Advanced",
      exerciseCount: 7,
      description: "This is the hard course part",
      type: "normal"
    },
    {
      name: "Using props to pass data",
      exerciseCount: 7,
      groupProjectCount: 3,
      type: "groupProject"
    },
    {
      name: "Deeper type usage",
      exerciseCount: 14,
      exerciseSubmissionLink: "https://fake-exercise-submit.made-up-url.dev",
      type: "submission"
    }
  ]
  return (
    <div>
      <Header courseName={courseName} />
      <Content courseParts={courseParts} />
      <Total courseParts={courseParts} />
    </div>
  );
};

export default App;

Content component:

import { interfacePart } from '../types'
import Part from './Part'

const Content = ({ courseParts }: {courseParts: interfacePart }): unknown => {

  return (
 
    <div>
      <Part courseParts={courseParts} />
    </div>

  )
}

export default Content

Part component:

import { interfacePart } from '../types'

// eslint-disable-next-line react/prop-types
const Part = ({ courseParts }: {courseParts: interfacePart })  => {

  return (
  courseParts.forEach(part => {
  switch (part.name) {
    case "Fundamentals":
      return (
        <div>
          {part.name}
        </div>
      )
    
      case "Advanced":
      return (
        <div>
          {part.name}
        </div>
      )
       case "Using props to pass data":
        return (
          <div>
            {part.name}
          </div>
        )
  
           case "Deeper type usage":
            return (
              <div>
                {part.name}
              </div>
            )
            default:
              break
    
  }
})
  )
}

export default Part

types.tsx

export interface Title {
    courseName: string
}

export interface CoursePartBase {
  name: string;
  exerciseCount: number;
  type: string;
}

export interface CoursePartBaseDescription extends CoursePartBase {
  description?: string;
}

export interface CoursePartOne extends CoursePartBaseDescription {
  name: "Fundamentals";
}

export interface CoursePartTwo extends CoursePartBase {
  name: "Using props to pass data";
  groupProjectCount: number;
}

export interface CoursePartThree extends CoursePartBaseDescription {
  name: "Deeper type usage";
  description?: string;
  exerciseSubmissionLink: string;
}

export interface CoursePartFour extends CoursePartBaseDescription {
  name: "Advanced";

}

export interface CourseSpecialPart extends CoursePartBaseDescription {
  name: "Backend development";
  requirements: Array<string>;
}

export type CoursePart = CoursePartOne | CoursePartTwo | CoursePartThree | CoursePartFour | CourseSpecialPart;

export interface interfacePart {
  reduce(arg0: (carry: any, part: any) => any, arg1: number): import("react").ReactNode;
  forEach(arg0: (part: any) => JSX.Element | undefined): unknown;
  part?: CoursePart;
}

CodePudding user response:

Try to wrap the content of Part component with Fragment, like this:

import { interfacePart } from '../types'

// eslint-disable-next-line react/prop-types
const Part = ({ courseParts }: {courseParts: interfacePart })  => {

  return (
  <>
  courseParts.forEach(part => {
  switch (part.name) {
    case "Fundamentals":
      return (
        <div>
          {part.name}
        </div>
      )
    
      case "Advanced":
      return (
        <div>
          {part.name}
        </div>
      )
       case "Using props to pass data":
        return (
          <div>
            {part.name}
          </div>
        )
  
           case "Deeper type usage":
            return (
              <div>
                {part.name}
              </div>
            )
            default:
              break
    
  }
})
  )
</>
}

export default Part

The reason for using Fragment here is

A common pattern in React is for a component to return multiple elements. Fragments let you group a list of children without adding extra nodes to the DOM.

https://reactjs.org/docs/fragments.html

CodePudding user response:

The Content component is being typed to type of unknown. Type unknown is a type safe counter part of type any. However unknown type is much less permissive than any type.

To fix the error, change the type of your content component to the following:

const Content: React.FC<{courseParts: interfacePart }> = ({ courseParts }) => {
    return (
      // Your JSX code
    )
}

Secondly, edit your Parts component to return a React fragment when case is default and wrap your forEach with a React fragment so the block of code is interpreted as a JSX:

import React from 'react';
import { interfacePart } from '../types'

 const Part: React.FC<{courseParts: interfacePart }> = ({ courseParts })  => {

  return (
   <>
     {
      courseParts.forEach(part => {
       switch (part.name) {
         case "Fundamentals":
           return (
              <div>
                {part.name}
              </div>
            )
          case "Advanced":
           return (
              <div>
                {part.name}
              </div>
            )
          case "Using props to pass data":
           return (
               <div>
                 {part.name}
               </div>
           )
           case "Deeper type usage":
            return (
               <div>
                 {part.name}
               </div>
            )
            default:
              return (<></>)
         }
       })
     }
   </>
  )
}

export default Part

Lastly, update your interfacePart to return a JSX Element instead of unknown for the forEach loop.

export interface interfacePart {
    reduce(arg0: (carry: any, part: any) => any, arg1: number): import("react").ReactNode;
    forEach(arg0: (part: any) => JSX.Element | undefined): JSX.Element;
    part?: CoursePart;
}
  • Related