Malik Alimoekhamedov
Malik Alimoekhamedov

Reputation: 438

Can't import named exports from an external NPM library with TypeScript

I'm unable to import named exports from an external component library I've created as it is described in the official documentation. The situation seems to be very niche as I struggle to find anything online that would bring me closer to the solution. Did anyone manage to get it to work?

Here comes the context...

In my NPM library

Component

Modal component (fragment)

const Modal: React.FC<ModalProps> = ({ onCancelCallback, children }) => {...};
export default Modal;

As you can see, I export it as a default from Modal.tsx but later re-export it as a named export in every folder's index.ts file like so:

export { default as Modal } from './Modal';

then

export { Modal } from './Modal';

and, finally:

export * from './components';

Interface

ModalProps

export interface ModalProps {
  onCancelCallback?: () => void;
}

In Next.js

Here's how I'm importing my external "Modal" component in one of the Next.js components (not pages):

nextjscomponent.tsx

import dynamic from 'next/dynamic';
const Modal = dynamic(() => import('my-ui-kit').then((mod) => mod.Modal), {
  ssr: false,
});

TypeScript error

Argument of type '() => Promise<React.ComponentClass<never, any> | React.FunctionComponent<never> | { default: React.ComponentType<never>; } | React.FC<ModalProps>>' is not assignable to parameter of type 'DynamicOptions<{}> | (() => LoaderComponent<{}>) | LoaderComponent<{}>'.
  Type '() => Promise<React.ComponentClass<never, any> | React.FunctionComponent<never> | { default: React.ComponentType<never>; } | React.FC<ModalProps>>' is not assignable to type '() => LoaderComponent<{}>'.
    Type 'Promise<ComponentClass<never, any> | FunctionComponent<never> | { default: ComponentType<never>; } | FC<ModalProps>>' is not assignable to type 'LoaderComponent<{}>'.
      Type 'ComponentClass<never, any> | FunctionComponent<never> | { default: ComponentType<never>; } | FC<ModalProps>' 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'.ts(2345)

Notes

Any help is highly appreciated. Thank you in advance.


Update Apr. 4, 2020

On the official GitHub page of Next.js I was told that the issue might have been related to the fact that two @types/react libraries were co-living in the same Next.js project.

Here's what I tried (to no avail) to test this hypothesis:


I rand a quick "yarn why @types/react" and saw the following:

yarn why v1.22.4
[1/4] šŸ¤”  Why do we have the module "@types/react"...?
[2/4] šŸšš  Initialising dependency graph...
[3/4] šŸ”  Finding dependency...
[4/4] šŸš”  Calculating file sizes...
=> Found "@types/[email protected]"
info Has been hoisted to "@types/react"
info This module exists because it's specified in "devDependencies".
info Disk size without dependencies: "164KB"
info Disk size with unique dependencies: "1.98MB"
info Disk size with transitive dependencies: "1.98MB"
info Number of shared dependencies: 2
=> Found "@types/react-dom#@types/[email protected]"
info This module exists because "@types#react-dom" depends on it.
info Disk size without dependencies: "164KB"
info Disk size with unique dependencies: "1.98MB"
info Disk size with transitive dependencies: "1.98MB"
info Number of shared dependencies: 2
āœØ  Done in 0.99s.

I then tried three things:

  1. Unlink my library > yarn cache clean > reinstall the library (this time from a freshly generated tgz tarball). The issue persisted
  2. Yarn remove @types/react from the Next.js folder (effectively leaving only @types/react v16.9.31 hoisted from @types/react-dom). The issue persisted
  3. Completely removed node_modules and yarn.lock and reinstalled all packages (including my library from the tarball). The issue persisted.

I'm still seing the same error message.

By the way, that same component works fine with dynamic loading when imported from Next.js project's own components folder but the goal is to use the external UI kit, obviously.

Upvotes: 2

Views: 4399

Answers (2)

Ihor
Ihor

Reputation: 3809

Using async functions also worked for me. So, your case would look like this, I think

const Modal = dynamic(
  async () => (await import('my-ui-kit')).Modal,
  { ssr: false }
);

Upvotes: 0

Malik Alimoekhamedov
Malik Alimoekhamedov

Reputation: 438

Here's the solution that came from Next.js's GitHub by @r0skar:


I've done the following:

In my external ui kit

export type { ModalProps } from './Modal.interface';

In Next.js

import { ModalProps } from 'my-ui-kit';
const Modal = dynamic<ModalProps>(() => import('my-ui-kit').then((mod) => mod.Modal), {
  ssr: false,
});

This made TypeScript (and myself) happy.

Upvotes: 4

Related Questions