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

如何正确声明包含动态泛型T值的Map/Record映射

问题描述

我正尝试为固定字符串枚举类型CellTemplate中的每个键,映射对应泛型参数不同的CustomCellRenderer——这类渲染器的value参数和additionalProps支持泛型定义。相关类型与实现代码如下:

// :: 基础类型定义 ::
export type CellTemplate = "checkbox" | "badge" | "label" | "link";
export interface CustomCellRenderer<
  TValue = unknown,
  TRendererProps = unknown
> {
  (
    value: TValue,
    key: string,
    label: string,
    row: unknown,
    additionalProps?: TRendererProps
  ): JSX.Element;
}
// :: 基础类型定义 ::

// :: 渲染器类型实现 ::
type LinkCellRendererProps = {
  linkTo?: string;
};
export type LinkCustomCellRendererType = CustomCellRenderer<
  string,
  LinkCellRendererProps
>;
export type LabelCustomCellRendererType = CustomCellRenderer<string>;
export type CheckboxCustomCellRendererType = CustomCellRenderer<boolean>;
export type BadgeCustomCellRendererType = CustomCellRenderer<string | string[]>;

// 每个渲染器遵循相同结构,示例:
export const BadgeCellRenderer: BadgeCustomCellRendererType = function (
  value: string | string[],
  key: string,
  label: string,
  row: unknown
): JSX.Element {
  // ... 具体渲染逻辑
};
// :: 渲染器类型实现 ::

我希望开发者指定CellTemplate类型的字符串时,能自动关联到对应的CustomCellRenderer类型。目前尝试用makeMap函数创建映射:

function makeMap<K>(map: Record<CellTemplate, K>): Record<CellTemplate, K> {
  return map;
}
// 问题:无法支持不同泛型类型的渲染器映射
const CustomComponentRenderer = makeMap({
  checkbox: CheckBoxCellRenderer,
  badge: BadgeCellRenderer,
  label: LabelCellRenderer,
  link: LinkCellRenderer,
});

但不同渲染器的泛型参数存在差异(比如LabelCellRenderer对应string类型、CheckBoxCellRenderer对应boolean类型),导致K类型冲突;使用makeMap<any>虽然能运行,但会丢失后续SampleProps接口中需要的类型提示:

interface SampleProps<TCellTemplate extends CellTemplate = CellTemplate, TRow = unknown> {
  template?: TCellTemplate;
  templateAdditionalProps?: (row: TRow) => typeof CustomComponentRenderer[TCellTemplate];
}

如何正确实现这一映射并保留完整的类型推断能力?


解决方案

核心思路是让TypeScript明确每个CellTemplate键对应的具体渲染器类型,避免用强制统一类型的工具函数,直接通过类型别名或自动推断的方式建立键与类型的关联。

1. 定义模板与渲染器的类型映射

先创建一个类型别名,显式声明每个CellTemplate对应的CustomCellRenderer具体类型:

// 建立CellTemplate到对应渲染器类型的映射
type CellRendererMap = {
  checkbox: CheckboxCustomCellRendererType;
  badge: BadgeCustomCellRendererType;
  label: LabelCustomCellRendererType;
  link: LinkCustomCellRendererType;
};

2. 创建类型安全的渲染器映射对象

直接用const声明映射对象,并指定类型为CellRendererMap,确保每个键对应正确的渲染器:

// 类型安全的渲染器映射
const CustomComponentRenderer: CellRendererMap = {
  checkbox: CheckBoxCellRenderer,
  badge: BadgeCellRenderer,
  label: LabelCellRenderer,
  link: LinkCellRenderer,
};

3. 完善SampleProps的类型提示

为了让templateAdditionalProps能根据选中的template自动推断类型,需要一个辅助类型从CustomCellRenderer中提取TRendererProps泛型参数:

// 辅助类型:提取CustomCellRenderer的附加属性类型
type GetRendererProps<T extends CustomCellRenderer> = T extends CustomCellRenderer<any, infer P> ? P : never;

interface SampleProps<TCellTemplate extends CellTemplate = CellTemplate, TRow = unknown> {
  template?: TCellTemplate;
  // 根据template自动推断对应的附加属性类型
  templateAdditionalProps?: (row: TRow) => GetRendererProps<CellRendererMap[TCellTemplate]>;
}

4. (可选)自动推断映射类型

如果不想手动维护CellRendererMap的类型定义,可以让TypeScript自动推断映射对象的类型:

// 创建映射并让TS自动推断类型(as const 确保类型不会被拓宽)
const CustomComponentRenderer = {
  checkbox: CheckBoxCellRenderer,
  badge: BadgeCellRenderer,
  label: LabelCellRenderer,
  link: LinkCellRenderer,
} as const;

// 提取自动推断的映射类型
type CellRendererMap = typeof CustomComponentRenderer;

这种方式更灵活,新增模板时无需手动修改类型定义,TypeScript会自动更新CellRendererMap。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 07:07:07