Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamic Imports not working with named exports - Typescript? #22278

Closed
benhickson opened this issue Feb 17, 2021 · 9 comments
Closed

Dynamic Imports not working with named exports - Typescript? #22278

benhickson opened this issue Feb 17, 2021 · 9 comments
Labels
bug Issue was opened via the bug report template.

Comments

@benhickson
Copy link

benhickson commented Feb 17, 2021

What version of Next.js are you using?

10.0.5

What version of Node.js are you using?

12.20.1

What browser are you using?

Chrome

What operating system are you using?

macOS

How are you deploying your application?

Vercel

Describe the Bug

When using dynamic imports (https://nextjs.org/docs/advanced-features/dynamic-import), everything works fine with default exports. However, when using named exports, I get this error:

Argument of type '() => Promise<ComponentClass<never, any> | FunctionComponent<never> | { default: ComponentType<never>; } | ((props: PageProps) => JSX.Element)>' is not assignable to parameter of type 'DynamicOptions<{}> | (() => LoaderComponent<{}>) | LoaderComponent<{}>'.
  Type '() => Promise<ComponentClass<never, any> | FunctionComponent<never> | { default: ComponentType<never>; } | ((props: PageProps) => JSX.Element)>' is not assignable to type '() => LoaderComponent<{}>'.
    Type 'Promise<ComponentClass<never, any> | FunctionComponent<never> | { default: ComponentType<never>; } | ((props: PageProps) => Element)>' is not assignable to type 'LoaderComponent<{}>'.
      Type 'ComponentClass<never, any> | FunctionComponent<never> | { default: ComponentType<never>; } | ((props: PageProps) => Element)' is not assignable to type 'ComponentClass<{}, any> | FunctionComponent<{}> | { default: ComponentType<{}>; }'.
        Type 'ComponentClass<never, any>' is not assignable to type 'ComponentClass<{}, any> | FunctionComponent<{}> | { default: ComponentType<{}>; }'.
          Type 'ComponentClass<never, any>' is not assignable to type 'ComponentClass<{}, any>'.
            Types of property 'getDerivedStateFromProps' are incompatible.
              Type 'GetDerivedStateFromProps<never, any> | undefined' is not assignable to type 'GetDerivedStateFromProps<{}, any> | undefined'.
                Type 'GetDerivedStateFromProps<never, any>' is not assignable to type 'GetDerivedStateFromProps<{}, any>'.
                  Types of parameters 'nextProps' and 'nextProps' are incompatible.
                    Type 'Readonly<{}>' is not assignable to type 'never'.

Expected Behavior

I expect the component to be imported lazily, without errors.

To Reproduce

// parent
import dynamic from "next/dynamic";
const MyComponent = dynamic(() => import("./MyComponent").then(module => module.MyComponent));
// child
export function MyComponent() {
  return null;  
}
@benhickson benhickson added the bug Issue was opened via the bug report template. label Feb 17, 2021
@jvitela
Copy link

jvitela commented Feb 18, 2021

As you are using Typescript have you tried defining an interface for your component props ?
It would be very helpful if you provide a sandbox with a reproducible example.

// parent
import dynamic from "next/dynamic";
import { MyComponentProps } from './interface'

const MyComponent = dynamic<MyComponentProps>(() =>
    import("./MyComponent").then(module =>   module.MyComponent)
);
// Interface
export interface MyComponentProps {
  // ...
}
import { MyComponentProps } from './interface'

// child
export function MyComponent(props: MyComponentProps) {
  return null;  
}

@benhickson
Copy link
Author

That worked, thank you @jvitela!

@RINDAMAN2426
Copy link

@jvitela Thanks!!

@justinphilpott
Copy link
Contributor

justinphilpott commented Jul 22, 2021

Yes thanks for this. I found in the case of a component with no props, just supplying an empty interface appeased TypeScript.

@borachoidev
Copy link

if component doesn't have props, just define {} .

import dynamic from "next/dynamic";
const MyComponent = dynamic<{}>(() => import("./MyComponent").then(module => module.MyComponent));

@WinnersProx
Copy link

if component doesn't have props, just define {} .

import dynamic from "next/dynamic";
const MyComponent = dynamic<{}>(() => import("./MyComponent").then(module => module.MyComponent));

This works fine

@statusunknown418
Copy link

if component doesn't have props, just define {} .

import dynamic from "next/dynamic";
const MyComponent = dynamic<{}>(() => import("./MyComponent").then(module => module.MyComponent));

Thanks a lot man I couldn't find any solution

@schickling
Copy link
Contributor

I was facing the same problem and investigated it more thoroughly together with @timneutkens's help. Here is what I found:

image

const components = {

  // 1
  CheckboxFormControl_doesnt_work: dynamic(() =>
    import('./blocks/FormBlock/CheckboxFormControl').then((_) => _.CheckboxFormControl)
  ),

  // 2
  CheckboxFormControl_works: dynamic(async () => {
    const mod = await import('./blocks/FormBlock/CheckboxFormControl');
    return mod.CheckboxFormControl;
  }),

  // 3
  CheckboxFormControl_also_works: dynamic( () => 
    import('./blocks/FormBlock/CheckboxFormControl').then((_) => _.CheckboxFormControl, () => () => null)
  ),

It seems that using .then(_ => _.namedExport) alone isn't enough to make TS happy in this case but the error/catch path needs to be taken care of as well (see Stackoverflow).

I've ultimately landed on the following pattern:

const namedComponent = async <T, N extends keyof T>(modPromise: Promise<T>, exportName: N) => {
  const mod = await modPromise;
  return mod[exportName];
};

const components = {

  CheckboxFormControl: dynamic(() =>
    namedComponent(import('./blocks/FormBlock/CheckboxFormControl'), 'CheckboxFormControl')
  ),

}

@balazsorban44
Copy link
Member

This issue has been automatically locked due to no recent activity. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

@vercel vercel locked as resolved and limited conversation to collaborators Feb 10, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Issue was opened via the bug report template.
Projects
None yet
Development

No branches or pull requests

9 participants