我有以下界面:
interface MyInterface {
GetChocolate: () => string;
GetVanilla: () => number;
SetChocolate: () => number;
SetVanilla: () => string;
}
接口仅由函数类型组成。我有一个函数,它接受
keyof MyInterface
并用它做一些事情(无关紧要)。但是,我只希望该函数接受返回类型为 string
的键。我有这样的东西:
type KeysReturnStrings<K extends keyof MyInterface> = ReturnType<MyInterface[K]> extends string ? K : never;
当我只传递一把钥匙时,这非常有用:
type Test1 = KeysReturnStrings<"GetChocolate">; // "GetChocolate"
type Test2 = KeysReturnStrings<"SetChocolate">; // never
但是当我传入时这不起作用
keyof MyInterface
:
type Test3 = KeysReturnStrings<keyof MyInterface>; // never
当我希望它是
Test3
时,"GetChocolate" | "SetVanilla"
的类型。
我可以创建一个不同的通用类型,根据键是否包含工作来过滤键
"Get"
:
type KeysNoGet<K extends keyof MyInterface> = K extends `Get${string}` ? never : K;
type Test4 = KeysNoGet<keyof MyInterface>; // "SetChocolate" | "SetVanilla"
我只是不明白为什么
Test4
有效但 Test3
不起作用。我唯一的猜测是,问题在于将 keyof MyInterface
传递到 ReturnType<MyInterface[K]>
中,因为这会创建该对象的返回类型的并集(string | number
),这显然不会扩展类型 string
。但无论如何 keyof MyInterface
都是一个并集,所以它也不应该能够扩展 Get${string}
。
是否有实用程序类型或其他泛型类型可以将这些键从联合类型中排除?
您希望
KeysReturnStrings<K>
distribute over K
中的 unions,因此
KeysReturnStrings<K1 | K2 | K3>
相当于 KeysReturnStrings<K1> | KeysReturnStrings<K2> | KeysReturnStrings<K3>
。但你的定义并没有这样做,你会得到意想不到的结果。
有几种方法可以让类型函数分布在联合体上;其中之一是使用“分布式条件类型”,其中您检查的类型是与要分配的联合相对应的“通用”类型参数。 type F<T> = T extends U ? X<T> : Y<T>
形式的类型将分布在 T
中的并集上。 您的
KeysNoGet<K>
就是这种形式,因此它的行为符合您的预期。因此,我们可以将 KeysReturnStrings<K>
的定义包装在“无操作”分配条件类型中,该类型始终采用 true
分支。我们仅将其用于其分配属性,而不用于任何类型的条件类型检查:
type KeysReturnStrings<K extends keyof MyInterface> = K extends unknown ?
ReturnType<MyInterface[K]> extends string ? K : never
: never;
现在你得到了你期望的行为:
type Test1 = KeysReturnStrings<"GetChocolate">;
// ^? type Test1 = "GetChocolate"
type Test2 = KeysReturnStrings<"SetChocolate">;
// ^? type Test2 = never
type Test3 = KeysReturnStrings<keyof MyInterface>;
// ^? type Test3 = "GetChocolate" | "SetVanilla"
Playground 代码链接