您可以使用映射类型的as 子句来过滤类型的键:
type KeyOfType<T, V> = keyof {
[P in keyof T as T[P] extends V? P: never]: any
}
映射类型的as 子句允许我们操作键的名称。如果键映射到never,则键将从结果类型中删除。你可以在这个PR找到更多信息
前 TS 4.1
这个版本在 4.1 后仍然有效,只是其他方式更容易阅读。
您可以使用条件类型和映射类型来做到这一点
type KeyOfType<T, U> = {[P in keyof T]: T[P] extends U ? P: never}[keyof T]
让我们稍微分解一下。
我们可以从映射类型开始,它具有与原始T 相同类型的相同属性,这是一个简单的标准映射类型:
type KeyOfType<T> = { [P in keyof T]: T[P] } // New Type same as the original
T[P] 是一个类型查询,表示T 类型中的键P 的类型。我们可以将其更改为 P,这意味着新属性的类型与其名称相同:
type KeyOfType<T> = { [P in keyof T]: P }
// So
KeyOfType<{ a: number, b: string }> == { a: 'a', b: 'b' }
我们可以给这个类型添加一个类型查询来再次获取该类型的所有键。通常一个构造T[keyof T] 获取一个类型的所有属性类型。将其应用于我们的映射类型,它的属性类型与我们基本上返回到 keyof T 的键名相同:
type KeyOfType<T> = { [P in keyof T]: P }[keyof T]
// So
KeyOfType<{ a: number, b: string }> == 'a'|'b'
现在我们可以为不总是选择P 添加一个条件类型。由于A | never == A,如果原始属性(T[P])的类型不满足某个约束,我们可以将映射类型中的属性类型设置为never。
为了表达约束,我们添加了一个额外的通用参数U,并且我们使用了一个具有T extends U ? TypeIfTrue: TYpeIfFalse 形式的条件类型。把它们放在一起,我们得到:
type KeyOfType<T, U> = {[P in keyof T]: T[P] extends U ? P: never}[keyof T]