我正在开发一个新的库,它将包含一组回调函数。我想实现一种“命名空间”特性,这样接口和回调链就不会变得如此强大。
具有回调类型的基本回调/命名空间如下所示:
type AnyFunction = (...args: any) => void;
type DefinedTypeMap = {
[key: string]: AnyFunction | DefinedTypeMap;
};然后扩展的接口可以如下所示:
interface IMyCallbackFunctions extends DefinedTypeMap {
namespace: {
func: (msg: string) => void;
};
root: (msg: string) => void;
otherRoot: (msg: string) => void;
}我正在处理一个类函数,如果该值是AnyFunction类型(而不是嵌套的命名空间),它将只接受对象上的键。
由于接口是由开发人员使用库定义的(只要它们扩展了所需的接口),在本例中接受的函数参数只能是"root" | "otherRoot",因此我尝试过Omit<T, K>,并且在堆栈溢出上查找过高、低的解决方案(没有效果)。
此库使用Typescript@4.5.5
当前函数(它不支持正在进行的命名空间特性)如下所示:
on<E extends keyof P2PConnectionEventMap<T>, F extends P2PConnectionEventMap<T>[E]>(event: E, callback: F) => void并且应该修改为只允许event param接受接口上的值属于回调类型的键,而不是对象类型。
提前谢谢你的帮助,这让我很想把头发拔出来。
发布于 2022-02-10 15:43:35
让我们定义一个名为FuncKeys<T>的类型实用程序,它接受对象类型T,并返回键的友联市,其中每个键的属性类型都可分配给AnyFunction。目标是FuncKeys<IMyCallbackFunctions>应该计算为"root" | "otherRoot",并且不应该包含"namespace" (这是一个非函数对象)或string (来自扩展DefinedTypeMap接口上的索引签名 )。
让人痛苦的是,您的IMyCallbackFunctions接口相当于:
interface IMyCallbackFunctions {
[key: string]: DefinedTypeMap | AnyFunction;
namespace: {
func: (msg: string) => void;
};
root: (msg: string) => void;
otherRoot: (msg: string) => void;
}我刚刚显式地添加了索引签名。
当遇到这样的问题时,我通常会指向KeysMatching<T, V>实用程序,如在TypeScript中,如何获取值为给定类型的对象类型的键?的答案所示。但是,该实用程序不适用于具有string索引签名的类型;事实上,每个可能的string都是对象类型的有效键,这一事实往往会“洗掉”来自特定字面量键的任何结果。所以我们必须采取不同的方法。
下面是用于用例的实现:
type FuncKeys<T extends object> =
keyof { [K in keyof T as T[K] extends AnyFunction ? K : never]: any };此使用键重映射用于筛选出不能将属性分配给AnyFunction的密钥。对象类型T的键中的每个键T都被“重新映射”到其自身(如果您在索引成时获得的属性类型) T的键类型为K,即a.k.a。T[K]可以分配给AnyFunction),也可以分配给类型 (否则)。这实际上只保留了您想要的键,并且移除了您不需要的键。请注意,我们提出的映射类型只有any作为其属性类型,但这并不重要,因为我们只是使用类型运算符来获取键。
让我们介绍一下IMyCallbackFunctions是如何工作的。看起来像是
keyof { [K in keyof IMyCallbackFunctions as IMyCallbackFunctions[K] extends AnyFunction ? K : never]: any };因此,K将遍历string、"namespace"、"root"和"otherRoot"。对于string和"namespace",测试IMyCallbackFunctions[K] extends AnyFunction ? K : never将计算为never,因为(DefinedTypeMap | AnyFunction) extends AnyFunction不是真,{func: (msg: string) => void;} extends AnyFunction也不是。所以那些钥匙掉了。对于"root"和"otherRoot",测试将分别评估为"root"和"otherRoot",因为((msg: string) => void) extends AnyFunction是正确的。
这意味着我们现在
keyof { root: any, otherRoot: any }它最终按需要计算为"root" | "otherRoot"。
然后可以使用FuncKeys<T>使on()方法的调用签名只接受值为函数类型的键。
https://stackoverflow.com/questions/71045277
复制相似问题