What's the return type of a dynamic import?

32.5k Views Asked by At

I have a file, which must be loaded asynchronously, so I made a function, which loads this file and returns the Promise:

export function load() {
    // ...
    return import(filename);
}

What is the return type of this Function? Promise<any> works, but it feels very weird. I'd like to write the signature as.

export function load() -> Promise<???>;
5

There are 5 best solutions below

1
Remo H. Jansen On BEST ANSWER

You need to use an import type and TypeScript 2.9 or higher. Here is an example:

my_module.ts

export const user = { name: "John", age: 30 };
export const event = { name: "Birthday", date: new Date(1989, 13, 2) };

demo.ts

type ModuleType = typeof import("./my_module"); // This is the import type!

export function load(): Promise<ModuleType> {
    // ...
    return import("./my_module");
}

(async () => {
    const module = await load();
    console.log(module.user.age); // It works!
})();

tsconfig.json (Added for reference)

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "lib": [
      "es2015",
      "dom"
    ], 
    "strict": true,  
    "esModuleInterop": true
  }
}
1
Ian Campbell On

With latest React, the dynamic import types are:

type DynamicImportType = () => Promise<{ default: React.ComponentType<any>; }>;
type LazyComponentType = React.LazyExoticComponent<React.ComponentType<any>>;

const dynamicImport: DynamicImportType = () => import('./MyComponent');
const LazyComponent: LazyComponentType = React.lazy(dynamicImport);

Resources

0
unadlib On

counter.ts:

export class Counter {}

export const list = ['test'];

index.ts:

type ImportClass<T, K extends keyof T> = T extends Record<K, infer S>
  ? S extends new (...args: any[]) => infer R
    ? R
    : never
  : never;

type ImportType<T, K extends keyof T> = T extends Record<K, infer R>
  ? R
  : never;

type Counter = ImportClass<typeof import('./counter'), 'Counter'>;

type List = ImportType<typeof import('./counter'), 'list'>;
0
Ido On

In my case I was able to just import as usual just the types, and dynamically import the actual classes etc I used.

For example, I have a HttpModule.ts which uses import { ... } from "uWebSockets.js" Even though it's in my internal library, I wasn't actually using the HttpModule.ts (but my library included it in the index.ts) I still got errors of the binaries uWebSockets.js where using is missing, in other words it's loaded the module.

This is because I had:

import uWS, {
    HttpRequest as uWSHttpRequest,
    HttpResponse as uWSHttpResponse,
    RecognizedString,
    us_socket_context_t
} from "uWebSockets.js";

Changing to just including the types:

import {
    HttpRequest as uWSHttpRequest,
    HttpResponse as uWSHttpResponse,
    RecognizedString,
    us_socket_context_t
} from "uWebSockets.js";

Skipped the file, when to initialize the actual uWS I did:

const app = (await import("uWebSockets.js")).App({});

Now, only when I'm actually using the HttpModule.ts it will import the uWebSockets.js module.

0
robstarbuck On

The type of import you want to consume can be defined for both default and named exports.

For Default Exports

// File with export - default-exporter.ts
export default {
  value: 451,
};

// File with import - index.ts
type DefaultImport = Promise<{ default: { value: number } }>;

const defaultImport: DefaultImport = import("./default-exporter");

For Named Exports

// File with export - named-exporter.ts
export const namedExport = {
  value: 451,
};

// File with import - index.ts
type NamedImport = Promise<{ namedExport: { value: number } }>;

const namedImport: NamedImport = import("./named-exporter");

NB Mostly these answers include the typeof operator in typescript which great for knowing what your importing but can't limit what you import.