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

如何实现TypeScript中Definition类型的动态扩展?

实现TypeScript字符串字面量联合类型的动态扩展

核心问题分析

你之前的两种方案无法满足需求的原因:

  • 方案1将联合类型与string合并后,TypeScript会直接将整个类型推断为string,丢失了具体字面量的自动补全能力。
  • 方案2的固定联合类型无法感知运行时动态添加的内容——TypeScript是静态类型系统,运行时操作不会自动同步到静态类型中。

可行解决方案:模块扩展 + 声明合并

这是TypeScript生态中扩展库类型的标准方式,既能保留原有字面量的自动补全,又允许用户添加自定义类型。

1. 包的核心实现代码

// your-package/index.ts
// 用接口承载可扩展的函数名字面量,接口支持声明合并
export interface FunctionNameMap {
  foo: never;
  bar: never;
}

// 将接口的键转换为联合类型,作为核心业务使用的类型
export type FunctionName = keyof FunctionNameMap;

// 运行时存储自定义函数的容器
const customFunctions: Record<string, () => void> = {};

// addFunction函数,用泛型捕获用户传入的函数名字面量
export function addFunction<Name extends string>(name: Name, fn: () => void) {
  customFunctions[name] = fn;
}

// 核心调用函数,使用扩展后的FunctionName类型
export function callFunction(name: FunctionName) {
  customFunctions[name]?.();
}

2. 用户端扩展类型的方式

用户只需通过declare module扩展FunctionNameMap接口,就能让核心类型自动更新:

// 用户代码
import { FunctionNameMap, addFunction, callFunction } from 'your-package';

// 扩展包的类型声明
declare module 'your-package' {
  interface FunctionNameMap {
    // 添加自定义函数名字面量
    baz: never;
  }
}

// 添加自定义函数
addFunction('baz', () => console.log('test'));

// 此时VS Code会自动补全foo、bar、baz三个选项
callFunction('baz');
callFunction('foo');

替代方案:泛型默认值

如果不想用模块扩展,也可以通过泛型默认值让用户主动传入自定义类型:

包的核心代码

// your-package/index.ts
export type DefaultFunctions = "foo" | "bar";
// 用泛型接收用户自定义的函数名,默认值为never(不扩展)
export type FunctionName<T extends string = never> = DefaultFunctions | T;

const customFunctions: Record<string, () => void> = {};

export function addFunction<Name extends string>(name: Name, fn: () => void) {
  customFunctions[name] = fn;
}

export function callFunction<T extends string>(name: FunctionName<T>) {
  customFunctions[name]?.();
}

用户端使用

import { FunctionName, addFunction, callFunction } from 'your-package';

// 定义自定义函数名类型
type CustomFunctions = "baz";

addFunction('baz', () => console.log('test'));
// 调用时指定泛型,获得自动补全
callFunction<CustomFunctions>('baz');

方案对比

  • 模块扩展:更符合TypeScript的类型扩展规范,用户只需一次声明,所有使用FunctionName的地方都会自动更新类型,体验更流畅。
  • 泛型默认值:无需修改类型声明,但需要用户在调用时手动指定泛型,适合简单场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 12:20:30