TypeScript:使用数组获取深度嵌套的属性值

宾格斯

我想声明一个函数,该函数可以接受一个对象以及一个嵌套的属性键数组,并将嵌套值的类型派生为该函数的返回类型。

例如

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当前不允许使用任意递归类型函数,这是您要遍历键列表,向下钻取到对象类型并给出与键列表相对应的嵌套属性的类型的功能。您可以做一些事情,但是很混乱。

因此,您将必须选择一些最大级别的嵌套并为此编写代码。这是不使用重载的函数的可能的类型签名:

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;

请注意,您可以根据需要轻松地将其扩展到六层以上的嵌套。

事情是这样工作的:有两种类型参数...主要类型(命名K1K2等等),和对象类型(命名T0T1等)。state属性的类型为T0,路径为一个包含键类型的可选元素元组每个键类型要么是先前对象类型的键,要么是undefined如果键未定义,则下一个对象类型与当前对象类型相同;否则,返回下一个对象类型。否则就是相关属性的类型。因此,只要键类型变为并保持undefined,对象类型即变为并保持最后一个相关的属性类型...,最后一个对象类型(T6上面)是函数的返回类型。

让我们做一个例子:如果T0{a: {b: string}, c: {d: string}},则K1必须是一个'a''d'undefined可以说K1'a'然后T1{b: string}现在K2必须是'b'undefined可以说K2'b'然后T2string现在K3必须位于keyof string或中undefined(因此K3可能是"charAt"也可能是任何string方法和属性)。可以说K3undefined然后T3string(因为它与相同T2)。如果所有的休息K4K5K6undefined的话T4T5T6只是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这是上面的例子,所以T6string从而ret成为类型string

如果您输入了错误的密钥,上述功能签名也会对您大喊:

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'],因此它的作用就像K1undefined如果您解决了第一个错误,第二个错误将消失。可能有一些方法可以更改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;

这几乎是相同的,除了当因键不匹配而发生错误时。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

获取财产的数组,属性(嵌套属性)的使用流的Java 8

从对象数组获取属性的值

通过ID的数组路径获取深度嵌套的对象

GO-从嵌套的JSON对象获取属性值的数组

使用jQuery从对象数组中获取属性值数组

如何从JSON字符串获取深度嵌套的属性?

使用Lodash深度嵌套数组对象值

在JS中使用深度嵌套的数组过滤数组

在JavaScript中的对象数组中搜索深度嵌套的值

根据其值过滤JavaScript深度嵌套的对象数组

使用key的值获取对JSON / JS对象内任意(深度)嵌套节点的引用

如何使用Javascript从值数组中获取深度嵌套的对象结构

使用C#访问深度嵌套的json中的特定属性并将其添加到数组中

TypeScript:基于tupel获取嵌套属性的类型

使用变量获取数组属性值

ElasticSearch获取嵌套属性的值

无法获取深度嵌套的哈希值

从多维数组嵌套属性值中获取数组

按属性键对深度嵌套的值进行分组?

Redux 更改深度嵌套数组的值

如何使用 TypeScript 和 Jest 模拟深度嵌套的函数

Javascript/Typescript:如何在深度嵌套的 javascript 数组中设置一个已知属性

尝试使用reduce将数组内对象的嵌套属性合并为包含所有嵌套属性的单个对象,仅获取单个值

Javascript 使用对象和重命名属性压平深度嵌套的数组

根据深度嵌套数组PHP的值过滤多维数组

通过嵌套属性的值定义 TypeScript 类型

MongoDB更新深度嵌套数组的值并排序

在深度嵌套的对象数组中查找最大 id 值

在 MongoDB 中获取深度嵌套项的平面数组