TypeScript 合并通用数组

2024-02-25

我想要一个对象的通用对象数组。

但是,它生成联合类型。

如何获得一种合并对象类型?喜欢{key: string; boo?: string; foo?: string;}

type PluginType<T extends Record<string, unknown> = {}> = T & {
  key: string;
}

function renderer<T extends Record<string, unknown> = {}>(plugins?: Array<PluginType<T>>) {
  const test = {} as T;

  return () => test;
}

// ---cut---

const plugins1: PluginType<{boo?: string}> = { key: '', boo: '' };
const plugins2: PluginType<{foo?: string}> = { key: '', foo: '' };

const render = renderer([plugins1, plugins2]);
render().boo
render().foo

打字稿游乐场链接 https://www.typescriptlang.org/play?#code/C4TwDgpgBACgNgVwOYEsB2AVcEA8GoQAewEaAJgM5QBKEAxgPYBOZOFwT6SANFAmgGs0DAO5oAfFAC8UAN4BfSTPwAyOQCgoUARBAAuKO05okAbnXz16gGb86wFAzRQmpMhFdM8BYm6q1GFjYOLl5+IVEJaTlFAAowRFQ0CgB+AwBBJiYAQxAceGR0LEg8cXEASg0tRmTgKBJ2aIUobKoMc00XCGAEJmdYyqlJBuBzS3UAegmoAFo5ugRgOZmrGsaEwuSARgMCpOLcWQAjBgY0wxCTRSbtXQMAcnveE4YH+6h5czW6jaSKACZdokitgcLJrKdzkYuNcZLJbvooI9eBDXkj3p9Vk5Gq5yB5orj3J5YgBtX7oChbXjk5L-AC65XMhI8AwAdC91MymGzUUA


如果你有一个价值v类型的V和一个值w类型的W,然后是数组[v, w]是类型Array<V | W>,其中元素类型是union https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#union-types(因为每个元素要么是V or a W);它不是类型Array<V & W>其中元素类型是路口 https://www.typescriptlang.org/docs/handbook/2/objects.html#intersection-types(这意味着每个元素都是both a V and a W,这是不真实的)。所以你不能只从数组中推断出元素类型,然后直接将其用作合并的东西。

相反,您想要做的是解释类型plugins as a 元组类型 https://www.typescriptlang.org/docs/handbook/2/objects.html#tuple-types因此编译器分别知道每个元素的类型,然后使用一些类型魔法通过交集将元组类型的元素组合成单个类型。这是一种方法:

type TupleToIntersection<T extends any[]> = {
  [I in keyof T]: (x: T[I]) => void }[number] extends
  (x: infer I) => void ? I : never;

declare function
  renderer<T extends PluginType<Record<string, unknown>>[]>(
    plugins?: [...T]):
  () => TupleToIntersection<T>;

So renderer() is generic https://www.typescriptlang.org/docs/handbook/2/generics.html in T,类型plugins。注意我已经标记了plugins作为有类型[...T]代替T。那是可变元组类型 https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-0.html#variadic-tuple-types语法,它只是给编译器一个它应该解释的提示plugins作为元组而不是无序数组。

一旦我们有T,我们计算TupleToIntersection<T>是元素类型的交集T。 (因此,如果T is [V, W, X, Y], then TupleToIntersection<T> is V & W & X & Y.) 这是通过使用来完成的分配条件类型 https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#distributive-conditional-types and 条件类型推断 https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#inferring-within-conditional-types。这类似于如何the UnionToIntersection<T>类型作品 https://stackoverflow.com/q/50374908/2887218:我获取元组的每个元素并将其映射到函数参数类型([V, W, X, Y]变成[(x: V)=>void, (x: W)=>void, (x: X)=>void, (y: Y=>void)]),这是一个逆变 https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)#Contravariant_method_parameter_type位置。然后我将其合并到函数的联合中(((x: V) => void)|((x: W) => void)|((x: X) => void)|((x: Y) => void)),并从那里推断出与其匹配的单个函数参数类型。函数并集通过逆变接受参数的交集,因此编译器可以推断(x: infer I) => void (where I在这个例子中会变成V & W & X & Y)。如果这太令人困惑了……呃,这很神奇????‍♂️✨????‍♀️。


