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
}