I'm trying to write TypeScript typings for an existing Javascript library. The library is packaged as CJS, and exports several variables and functions at the top level. It also exports a single top level "namespace like" object with the same variables and functions. (I know this is not a "recommended" pattern; I'm not here to discuss that.)
All of these are valid in an ESM (.mjs) context:
// Default export
import lib from "lib";
const x = lib(...);
const y = lib.b(...);
// Top level function, namespace-like variable
import {b, lib} from "lib";
const x = lib(...);
const y = b(...);
I want to write typings that describe this pattern. I can write top-level exports like
export function b(...);
declare function lib(...);
export default lib;
but I don't see an easy way to create a namespace that re-uses the various top level exports, so I can't really merge the function declaration with a namespace declaration.
Likewise, I can wrap the other functions in a namespace to start with
export namespace lib {
function b(...);
}
but I don't see how to re-export lib.b
at the top level, as simply b
.
I've tried putting the top level exports in their own file ("top-exports.d.ts"), which lets me write
export * from "./top-exports";
import * as lib from "./top-exports";
export { lib }
but then I can't make lib
callable. I know that you can use "export assignment" to make a callable default (export = lib
), but then I'm back to the problem of making a callable, namespace-like object with both top-level and namespace-wrapped exports in the same place.
CodePudding user response:
I have a solution that sort of works. export = lib
is required if you want a a function merged with a namespace. The problem is that you cannot export * from
a module that was created using export assignment (export =
), so you have to re-export each item by hand:
// top-exports.d.ts
declare namespace lib {
function a(...);
type SomeType = ...;
// etc
}
declare function lib(...);
export = lib;
// lib.d.ts
import * as lib from "./top-exports";
import {a, b, c, d, SomeType, OtherType} from "./top-exports";
export default lib;
export {lib, a, b, c, d};
export type {SomeType, OtherType};
Of course this is much harder to maintain, spreading definitions across two files, and having to remember to include any new stuff in two extra places (the import {...}
and export {...}
statements), but at least you don't have to declare anything twice. I'd still really love to see a better answer.
CodePudding user response:
Unless I misunderstand a requirement, I think this is what you're looking for:
lib.d.ts
type A = () => string;
type B = () => number;
declare const a: A;
declare const b: B;
// ...etc.
interface Lib {
(): void;
a: A;
b: B;
// ...etc.
}
declare const lib: Lib;
declare namespace lib {
export type Num = number;
}
export {
lib as default,
a,
b,
lib,
// ...etc.
};
module.ts
import {a, lib} from './lib';
const x = a();
const y = lib();
const z = lib.b();
const n: lib.Num = 42;