如何获取`keyof T`的子集,其值T[K]是Typescript中的可调用函数

Joo*_*oon 7 typescript

我想keyof T根据类型过滤T[keyof T]

它应该像这样工作:

type KeyOfType<T, U> = ...

KeyOfType<{a: 1, b: '', c: 0, d: () => 1}, number> === 'a' | 'c'

KeyOfType<{a: 1, b: '', c: 0: d: () => 1}, string> === 'b'

KeyOfType<{a: 1, b: '', c: 0: d: () => 1}, Function> === 'd'
Run Code Online (Sandbox Code Playgroud)

这可能吗?

Tit*_*mir 21

您可以使用条件和映射类型来做到这一点

type KeyOfType<T, U> = {[P in keyof T]: T[P] extends U ? P: never}[keyof T]
Run Code Online (Sandbox Code Playgroud)

让我们把事情分解一下。

我们可以从一个映射类型开始,它具有与原始类型相同的属性T,这是一个简单的标准映射类型:

type KeyOfType<T> = { [P in keyof T]: T[P] } // New Type same as the original
Run Code Online (Sandbox Code Playgroud)

T[P]是一个类型查询,表示 type 中键P的类型T。我们可以将其更改为 just P,这意味着新属性的类型与其名称相同:

type KeyOfType<T> = { [P in keyof T]: P }
// So
KeyOfType<{ a: number, b: string }> == { a: 'a', b: 'b' }
Run Code Online (Sandbox Code Playgroud)

我们可以向这个类型添加一个类型查询来再次获取该类型的所有键。通常,构造T[keyof T]获取类型的所有属性类型。将此应用于我们的映射类型,该类型具有与我们基本上返回的键名相同的属性类型keyof T

type KeyOfType<T> = { [P in keyof T]: P }[keyof T]
// So
KeyOfType<{ a: number, b: string }> ==  'a'|'b' 
Run Code Online (Sandbox Code Playgroud)

现在我们可以向 o not always select 添加条件类型P。因为A | never == A如果原始属性(T[P])的类型不满足某个约束,我们可以将映射类型中的属性类型设置为从不。

为了表达约束,我们添加了一个额外的泛型参数,U并使用了形式为 的条件类型T extends U ? TypeIfTrue: TYpeIfFalse。把它放在一起,我们得到:

type KeyOfType<T, U> = {[P in keyof T]: T[P] extends U ? P: never}[keyof T]
Run Code Online (Sandbox Code Playgroud)