My quest began with a desire to solve a problem faced by others, leading me down a complex path of attempting to export and import a value both as a default export and a named export (with valuable insights from @nathan-shively-sanders).
The resolution I reached can be found at https://github.com/DefinitelyTyped/DefinitelyTyped/pull/42315.
The initial type definitions I worked with had the following structure:
//
// The class intended for dual export/import as:
//
// - A named export (already functional due to `export class`)
// - A default export (initially malfunctioning)
//
export class Strategy extends passport.Strategy { /* ... */ }
export interface Profile extends passport.Profile { /* ... */ }
export interface AuthenticateOptions extends passport.AuthenticateOptions { /* ... */ }
export interface StrategyOption { /* ... */ }
export interface StrategyOptionWithRequest extends StrategyOption { /* ... */ }
export interface ExtraVerificationParams { /* ... */ }
export type VerifyFunction = ( /* ... */ ) => void;
export type VerifyFunctionWithRequest = ( /* ... */ ) => void;
To resolve this issue, I had to take several steps:
- Rename the class from
Strategy
to StrategyInternal
to manage circular references during export import ...
- Change the
export
before the class to declare
- Declare a namespace using the new name
StrategyInternal
at the bottom of the file (referred to as Namespace Merging)
- Move all other interfaces and types to the namespace, removing the
export
declarations
- Update internal references within the class to use fully qualified names like
StrategyInternal.VerifyFunction
- Utilize
export import
within the namespace to enable named exports
- Set the default export with
export = StrategyInternal
at the end of the file
The finalized solution summary is as follows:
declare class StrategyInternal extends passport.Strategy {
// Updated references to external types and interfaces
// For example, changing `VerifyFunction` to `StrategyInternal.VerifyFunction`
}
declare namespace StrategyInternal {
interface Profile extends passport.Profile { /* ... */ }
interface AuthenticateOptions extends passport.AuthenticateOptions { /* ... */ }
interface StrategyOption { /* ... */ }
interface StrategyOptionWithRequest extends StrategyOption { /* ... */ }
interface ExtraVerificationParams { /* ... */ }
type VerifyFunction = ( /* ... */ ) => void;
type VerifyFunctionWithRequest = ( /* ... */ ) => void;
// NOTE: not applicable for `export import` statements
// tslint:disable-next-line:strict-export-declare-modifiers
export import Strategy = StrategyInternal;
}
export = StrategyInternal;
I'm sharing these details in hopes that it may serve as a solution, particularly because it successfully passes tests and accommodates various styles of ESM imports (when using "esModuleInterop": true
in tsconfig):
import {Strategy} from 'passport-auth0';
...or...
import Strategy from 'passport-auth0';
If you are not employing ES Modules interop, these alternative import styles are also supported:
import Strategy = require('passport-auth0');
...or...
import {Strategy} = require('passport-auth0');
Furthermore, in CommonJS written in JavaScript:
const Strategy = require('passport-auth0');
...or...
const {Strategy} = require('passport-auth0');