- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我想声明一个函数,它可以接受一个对象和一个嵌套属性键数组,并将嵌套值的类型派生为函数的返回类型。
例如
const value = byPath({ state: State, path: ['one', 'two', 'three'] });
// return type == State['one']['two']['three']
const value2 = byPath({ state: State, path: ['one', 'two'] });
// return type == State['one']['two']
我能够组合的最好的是以下内容,但它比我希望的更冗长,而且我必须为每一层嵌套添加一个函数重载。
export function byPath<
K1 extends string,
R
>({ state, path }: {
state: {[P1 in K1]?: R},
path: [K1]
}): R;
export function byPath<
K1 extends string,
K2 extends string,
R
>({ state, path }: {
state: {[P1 in K1]?: {[P2 in K2]?: R}},
path: [K1, K2]
}): R;
export function byPath<
K1 extends string,
K2 extends string,
K3 extends string,
R
>({ state, path }: {
state: {[P1 in K1]?: {[P2 in K2]?: {[P3 in K3]?: R}}},
path: [K1, K2, K3]
}): R;
export function byPath<R>({ state, path }: { state: State, path: string[] }): R | undefined {
// do the actual nested property retrieval
}
有没有更简单/更好的方法来做到这一点?
最佳答案
不幸的是,TypeScript 目前不允许任意 recursive type functions ,这就是您想要遍历键列表,深入到对象类型,并得出与键列表对应的嵌套属性的类型。你可以做它的一部分,但它是一团糟。
所以你将不得不选择一些最高级别的嵌套并为此编写。这是不使用重载的函数的可能类型签名:
type IfKey<T, K> = [K] extends [keyof T] ? T[K] : T;
declare function byPath<T0,
K1 extends keyof T0 | undefined, T1 extends IfKey<T0, K1>,
K2 extends keyof T1 | undefined, T2 extends IfKey<T1, K2>,
K3 extends keyof T2 | undefined, T3 extends IfKey<T2, K3>,
K4 extends keyof T3 | undefined, T4 extends IfKey<T3, K4>,
K5 extends keyof T4 | undefined, T5 extends IfKey<T4, K5>,
K6 extends keyof T5 | undefined, T6 extends IfKey<T5, K6>
>({ state, path }: { state: T0, path: [K1?, K2?, K3?, K4?, K5?, K6?] }): T6;
请注意,如果需要,您可以轻松地将其扩展到六层以上的嵌套。
它的工作方式:有两种类型参数...键类型(名为 K1
、K2
等)和对象类型(名为 T0
、T1
等)。 state
属性的类型为 T0
,路径为 tuple with optional elements的关键类型。每个键类型要么是前一个对象类型的键,要么是未定义
。如果键未定义,则下一个对象类型与当前对象类型相同;否则就是相关属性的类型。因此,一旦键类型变为并保持 undefined
,对象类型将变为并保持最后一个相关属性类型......最后一个对象类型(上面的 T6
)是函数的返回类型。
举个例子:如果T0
是{a: {b: string}, c: {d: string}}
,那么K1
必须是 'a'
、'd'
或 undefined
之一。假设 K1
是 'a'
。那么T1
就是{b:string}
。现在 K2
必须是 'b'
或 undefined
。假设 K2
是 'b'
。那么T2
就是string
。现在 K3
必须在 keyof string
或 undefined
中。 (因此 K3
可以是 "charAt"
,或任何 string
方法和属性)。假设 K3
是 undefined
。然后 T3
是 string
(因为它与 T2
相同)。如果 K4
、K5
和 K6
的所有其余部分都是 undefined
,则 T4
、T5
和T6
只是string
。函数返回 T6
。
因此,如果您执行此调用:
const ret = byPath({state: {a: {b: "hey"}, c: {d: "you"} }, path: ['a', 'b'] });
那么T0
会被推断为{a: {b: string}, c: {d: string}
, K1
会是'a'
,K2
将是 'b'
,K3
到 K6
都将是 undefined
。这是上面的例子,所以 T6
将是 string
。因此 ret
将是 string
类型。
如果你输入了错误的 key ,上面的函数签名也会对你大喊大叫:
const whoops = byPath({ state: { a: { b: "hey" }, c: { d: "you" } }, path: ['a', 'B'] });
// error! type "B" is not assignable to "b" | undefined: ----------------------> ~~~
该错误是有道理的,因为 B
无效。下面也骂你:
const alsoWhoops = byPath({ state: { a: { b: "hey" }, c: { d: "you" } }, path: ['A', 'b'] });
// error! type "A" is not assignable to "a" | "c" | undefined: ---------------> ~~~
// also error! Type "b" is not assignable to "a" | "c" | undefined ?! -------------> ~~~
第一个错误正是您所期望的;第二个有点奇怪,因为 "b"
没问题。但是编译器现在不知道 keyof T['A']
会发生什么,所以它的行为就好像 K1
是 undefined
一样。如果您修复了第一个错误,第二个错误就会消失。可能有一些方法可以改变 byPath()
签名来避免这种情况,但这对我来说似乎微不足道。
无论如何,希望对您有所帮助或给您一些想法。祝你好运!
编辑:如果您关心错误的第二条错误消息,您可以使用稍微复杂一点的:
type IfKey<T, K> = [K] extends [keyof T] ? T[K] : T
type NextKey<T, K = keyof any> = [K] extends [undefined] ? undefined :
[keyof T | undefined] extends [K] ? keyof any : (keyof T | undefined)
declare function byPath<T0,
K1 extends NextKey<T0>, T1 extends IfKey<T0, K1>,
K2 extends NextKey<T1, K1>, T2 extends IfKey<T1, K2>,
K3 extends NextKey<T2, K2>, T3 extends IfKey<T2, K3>,
K4 extends NextKey<T3, K3>, T4 extends IfKey<T3, K4>,
K5 extends NextKey<T4, K4>, T5 extends IfKey<T4, K5>,
K6 extends NextKey<T5, K5>, T6 extends IfKey<T5, K6>
>({ state, path }: { state: T0, path: [K1?, K2?, K3?, K4?, K5?, K6?] }): T6;
这几乎是一样的,除了当键与它们应该匹配的不匹配时出现问题。
关于TypeScript:使用数组获取深度嵌套的属性值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53293200/
我需要您在以下方面提供帮助。近一个月来,我一直在阅读有关任务和异步的内容。 我想尝试在一个简单的 wep api 项目中实现我新获得的知识。我有以下方法,并且它们都按预期工作: public Htt
我的可执行 jar 中有一个模板文件 (.xls)。不需要在运行时我需要为这个文件创建 100 多个副本(稍后将唯一地附加)。用于获取 jar 文件中的资源 (template.xls)。我正在使用
我在查看网站的模型代码时对原型(prototype)有疑问。我知道这对 Javascript 中的继承很有用。 在这个例子中... define([], function () { "use
影响我性能的前三项操作是: 获取滚动条 获取偏移高度 Ext.getStyle 为了解释我的应用程序中发生了什么:我有一个网格,其中有一列在每个单元格中呈现网格。当我几乎对网格的内容做任何事情时,它运
我正在使用以下函数来获取 URL 参数。 function gup(name, url) { name = name.replace(/[\[]/, '\\\[').replace(/[\]]/,
我最近一直在使用 sysctl 来做很多事情,现在我使用 HW_MACHINE_ARCH 变量。我正在使用以下代码。请注意,当我尝试获取其他变量 HW_MACHINE 时,此代码可以完美运行。我还认为
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 关闭 9 年前。 要求提供代码的问题必须表现出对所解决问题的最低限度的理解。包括尝试过的解决方案、为什么
由于使用 main-bower-files 作为使用 Gulp 的编译任务的一部分,我无法使用 node_modules 中的 webpack 来require 模块code> dir 因为我会弄乱当
关闭。这个问题需要更多focused .它目前不接受答案。 想改进这个问题吗? 更新问题,使其只关注一个问题 editing this post . 关闭 5 年前。 Improve this qu
我使用 Gridlayout 在一行中放置 4 个元素。首先,我有一个 JPanel,一切正常。对于行数变大并且我必须能够向下滚动的情况,我对其进行了一些更改。现在我的 JPanel 上添加了一个 J
由于以下原因,我想将 VolumeId 的值保存在变量中: #!/usr/bin/env python import boto3 import json import argparse import
我正在将 MSAL 版本 1.x 更新为 MSAL-browser 的 Angular 。所以我正在尝试从版本 1.x 迁移到 2.X.I 能够成功替换代码并且工作正常。但是我遇到了 acquireT
我知道有很多关于此的问题,例如 Getting daily averages with pandas和 How get monthly mean in pandas using groupby但我遇到
This is the query string that I am receiving in URL. Output url: /demo/analysis/test?startDate=Sat+
我正在尝试使用 javascript 中的以下代码访问 Geoserver 层 var gkvrtWmsSource =new ol.source.ImageWMS({ u
API 需要一个包含授权代码的 header 。这就是我到目前为止所拥有的: var fullUrl = 'https://api.ecobee.com/1/thermostat?json=\{"s
如何获取文件中的最后一个字符,如果是某个字符,则删除它而不将整个文件加载到内存中? 这就是我目前所拥有的。 using (var fileStream = new FileStream("file.t
我是这个社区的新手,想出了我的第一个问题。 我正在使用 JSP,我成功地创建了 JSP-Sites,它正在使用jsp:setParameter 和 jsp:getParameter 具有单个字符串。
在回答 StoreStore reordering happens when compiling C++ for x86 @Peter Cordes 写过 For Acquire/Release se
我有一个函数,我们将其命名为 X1,它返回变量 Y。该函数在操作 .on("focusout", X1) 中使用。如何获取变量Y?执行.on后X1的结果? 最佳答案 您可以更改 Y 的范围以使其位于函
我是一名优秀的程序员,十分优秀!