简体   繁体   中英

React Typescript HOC TypeError when wrapping a Lazy component

Code:

Link to TypeScript Playground

React version:

"dependencies": {
  "react": "^18.1.0",
  "react-dom": "^18.1.0",
},
"devDependencies": {
  "@types/node": "^17.0.30",
  "@types/react": "^18.0.8",
  "@types/react-dom": "^18.0.3",
  "typescript": "^4.6.4",
}

I'm trying to wrap the lazy component in <React.Suspense> by a HOC to reduce redundant code, but I get a TS Error:

Type 'P' is not assignable to type 'IntrinsicAttributes & (T extends MemoExoticComponent<infer U> | LazyExoticComponent<infer U> ? ReactManagedAttributes<U, ComponentPropsWithRef<T>> : ReactManagedAttributes<...>)'

My HOC code is here:

function lazyLoading<P = {}, T extends ComponentType<P> = ComponentType<P>>(LazyComponent: LazyExoticComponent<T>) {
  return (props: P): JSX.Element => (
    <Suspense fallback={<div>Loading...</div>}>
      <LazyComponent {...props} />
    </Suspense>
  )
}

A simpler version without considering about props:

function lazyLoadingNoProps<T extends ComponentType<any>>(LazyComponent: LazyExoticComponent<T>) {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <LazyComponent />
    </Suspense>
  )
}

Since I don't want to use LazyExoticComponent<any> (to get rid of any ), I decided to let TS infer the type of lazy component for me.
I've had a look at React.lazy 's signature which works perfectly well, I decided to use it on my HOC

export function lazy<T extends ComponentType<any>>(
  factory: () => Promise<{default: T}>
): LazyExoticComponent<T>

And I get the following TS error for this simpler version:

Type '{}' is not assignable to type 'T extends MemoExoticComponent<infer U> | LazyExoticComponent<infer U> ? ReactManagedAttributes<U, ComponentPropsWithRef<T>> : ReactManagedAttributes<...>'

I've totally no idea what's happening here, as the Application is working as expected at runtime, but a TS error here in IDE.


Usage Example (simpler version without props):

const Admin = lazyLoading(lazy(() => import('./pages/Admin')))

const App = (): JSX.Element => (
  <Routes>
    <Route
      path="/admin"
      element={Admin}
    />
  {/* ... */}
)

HOC code is here: Link to TypeScript Playground

Here's how to achieve it in TypeScript

import { Loader } from "../components/loader";
import { Suspense } from "react";

/**
 * HOC to wrap a component in a Suspense component.
 */

export default function withSuspense<P>(Component: React.ComponentType & any) {
  return function WithSuspense(props: P) {
    return (
      <Suspense fallback={<Loader />}>
        <Component {...props} />
      </Suspense>
    );
  };
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM