首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从T型值的类型/接口中拔出键

从T型值的类型/接口中拔出键
EN

Stack Overflow用户
提问于 2022-02-09 07:07:23
回答 1查看 245关注 0票数 1

我正在开发一个新的库,它将包含一组回调函数。我想实现一种“命名空间”特性,这样接口和回调链就不会变得如此强大。

具有回调类型的基本回调/命名空间如下所示:

代码语言:javascript
复制
type AnyFunction = (...args: any) => void;

type DefinedTypeMap = {
  [key: string]: AnyFunction | DefinedTypeMap;
};

然后扩展的接口可以如下所示:

代码语言:javascript
复制
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

当前函数(它不支持正在进行的命名空间特性)如下所示:

代码语言:javascript
复制
on<E extends keyof P2PConnectionEventMap<T>, F extends P2PConnectionEventMap<T>[E]>(event: E, callback: F) => void

并且应该修改为只允许event param接受接口上的值属于回调类型的键,而不是对象类型。

提前谢谢你的帮助,这让我很想把头发拔出来。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-02-10 15:43:35

让我们定义一个名为FuncKeys<T>的类型实用程序,它接受对象类型T,并返回键的友联市,其中每个键的属性类型都可分配给AnyFunction。目标是FuncKeys<IMyCallbackFunctions>应该计算为"root" | "otherRoot",并且不应该包含"namespace" (这是一个非函数对象)或string (来自扩展DefinedTypeMap接口上的索引签名 )。

让人痛苦的是,您的IMyCallbackFunctions接口相当于:

代码语言:javascript
复制
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都是对象类型的有效键,这一事实往往会“洗掉”来自特定字面量键的任何结果。所以我们必须采取不同的方法。

下面是用于用例的实现:

代码语言:javascript
复制
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是如何工作的。看起来像是

代码语言:javascript
复制
  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是正确的。

这意味着我们现在

代码语言:javascript
复制
  keyof { root: any, otherRoot: any }

它最终按需要计算为"root" | "otherRoot"

然后可以使用FuncKeys<T>使on()方法的调用签名只接受值为函数类型的键。

操场链接到代码

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71045277

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档