Suggestion
async function dynamicImport(): module {
switch(environment()) {
case 'browser': return await import('./browser/index.mjs')
case 'node': return await import('./node/index.mjs')
default: throw Error('Environment not supported')
}
}
export { Foo } = await dynamicImport() // use semantics of static import
Hi, I'm not sure if this an issue, feature request or general inquiry, but I am currently having some difficulties importing and exporting class definitions through ESM dynamic imports. I have provided a minimal reproduction of the issue I'm facing at the repository link below. This setup attempts to dynamically ex-export one of many sub-modules based on information returned for the environment at runtime.
https://github.com/sinclairzx81/esm-dynamic-class-export
With the main issues shown in the code below with associated comments.
import { Agent } from '@lib/agent'
async function dynamicImport() {
switch(Agent.resolve()) {
case 'chrome': return await import('./chrome/index.mjs')
case 'firefox': return await import('./firefox/index.mjs')
case 'node': return await import('./node/index.mjs')
default: 'Foo: Agent not supported'
}
}
// Here we dynamically import one of the constructors using the dynamicImport()
// function above. However by importing as `const` we lose the semantics of the
// class being imported. This means FooConstructor can not be used as a type
// annotation in the importers implementation.
//
// example:
//
// import { FooConstructor } from '@lib/foo'
//
// const foo = new FooConstructor() // ok
//
// function test(foo: FooConstructor) {} // error: not a class
//
const { Foo: FooConstructor } = await dynamicImport()
// To address this, we create the actual intended class to be exported by
// extending the imported FooConstructor const value. Because Foo is
// exported as a class definition, this enables importers to observe
// Foo with the semantics of a class.
//
// example:
//
// import { Foo } from '@lib/foo'
//
// const foo = new Foo() // ok
//
// function test(foo: Foo) { } // ok
//
export class Foo extends FooConstructor {}
// However, using this approach results in problems when compiling `@lib/foo` with declarations.
//
// command:
//
// tsc -p libs/foo/tsconfig.json --outDir target/build --declaration
//
// error:
//
// libs/foo/index.mts:13:26 - error TS4020: 'extends' clause of exported class 'Foo' has or
// is using private name 'FooConstructor'.
//
// export class Foo extends FooConstructor {}
// ~~~~~~~~~~~~~~
Compiled with the following.
$ tsc -p libs/foo/tsconfig.json --outDir target/build --declaration
✅ Viability Checklist
My suggestion meets these guidelines:
🔍 Search Terms
esm dynamic export class
⭐ Suggestion
I guess it would be good if TypeScript could provide a way to hint that a functions role is to dynamically import modules and that TypeScript should use the same semantics for static imports. I think the core of the issue currently is that (afaik) the dynamically imported module needs to be imported into a const before re-export.
const { FooConstructor } = await dynamicImport() // FooConstructor is not a class from TS's perspective
export class Foo extends FooConstructor {} // this doesn't seem right
So I guess one idea I may be to have a module return type annotation that hints to TypeScript that it should treat the functions return type similar to a static import (where imported classes are observed as class definitions, and not typeof definitions). Allowing for something like the following.
async function dynamicImport(): module {} // <-- perhaps an assertion could be added to imply that TS should treat
// the return value as a kind of static import. This would mitigate
// the need to export a runtime class definition.
export { Foo } = await dynamicImport()
📃 Motivating Example
See https://github.com/sinclairzx81/esm-dynamic-class-export
💻 Use Cases
To be able to use ESM dynamic imports to implement functionality for multiple JavaScript environments and to have TypeScript treat the dynamic import using the same semantics as a static import.
Suggestion
Hi, I'm not sure if this an issue, feature request or general inquiry, but I am currently having some difficulties importing and exporting class definitions through ESM dynamic imports. I have provided a minimal reproduction of the issue I'm facing at the repository link below. This setup attempts to dynamically ex-export one of many sub-modules based on information returned for the environment at runtime.
https://github.com/sinclairzx81/esm-dynamic-class-export
With the main issues shown in the code below with associated comments.
Compiled with the following.
✅ Viability Checklist
My suggestion meets these guidelines:
🔍 Search Terms
esm dynamic export class
⭐ Suggestion
I guess it would be good if TypeScript could provide a way to hint that a functions role is to dynamically import modules and that TypeScript should use the same semantics for static imports. I think the core of the issue currently is that (afaik) the dynamically imported module needs to be imported into a
constbefore re-export.So I guess one idea I may be to have a
modulereturn type annotation that hints to TypeScript that it should treat the functions return type similar to a static import (where imported classes are observed as class definitions, and nottypeofdefinitions). Allowing for something like the following.📃 Motivating Example
See https://github.com/sinclairzx81/esm-dynamic-class-export
💻 Use Cases
To be able to use ESM dynamic imports to implement functionality for multiple JavaScript environments and to have TypeScript treat the dynamic import using the same semantics as a static import.