You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript声明文件中为导出对象添加动态属性的解决方案

问题背景

我正尝试向DefinitelyTyped仓库提交PR,为一个JavaScript库创建对应的@types类型包,待编写类型的JavaScript库源码结构如下:

const COMMANDS = [
  "about", "authorize", "backend", "cat", "check", "checksum", "cleanup",
  "config", "config create", "config delete", "config disconnect", "config dump",
  "config edit", "config file", "config password", "config providers",
  /* 此处省略大量其余命令 */
];

const api = function(...args) {
  // 省略无关实现逻辑
}

const promises = api.promises = function(...args) {
  // 省略无关实现逻辑
}

COMMANDS.forEach(command => {
  Object.defineProperty(api, command, {
    value: function(...args) {
      return api(command, ...args);
    }
  });

  Object.defineProperty(api.promises, command, {
    value: function(...args) {
      return promises(command, ...args);
    }
  });
});

module.exports = api;

强制约束:由于该库使用module.exports语法导出,DefinitelyTyped校验规则要求类型声明必须使用*export = api语法,禁止使用export default api*语法,所有方案必须满足该要求。

目前已尝试编写的声明文件代码如下:

/// <reference types="node" />

import { ChildProcess } from "child_process";

declare const COMMANDS: [
  "about", "authorize", "backend", "cat", "check", "checksum", "cleanup",
  "config", "config create", "config delete", "config disconnect", "config dump",
  "config edit", "config file", "config password", "config providers",
  /* 此处省略大量其余命令 */
];

type CommandsUnion = typeof COMMANDS[number];

declare function api(
  ...args: (string | Object)[]
): ChildProcess;

declare namespace api {
  type PromiseApiFn = (
    ...args: (string | Object)[]
  ) => Promise<Buffer>;

  const promises: PromiseApiFn & {
    [Command in CommandsUnion]: PromiseApiFn
  };
}

export = api;

当前核心阻塞问题:上述写法仅完成了api.promises上对应命令属性的类型声明,无法将COMMANDS数组中对应的命令属性正确挂载到api函数对象本身的类型上,需要可行的实现方案。

可行实现方案

直接使用交叉类型合并函数调用签名与所有静态挂载属性即可,不需要依赖declare function + declare namespace的声明合并逻辑,最终类型定义代码如下:

/// <reference types="node" />

import { ChildProcess } from "child_process";

// 补全所有命令到联合类型中即可
type CommandsUnion =
  | "about"
  | "authorize"
  | "backend"
  | "cat"
  | "check"
  | "checksum"
  | "cleanup"
  | "config"
  | "config create"
  | "config delete"
  | "config disconnect"
  | "config dump"
  | "config edit"
  | "config file"
  | "config password"
  | "config providers";
// 此处继续补充剩余省略的命令

// 基础调用签名类型
type BaseSyncFn = (...args: (string | object)[]) => ChildProcess;
type BaseAsyncFn = (...args: (string | object)[]) => Promise<Buffer>;

// 合并api所有类型:同步调用签名 + 顶层命令方法 + promises异步API
declare const api: BaseSyncFn & {
  [Command in CommandsUnion]: BaseSyncFn;
} & {
  promises: BaseAsyncFn & {
    [Command in CommandsUnion]: BaseAsyncFn;
  };
};

export = api;

方案说明:

  • 完全符合export = api的导出要求,不会触发DefinitelyTyped的格式校验错误
  • 支持所有调用场景的类型提示:直接调用api(xxx)、调用顶层命令方法如api.about(xxx)、调用异步入口api.promises(xxx)、调用异步命令方法如api.promises.about(xxx)都能得到正确的类型推导
  • 原写法中大写的Object类型替换为TS推荐的小写object类型,避免不必要的类型兼容问题
  • 不需要单独声明元组类型的COMMANDS常量,直接维护命令联合类型即可,减少冗余代码

内容的提问来源于stack exchange,提问作者atifcppprogrammer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 05:06:20