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

TypeScript可选泛型实现返回对象类型推断并保留自动补全

TypeScript类型安全事件映射实现问题

需求概述

希望构建类型安全的事件系统,创建事件映射对象时强制事件符合指定分组规则,同时保留对象结构的智能提示:

type DashboardGroups = "home" | "settings" | "profile";
type DashboardEvent = IMetricsEvent<DashboardGroups>;

// 事件必须符合`{ [event:string]: DashboardEvent }`结构
const DashboardEventsMap = createEventMapping<DashboardEvent>({
  validEvent: {
    name: "valid event",
    group: "home" // ✅ 校验通过
  },
  invalidEvent: {
    name: "invalid event",
    group: "invalid group", // ❌ 触发类型错误
  }
})

// 使用时保留智能提示
DashboardEventsMap.validEvent // ✅ 自动补全正常
DashboardEventsMap.eventWhichDoesntExist // ❌ 触发类型错误

事件结构定义

事件接口支持自定义分组类型:

export interface IMetricsEvent<
  TGroup extends string = string,
> {
  name: string;
  group?: TGroup;
}

当前实现的问题

现有createMetricsEventMapping函数无法同时满足类型校验和智能提示需求:

type MetricsEventMapType<TEvent extends IMetricsEvent> = {
  [event: string]: TEvent;
};

export const createMetricsEventMapping = <
  TMetricsEvent extends IMetricsEvent,
  T extends MetricsEventMapType<TMetricsEvent> = MetricsEventMapType<TMetricsEvent>,
>(
  arg: T,
) => arg;

问题1:未传入类型参数时

自动补全正常,但分组无类型校验:

const map = createMetricsEventMapping({event: {name:"event", group:"any group"}});

map.event // ✅ 自动补全正常,但分组可任意输入

问题2:传入事件类型参数时

分组类型校验生效,但失去对象结构的智能提示:

type DashboardEvent = IMetricsEvent<"home">;
const map = createMetricsEventMapping<DashboardEvent>({event: {name:"event", group:"any group"}});

map.event // ❌ 无自动补全,仅能通过字符串索引访问

问题3:移除默认类型参数

移除T的默认值后,传入单个类型参数会报错:

export const createMetricsEventMapping = <
  TMetricsEvent extends IMetricsEvent,
  T extends MetricsEventMapType<TMetricsEvent>,
>(
  arg: T,
) => arg;

type DashboardEvent = IMetricsEvent<"home">;
// ❌ 错误:期望2个类型参数,但只传入了1个
const map = createMetricsEventMapping<DashboardEvent>({event: {name:"event", group:"any group"}});

解决方案

使用嵌套泛型函数实现,先指定事件类型,再传入映射对象,同时满足类型校验和智能提示需求:

export const createMetricsEventMapping = <TEvent extends IMetricsEvent>() => {
  // 内层函数自动推断映射对象的键值结构,同时约束值为指定的TEvent类型
  return <T extends Record<string, TEvent>>(events: T): T => events;
};

使用示例

type DashboardGroups = "home" | "settings" | "profile";
type DashboardEvent = IMetricsEvent<DashboardGroups>;

// 先调用外层函数指定事件类型,再传入事件映射
const DashboardEventsMap = createMetricsEventMapping<DashboardEvent>()({
  validEvent: {
    name: "valid event",
    group: "home" // ✅ 校验通过
  },
  invalidEvent: {
    name: "invalid event",
    group: "invalid group" // ❌ 触发类型错误:类型"invalid group"不能赋值给DashboardGroups
  }
});

// 使用时保留完整的智能提示
DashboardEventsMap.validEvent; // ✅ 自动补全正常
DashboardEventsMap.eventWhichDoesntExist; // ❌ 触发类型错误:属性不存在

无类型参数场景

如果不需要约束分组类型,直接调用内层函数即可,自动推断所有类型:

const map = createMetricsEventMapping()({
  event: {name:"event", group:"any group"}
});

map.event; // ✅ 自动补全正常

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 21:21:48