如何移除TypeScript接口的字符串索引签名以正确使用keyof?
问题背景
现有接口定义如下:
interface RowItem { [prop: string]: unknown; } interface MyItem extends RowItem { id: number; name: string; }
由于MyItem继承了带有字符串索引签名的RowItem,使用keyof MyItem会得到string | number | "id" | "name",无法直接限制函数参数为id/name这类明确声明的键名。因依赖库要求必须继承RowItem,且不想修改大量现有类型定义,需要一种方式提取出类型中不含索引签名的自有属性键。
解决方案
可以定义一个通用类型KnownKeys<T>,用于提取类型T中明确声明的属性键,排除字符串/数字索引签名:
type KnownKeys<T> = { [K in keyof T]: string extends K ? never : number extends K ? never : K } extends { [_ in keyof T]: infer U } ? U extends keyof T ? U : never : never;
使用示例
获取MyItem的明确属性键:
type MyItemKnownKeys = KnownKeys<MyItem>; // 类型为 "id" | "name"
定义接收明确键名的函数:
function getItemProperty<T extends RowItem>(item: T, key: KnownKeys<T>) { return item[key]; } const testItem: MyItem = { id: 123, name: "example" }; getItemProperty(testItem, "id"); // 类型正确,无报错 getItemProperty(testItem, "invalidKey"); // 类型检查报错,符合预期
类型原理说明
这个类型的核心逻辑是:
- 遍历
T的所有键K,如果K是string的子类型(对应字符串索引签名)或number的子类型(对应数字索引签名),则将该键映射为never - 通过条件类型的
infer关键字,提取出所有非never的键,得到最终的明确属性键集合
对之前尝试的失败分析
- Attempt1:由于
unknown是所有类型的父类型,所有属性都会匹配MyItem[prop] extends unknown,导致结果全为never - Attempt2:
Omit<MyItem, string>仅移除了字符串索引签名,但TS中对象的数字索引会被单独保留,因此剩余number类型的键,仍无法得到明确的属性名 - Attempt3:
Pick<MyItem, keyof MyItem>等价于原类型,没有过滤掉索引签名带来的string/number键
内容的提问来源于stack exchange,提问作者Vlad
相关产品推荐
相关产品推荐

