TypeScript实现枚举映射路由与DTO的泛型API方法
问题描述
现有20余个结构完全一致的接口请求方法,仅路由路径与返回DTO类型存在差异。为避免调用时重复传入路由、类型参数造成逻辑冗余,需要改造为仅传入实体标识(如Asset、Personnel)即可完成调用的通用方法。
原有重复实现示例:
async getAllAssets() { return await this.get<AssetDto[]>('assets/assets') } async getAllPersonnels() { return await this.get<PersonnelDto[]>('personnel/personnels') }
期望改造后支持传入枚举类参数,比如传入Entity.Asset或Entity.Personnel,方法内部自动匹配对应路由与返回DTO类型。
目前已做的尝试:
- 定义实体类型:
export type Asset = { route: '/assets/asset', dto: AssetDto[] } export type Personnel = { route: '/personnel/personnel', dto: PersonnelDto[] } export type Entity = Asset | Personnel
- 编写泛型方法初稿:
async getAll<T extends Entity>(entity: T) { return await this.get<typeof entity.Dto>(entity.route) }
当前卡点:不清楚如何通过传入类型/枚举值完成调用,不确定该思路是否可行,期望的调用形式如下:
async howIWantAFunctionCallToBeLike() { await this.getAll(Entity.Asset) }
实现方案
你的核心思路是对的,问题出在混淆了TypeScript的类型空间和JavaScript的运行时值空间:你之前定义的Asset/Personnel/Entity都只是TS类型,编译为JS后会被完全擦除,根本不能作为运行时参数传入函数。要实现需求,需要先定义运行时真实存在的枚举/常量作为传参标识,再通过TS类型映射关联标识对应的路由和返回类型。
- 第一步:定义运行时可用的实体枚举和类型映射
// 传参用的枚举,是真实存在的JS值,编译后不会消失 export enum EntityType { Asset = 'Asset', Personnel = 'Personnel' } // 类型映射表,用来关联每个枚举值对应的返回类型、路由字面量类型 export interface EntityMap { [EntityType.Asset]: { route: 'assets/assets', returnType: AssetDto[] }, [EntityType.Personnel]: { route: 'personnel/personnels', returnType: PersonnelDto[] } // 后续新增同类接口,只需要在这里加对应配置即可,不需要修改通用方法 }
- 第二步:抽离路由常量配置
路由是运行时需要用到的值,必须单独定义为JS常量,不能靠TS类型获取:
const ROUTE_MAP: Record<EntityType, string> = { [EntityType.Asset]: 'assets/assets', [EntityType.Personnel]: 'personnel/personnels' } as const
- 第三步:实现通用泛型方法
通过泛型约束自动推导传入实体对应的返回类型,不需要调用方手动传入泛型参数:
async getAll<T extends EntityType>(entity: T) { return await this.get<EntityMap[T]['returnType']>(ROUTE_MAP[entity]) }
调用效果
完全匹配你期望的调用形式,TS会自动推导返回值类型,不需要额外标注:
async testCall() { // assetList 自动推导为 AssetDto[] 类型 const assetList = await this.getAll(EntityType.Asset) // personnelList 自动推导为 PersonnelDto[] 类型 const personnelList = await this.getAll(EntityType.Personnel) }
注意:所有需要在运行时读取的配置(比如路由路径)都必须写成JS可执行的常量/枚举,不要尝试直接读取TS类型的属性,TS类型不会出现在最终运行的JS代码里。
内容的提问来源于stack exchange,提问作者JamesG
相关产品推荐
相关产品推荐

