I do not think what you are looking for is achievable with namespaces. But namespaces are a legacy feature from very early days of Typescript and their usage is (strongly) discouraged - from official docs:
[...] we recommended modules over namespaces in modern code.
and shortly again:
Thus, for new projects modules would be the recommended code organization mechanism.
In case of providing type definition removing usage of namespaces should be relatively straightforward.
The easiest option would be to declare exported objects as such by directly declaring their types. In case of Frontend
it would look something like that:
const Frontend: {
// in the main scope & Frontend
// redeclared with typeof
change: typeof change;
emptyChange: typeof emptyChange;
from: typeof from;
getActorId: typeof getActorId;
getConflicts: typeof getConflicts;
getLastLocalChange: typeof getLastLocalChange;
getObjectById: typeof getObjectById;
getObjectId: typeof getObjectId;
init: typeof init;
// in Frontend only
// declaration from scratch
applyPatch<T>(
doc: Doc<T>,
patch: Patch,
backendState?: BackendState
): Doc<T>;
getBackendState<T>(doc: Doc<T>): BackendState;
getElementIds(list: any): string[];
setActorId<T>(doc: Doc<T>, actorId: string): Doc<T>;
};
The above is not ideal as you need to type the exported function name twice, which is a bit error prone, but for amount of types you are dealing with probably totally fine.
The other option is to use auxiliary module to first group relative function together, then re-export them from auxiliary module and re-import from the main module:
declare module "automerge/frontend" {
export {
change,
emptyChange,
from,
getActorId,
getConflicts,
getLastLocalChange,
getObjectById,
getObjectId,
init
} from "automerge";
import { Doc, Patch, BackendState } from "automerge";
export function applyPatch<T>(
doc: Doc<T>,
patch: Patch,
backendState?: BackendState
): Doc<T>;
export function getBackendState<T>(doc: Doc<T>): BackendState;
export function getElementIds(list: any): string[];
export function setActorId<T>(doc: Doc<T>, actorId: string): Doc<T>;
}
declare module "automerge" {
/* other stuff */
import * as _Frontend from 'automerge/frontend'
const Frontend: typeof _Frontend
/* other stuff */
}
The above is a bit convoluted and rather inelegant due to circular nature of imports/exports. You could try to move all related functions to the module "automerge/frontend"
, but then you would need re-export them from there, which will change slightly semantics and all export will need to be explicit (prefixed with export
keyword - for example: export type Doc<T> = FreezeObject<T>;
).
As the most correct and future proof solution I could recommend refactoring the code into modules without any circular dependencies - probably it could require creating a common module for grouping shared types.
Btw. if you interested in any of the above options please let me know and I would happily create a PR and we could move a discussion there.