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

TypeScript中RemoveIndexSignature实现疑问:为何需双重条件判断?

为什么实现RemoveIndexSignature<T>需要双重extends条件判断?

先明确需求:我们要实现一个工具类型,把对象里的索引签名(比如[key: string]: any或[key: number]: any)剔除,只保留具体定义的属性。比如:

type Foo = {
  [key: string]: any;
  bar: string;
  baz: number;
};
// 预期 RemoveIndexSignature<Foo> = { bar: string; baz: number }

type Bar = {
  [key: number]: any;
  123: boolean;
  456: string;
};
// 预期 RemoveIndexSignature<Bar> = { 123: boolean; 456: string }

先看高赞的双重判断实现

常见的正确写法是这样的:

type RemoveIndexSignature<T> = {
  [K in keyof T as (
    string extends K ? never : 
    number extends K ? never : 
    K
  )]: T[K]
};

或者另一种利用模板字符串的变体:

type RemoveIndexSignature<T> = {
  [K in keyof T as K extends `${infer Str}` ? Str : never]: T[K]
};

为什么单条件判断不行?

如果你尝试简化成单条件,比如:

// 错误实现
type RemoveIndexSignature<T> = {
  [K in keyof T as K extends string ? K : never]: T[K]
};

会发现结果依然保留了字符串索引签名,根本没达到剔除效果。

问题出在TypeScript对索引签名和具体属性的类型区分上:

  • 当对象有字符串索引签名时,keyof T会包含string(宽泛类型)+ 具体属性的字面量(比如"bar")。
  • 当对象有数字索引签名时,keyof T会包含number(宽泛类型)+ 具体数字字面量(比如123)。

单条件K extends string只会筛选出所有属于string范畴的key——不管是宽泛的string索引,还是具体的"bar"字面量,都会被保留,自然无法剔除索引签名。

双重判断的核心逻辑

双重extends是在做反向校验,用来区分「宽泛的索引类型」和「具体的字面量属性」:

  1. 对于具体属性的字面量key(比如"bar"):
    • string extends "bar"是false(因为宽泛的string类型不能被更具体的字面量类型包含),所以会保留这个key。
  2. 对于宽泛的索引签名key(比如string):
    • string extends string是true,所以返回never,把这个索引签名剔除。

数字索引的逻辑完全一致:number extends K只有当K本身就是number类型(即数字索引签名)时才会成立,此时剔除;具体数字字面量则会被保留。

模板字符串的变体本质也是利用了同样的逻辑:宽泛的string/number类型无法被模板字符串捕获为具体的字面量,而具体属性的字面量可以被匹配,从而实现过滤。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 21:50:17