![](/img/trans.png)
[英]How can I add a type from an npm package into a `d.ts` file declared namespace?
[英]In a handwritten d.ts file, how can I expose functions from one namespace in the module root?
我正在开发一个全部在 javascript 中但导出手写类型声明( automerge/index.d.ts )的存储库。
代码库的结构是它有一个前端和一个后端,加上一个公共的API,它提供了自己的一些便利功能,此外还直接从前端和后端重新导出了一些功能。
像这样的东西:
declare module `foo` {
// functions that only exist in the public API
function a
function b
function c
// functions exposed directly from namespace A
function q
function r
function s
// functions exposed directly from namespace B
function x
function y
function z
namespace A {
function q
function r
function s
function t
}
namespace B {
function v
function w
function x
function y
function z
}
}
这是实际代码的摘录,显示了我们当前如何为重新导出的函数编写重复声明。
declare module 'automerge' {
...
function getObjectById<T>(doc: Doc<T>, objectId: OpId): Doc<T>
namespace Frontend {
...
function getObjectById<T>(doc: Doc<T>, objectId: OpId): Doc<T>
}
...
}
有没有办法避免两次编写这些声明?
我不认为你正在寻找的是可以通过命名空间实现的。 但是命名空间是 Typescript 早期的遗留功能,并且(强烈)不鼓励使用它们 - 来自官方文档:
[...] 我们推荐在现代代码中使用模块而不是名称空间。
不久又一次:
因此,对于新项目,模块将是推荐的代码组织机制。
在提供类型定义的情况下,删除命名空间的使用应该相对简单。
最简单的选择是通过直接声明它们的类型来声明导出的对象。 在Frontend
的情况下,它看起来像这样:
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>;
};
以上内容并不理想,因为您需要输入两次导出的 function 名称,这容易出错,但对于您处理的类型数量可能完全没问题。
另一种选择是使用辅助模块首先将相关的 function 组合在一起,然后从辅助模块重新导出它们并从主模块重新导入:
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 */
}
由于进出口的循环性质,上述内容有点令人费解且相当不雅。 您可以尝试将所有相关功能移动到module "automerge/frontend"
,但是您需要从那里重新导出它们,这会稍微改变语义并且所有导出都需要显式(以export
关键字为前缀 - 例如: export type Doc<T> = FreezeObject<T>;
)。
作为最正确和面向未来的解决方案,我可以建议将代码重构为没有任何循环依赖关系的模块——可能需要创建一个通用模块来对共享类型进行分组。
顺便提一句。 如果您对上述任何选项感兴趣,请告诉我,我很乐意创建一个 PR,我们可以在那里进行讨论。
这是简化的示例,但您可以通过这种方式实现不重复:
// backend.d.ts
declare module "backend" {
export function Subtract(a: number, b: number): number;
}
然后:
// foo.d.ts
declare module "foo" {
export function Add(a: number, b: number): number;
export * from "backend";
export * as B from "backend";
}
最后,用法:
// main.ts
import * as foo from "foo";
foo.Add(1, 2); // defined only in the "foo".
foo.Subtract(1, 2); // "backend" function exposed in the root of "foo".
foo.B.Subtract(1, 2); // same "backend" function exposed in the "B" (namespace) of "foo".
一种可能性是定义一个箭头 function 类型别名并在两个地方都使用它。 例如:
declare module "automerge" {
type GetObjectById = <T>(doc: Doc<T>, objectId: OpId) => Doc<T>
const getObjectById: GetObjectById
namespace Frontend {
const getObjectById: GetObjectById
}
}
不幸的是,不可能直接对“常规” function 声明做同样的事情(见这里)。
箭头函数和 function 声明并不完全相同,尤其是围绕 function 中this
范围。 例如箭头函数不能有this
参数:
// not allowed
const fn = (this: SomeContext) => void
// allowed
function fn(this: SomeConext): void
但是,如果您不依赖它们不同的任何功能,或者可以在 js 代码中切换到箭头函数以确保安全,那么这应该可以工作。
这样的事情会部分帮助你:
declare module 'automerge' {
namespace Frontend {
function getObjectById<T>(doc: T, objectId: any): T;
}
const getObjectById: typeof Frontend.getObjectById;
}
优点:
缺点:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.