无论如何,让我们测试一下:

const plugins1: PluginType<{ boo?: string }> = { key: '', boo: '' };
const plugins2: PluginType<{ foo?: string }> = { key: '', foo: '' };

const render = renderer([plugins1, plugins2]);
/* const render: () => {
    boo?: string | undefined;
} & {
    key: string 
} & {
    foo?: string | undefined;
}  */

render().boo // string | undefined
render().foo // string | undefined

看起来不错!返回值是一个返回两个元素类型的交集的函数,因此render()已知有一个boo and a foo财产,根据需要。

Playground 代码链接 https://www.typescriptlang.org/play?#code/C4TwDgpgBACgNgVwOYEsB2AVcEA8GoQAewEaAJgM5QBKEAxgPYBOZOFwT6SANFAmgGs0DAO5oAfOKgBeKPgBkUAN5QBEEAC4o7TmiQBuKAF8AUCdCQ5CMHAgYGASTQkmFesBQM0eAsVKUoAEM0EABtAF0pWSUTKChQhyh0VXUGADM5cK0ACkItDATwgEoZKQA3BhQyY1C0BABbACMIJnDfEnIKWKhcrXQ0lqgHEulyyuqAfiGoLTQIMpb9MzJ6OECmaDT+Og8vbo3yFpafIg6A+GR0LEgcWkYWNg4uXn4hUQlxCPFs7ribS7QFAmWlCADpwRhihputkRlIMNZbPYnC43DtPN4MOIliZGIDgFB-qhAQBGLQXYnXXAqRoMBjA7RPPTGKLKFKaKAAck5vFpDC03OMSzx7EJiGJFAATOTxVdsDgVGk6QydFwWTI2WoOdzeEr+VzOUKzCKCQcVkwNWajkxsqEiegKCTePbAZLiksAPQAKigJqgVqYOThyl+UD5KqZSCgAB8+Ic0ugIGQlkYoIoYnE4lqtKrmSZU+nQ3qI7oo7H+CsE3Nk-m4l6PWYA7DQXyoB6PYzSzG45XE2QTE2iqC9W2O7myz2IFWk0A

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

TypeScript 合并通用数组 的相关文章

