Home > Mobile >  How can I destructure a mixture of React props and my custom props?
How can I destructure a mixture of React props and my custom props?

Time:06-23

I want to create a React component that wraps the Route component to create guarded routes. I need to get the element property that the user would normally specify on the <Route> element, plus the properties, feature, role, and fallback which were defined by me, and then every other property should pass through from my wrapper component to the Route component.

The problem is making these props strongly typed in TypeScript. I've tried a few different approaches, but I get various errors. For example, if I try the example below, I get the error on RouteProps of "An interface can only extend an object type or intersection of object types with statically known members." How can I accomplish this?

import React from 'react'
import {
  IndexRouteProps,
  LayoutRouteProps,
  PathRouteProps,
  Route,
  useNavigate,
} from 'react-router-dom'

type RouteProps = PathRouteProps | LayoutRouteProps | IndexRouteProps

interface ReactProps extends RouteProps {
  element: React.ReactNode
  feature?: string
  role?: string
  fallback?: React.ReactNode | string
}

const ProtectedRoute = ({
  element,
  feature,
  role,
  fallback,
  ...restOfProps
}: ReactProps) => {
  // TODO: check if user is in specified role
  const isInRole = !role // || someRoleService.isInRole(role)
  const featureEnabled =
    !feature || featureManagement.isFeatureFlagEnabled(feature)

  const redirectUrl = fallback as string

  if (!isInRole || !featureEnabled) {
    if (redirectUrl) {
      const navigate = useNavigate()

      navigate(redirectUrl)
      return null
    }
    return (
      // eslint-disable-next-line react/jsx-props-no-spreading
      <Route {...restOfProps} element={fallback} />
    )
  }

  return (
    // eslint-disable-next-line react/jsx-props-no-spreading
    <Route {...restOfProps} element={element} />
  )
}

ProtectedRoute.defaultProps = {
  feature: null,
  role: null,
  fallback: null,
}

export default ProtectedRoute

CodePudding user response:

And interface cannot extend a union. That's what the error message means.

For example:

type A = { foo: 1 } | { bar: 2 }
interface B extends A { baz: 3 } // error

However, you can use a type alias with an intersection &.

type A = { foo: 1 } | { bar: 2 }
type C = A & { baz: 3 } // fine

Which means this should work as you expect:

type ReactProps = RouteProps & {
  element: React.ReactNode
  feature?: string
  role?: string
  fallback?: React.ReactNode | string
}

See playground

  • Related