Typescript 在手写的d.ts文件中,如何公开模块根中一个名称空间中的函数?
我正在开发一个repo,它全部使用javascript,但可以导出手写类型声明() 代码库的结构是,它有一个前端和一个后端,加上一个公共API,该API提供了自己的一些方便的函数,此外还可以直接从前端和后端重新导出一些函数 大概是这样的:Typescript 在手写的d.ts文件中,如何公开模块根中一个名称空间中的函数?,typescript,typescript-declarations,typescript-namespace,Typescript,Typescript Declarations,Typescript Namespace,我正在开发一个repo,它全部使用javascript,但可以导出手写类型声明() 代码库的结构是,它有一个前端和一个后端,加上一个公共API,该API提供了自己的一些方便的函数,此外还可以直接从前端和后端重新导出一些函数 大概是这样的: declare module `foo` { // functions that only exist in the public API function a function b function c // functions ex
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>
}
...
}
声明模块“自动合并”{
...
函数getObjectById(doc:doc,objectId:OpId):doc
命名空间前端{
...
函数getObjectById(doc:doc,objectId:OpId):doc
}
...
}
有没有办法避免两次编写这些声明?一种可能是定义一个arrow函数类型别名,并在这两个位置使用它。例如:
声明模块“自动合并”{
键入GetObjectById=(doc:doc,objectId:OpId)=>doc
常量getObjectById:getObjectById
命名空间前端{
常量getObjectById:getObjectById
}
}
不幸的是,不可能对“常规”函数声明直接执行相同的操作(请参阅)
箭头函数和函数声明是非常重要的,尤其是围绕函数中的this
的范围。例如,箭头函数不能有此参数:
//不允许
const fn=(this:SomeContext)=>void
//允许
函数fn(this:SomeConext):void
但是,如果您不依赖于它们不同的任何功能,或者为了安全起见,可以在js代码中切换到箭头函数,那么这应该是可行的。我认为您所寻找的名称空间是无法实现的。但是名称空间是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>;
};
const前端:{
//在主要范围和前端
//用typeof重新声明
变更:变更类型;
emptyChange:emptyChange的类型;
from:from的类型;
getActorId:getActorId的类型;
getConflicts:getConflicts的类型;
getLastLocalChange:getLastLocalChange的类型;
getObjectById:getObjectById的类型;
getObjectId:getObjectId的类型;
init:init的类型;
//仅在前端
//从头开始申报
applyPatch(
博士:博士,
补丁:补丁,
后端状态?:后端状态
):Doc;
getBackendState(doc:doc):后端状态;
getElementId(列表:任意):字符串[];
setActorId(doc:doc,actorId:string):doc;
};
上述情况并不理想,因为您需要键入导出的函数名两次,这很容易出错,但对于您正在处理的类型数量来说,可能完全没有问题
另一个选项是使用辅助模块首先将相关功能组合在一起,然后从辅助模块重新导出,再从主模块重新导入:
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 */
}
声明模块“自动合并/前端”{
出口{
改变
空变,
从…起
赫塔克特里德,
获取冲突,
getLastLocalChange,
getObjectById,
getObjectId,
初始化
}从“自动合并”;
从“自动合并”导入{Doc,Patch,BackendState};
导出函数applyPatch(
博士:博士,
补丁:补丁,
后端状态?:后端状态
):Doc;
导出函数getBackendState(doc:doc):BackendState;
导出函数getElementId(列表:任意):字符串[];
导出函数setActorId(doc:doc,actorId:string):doc;
}
声明模块“自动合并”{
/*其他东西*/
从“自动合并/前端”导入*作为前端
const Frontend:typeof _Frontend
/*其他东西*/
}
由于进出口的循环性质,上述情况有点复杂,相当不雅。您可以尝试将所有相关函数移动到模块“automerge/frontend”
,但是您需要从那里重新导出它们,这将稍微改变语义,并且所有导出都需要显式(前缀为export
关键字-例如:export type Doc=FreezeObject;
)
作为最正确、最经得起未来考验的解决方案,我建议将代码重构为没有任何循环依赖关系的模块——可能需要创建一个公共模块来对共享类型进行分组
顺便说一句。如果您对上述任何选项感兴趣,请让我知道,我很乐意创建一个公关,我们可以在那里进行讨论。类似的内容将部分帮助您:
declare module 'automerge' {
namespace Frontend {
function getObjectById<T>(doc: T, objectId: any): T;
}
const getObjectById: typeof Frontend.getObjectById;
}
声明模块“自动合并”{
命名空间前端{
函数getObjectById(doc:T,objectId:any):T;
}
const getObjectById:typeof Frontend.getObjectById;
}
优点:
- 通过重用已声明函数的类型来减少代码量
缺点:
- 并没有真正消除使用完全相同的名称声明const/函数两次的需要
这是一个简化的示例,但您可以通过以下方式实现无复制:
//backend.d.ts
将模块声明为“后端”{
导出函数减法(a:数字,b:数字):数字;
}
然后:
//foo.d.ts
声明模块“foo”{
导出功能添加(a:编号,b:编号):编号;
从“后端”导出*;
从“后端”将*导出为B;
}
最后是用法:
//main.ts
从“foo”导入*作为foo;
foo.Add(1,2);