随机推荐

  • 如何在 Objective-C 中测试相等性?

    比较顶视图时 第一个 无法执行 相等测试失败 In init i self setCurrentPuzzleView p1 后来 我 if self currentPuzzleView p1 NSLog Removing P1 from S
  • Google Cloud ML Engine 错误 429 内存不足

    我将模型上传到 ML engine 当尝试进行预测时 我收到以下错误 ERROR gcloud ml engine predict HTTP request failed Response error code 429 message Pr
  • 将数组的内容打印为字符串

    我正在尝试编写一个小函数 它接受整数数据并将其转换为 char 数组内的十六进制值 我想做的是 取int数组数据 将其转换为另一个数组中的十六进制 将十六进制数组的内容转换为字符串 打印字符串的内容 int main int data 40
  • phpmailer如何向两个不同的用户发送两条不同的消息

    我正在向两个不同的人发送邮件 两封不同的消息 一封发送给用户 一封发送给管理员 message1 hello user message2 hello admin email email protected cdn cgi l email p
  • Clojure 测试框架的优势?

    您更喜欢哪一个 为什么 各自的优点和缺点是什么 在什么情况下 每个人都比其他人更胜一筹 我对 midje 与 clojure test 特别感兴趣 但也可以随意提出其他 Clojure 测试框架 也可以看看Clojure 的最佳单元测试框架
  • 在静态方法中在 UIView 上用清晰的颜色绘制(切一个洞)

    我有一个iPhone应用程序 我需要实现以下方法 UITextView textView UITextView withCuttedRect CGRect r 此方法必须剪切 填充 UIColor clearColor 矩形r from U
  • 在同一套接字上并行调用 send/recv 是否有效?

    我们可以在同一个套接字上从一个线程调用 send 并从另一个线程调用 receive 吗 我们可以从同一套接字上的不同线程并行调用多个发送吗 我知道好的设计应该避免这种情况 但我不清楚这些系统 API 的行为方式 我也找不到同样的好的文档
  • 表单不提交动态生成的输入(jQuery)

    您好 我正在尝试为我的表单动态生成一些输入 但它没有发布生成的新输入 到目前为止我一直在四处搜索 我唯一能找到的就是使表单成为body 标签 并且在我的应用程序的设计中这是不可能的 所以有人可能知道会发生什么以及如何解决它 不 它不适用于
  • 如何在 Visual Studio Code 中显示 Jupyter Notebook 中的所有输出?

    在 VS Code 中的 Jupyter Notebook 中 当我运行在某个时刻打印大量输出的代码时 剩余的输出将被抑制并显示一条消息 显示更多 在文本编辑器中打开原始输出数据 如何使所有输出可见 我认为你在这里使用内部构建是正确的设置
  • Flink:处理数据早于应用程序水印的键控流

    我正在使用带有运动源和事件时间键控窗口的 F link 该应用程序将监听实时数据流 窗口 事件时间窗口 并处理每个键控流 我有另一个用例 我还需要能够支持某些关键流的旧数据的回填 这些将是事件时间 鉴于我正在使用水印 这会成为一个问题 因为
  • Azure Functions - Blob 流动态输入绑定

    我正在 azure 上运行一个 C 函数 它需要从容器中获取文件 唯一的问题是输入文件的路径每次都会 可能 不同 并且输入文件的数量将从 1 到大约 4 或 5 不等 因此 我不能只使用默认的输入 blob 绑定据我所知 我的选择是给予容器
  • 通过Ajax发送FormData + js变量

    我有这个 Ajax 来发送多个图像 btn on click function var formData new FormData form1 0 var path php upload adm prodpictures php ajax
  • 如何转到 Eclipse 中的“下一个出现”

    在 Eclipse 中 您可以打开 标记出现次数 然后它将突出显示当前范围内给定变量的每次出现 那太好了 但我真的很想从一次这样的事件转移到下一次 到目前为止 我已经找到了两个关于如何执行此操作的建议 但都没有奏效 首先有这样的帖子 Ecl
  • 使用 configparser 添加注释

    我可以使用 python 中的 ConfigParser 模块使用 add section 和 set 方法创建 ini 文件 请参阅示例http docs python org library configparser html http
  • 调试 $rootScope:infdig

    这是一个常见问题 已达到 5 次 digest 迭代 流产 观察者在最近 5 次迭代中被解雇 文档 https docs angularjs org error https docs angularjs org error rootScop
  • 自动更新 HTML 画布宽度和高度属性

    我想将 HTML5 画布与 Flexbox 结合使用 当用户调整窗口大小时 我需要自动设置 canvas width 和 canvas height 我尝试使用 jQuery 来实现 cnvs attr width cnvs width c
  • “Google_Exception”,消息为“经过身份验证后无法添加服务”

    我正在使用 Oauth 2 0 开发带有 Google Analytics 的 WP 插件 我所有的身份验证和数据提取都工作正常 除了这个问题 第一次获得新的 Google 授权代码 例如 4 xbSbg 并进行身份验证 然后尝试调用new
  • 我可以在 AngularJS 中要求通用父指令吗

    子指令是否可以在不确切知道父指令是什么的情况下需要父指令 而只是知道它 实现了一个接口 例如
  • 如何多次重用相同的代码 - html/css

    我想为每个按钮重复使用模式两次 按钮代码 function popUp model const pop up model document getElementById model pop up model classList toggle
  • TypeScript 合并通用数组

    我想要一个对象的通用对象数组 但是 它生成联合类型 如何获得一种合并对象类型 喜欢 key string boo string foo string type PluginType