TypeScript中如何基于接口子集键生成可遍历的键列表?
TypeScript中如何基于接口子集键生成可遍历的键列表?
这个问题其实是TypeScript里很典型的「编译时类型」和「运行时值」的鸿沟问题,我来给你捋清楚怎么解决~
你遇到的核心问题是:Pick生成的ProjectColumns、ProjectFilterColumns只是编译时的类型,在运行时根本不存在对应的实体,自然没法直接用map遍历。而枚举之所以能遍历,是因为它在编译后会生成对应的运行时对象。
但你完全没必要重复定义枚举,我们可以用一个小技巧,让运行时数组和编译时类型保持同步,还不用重复写键:
解决方案步骤:
- 先定义运行时的键数组,用
as const把数组锁定为只读的字面量元组(这样TypeScript能精确推导每个元素的类型) - 从数组推导类型,代替之前的
Pick写法 - (可选)用
satisfies关键字(TypeScript 4.9+支持)约束数组元素必须是接口的键,避免手动写错
下面是适配你代码的完整示例:
export interface Project { id?: string; name?: string; color?: string; notes?: string; } // 1. 定义运行时列数组,用as const锁定类型,satisfies确保都是Project的键 const projectColumns = ['id', 'name', 'color', 'notes'] as const satisfies (keyof Project)[]; // 2. 从数组推导类型,等价于Pick<Project, "id" | "name" | "color" | "notes"> type ProjectColumns = typeof projectColumns[number]; // 过滤列同理 const projectFilterColumns = ['name', 'color'] as const satisfies (keyof Project)[]; type ProjectFilterColumns = typeof projectFilterColumns[number]; // 现在就能正常遍历啦! projectColumns.map(column => i18n.t(`ProjectColumn.${column}`)); projectFilterColumns.map(column => i18n.t(`ProjectFilterColumn.${column}`));
为什么这个方案更好?
- 不用重复定义键:数组里的键就是接口的子集,类型从数组自动推导,不会出现不一致
- 类型安全:
satisfies会在编译时检查数组元素是否属于Project的键,写错了直接报错 - 同时满足运行时遍历需求:数组是真实存在的运行时值,能正常调用
map
补充说明
如果你用的是低于4.9的TypeScript版本,没法用satisfies,可以用类型断言来替代:
const projectColumns = ['id', 'name', 'color', 'notes'] as const as (keyof Project)[];
效果类似,只是语法稍微啰嗦一点。
总的来说,你之前的思路没大问题,只是没分清TypeScript类型和运行时值的区别——只要把「类型层面的子集」同步成「运行时的数组」,就能解决你的遍历需求啦!
备注:内容来源于stack exchange,提问作者Jens
相关产品推荐
相关产品推荐

