typescript - 如何使用对象接口的键路径数组的联合类型作为接口中这些键路径的联合类型?
问题描述
我为对象接口中的键路径类型创建了一个泛型类型:TypeOfPath<T, P>
. 路径是对象接口键的数组以及任何嵌套的对象键路径,迭代。
我还创建了一些辅助类型来将数组作为类型处理。
Head<T>
定义数组的第一个元素(可以是undefined
),或者never
如果参数不是数组。
Tail<T>
定义数组的其余元素(可以为空),或者never
如果参数不是数组。我有点惊讶的类似列表的标准东西不是语言的一部分。
type Head<T> = T extends unknown[] ? T[0] : never;
type Tail<T> = T extends unknown[]
? T extends [unknown, ...infer R]
? R
: []
: never;
type TypeOfPath<T, P> = T extends object
? Head<P> extends keyof T
? Tail<P> extends []
? T[Head<P>]
: TypeOfPath<T[Head<P>], Tail<P>>
: never
: never;
乍一看,所有这些类型似乎都按预期工作。与interface I { foo: { bar: boolean }; baz?: number }
:
Head<[1, 2, 3]>
和Head<[1]>
的类型1
,Head<[]>
isundefined
,Head<1>
isnever
Tail<[1, 2, 3]>
is[2, 3]
和are的类型neverTail<[1]>
_Tail<[]>
[]
Tail<1>
- 的类型
TypeOfPath<I, ['foo']>
是{ bar: boolean }
,TypeOfPath<I, ['foo', 'bar']>
是boolean
,TypeOfPath<I, ['baz']>
是number | undefined
但是,一旦我尝试使用TypeOfPath<I, ['foo'] | ['foo', 'bar'] | ['baz']>
,类型最终是never
. 我希望类型是{ bar: boolean } | boolean | number | undefined
.
似乎P
参数的联合TypeOfPath<T, P>
总是never
。我认为联合泛型是分布式的。我在这里遗漏了一些明显的(或不那么明显的)吗?
解决方案
你得到never
是因为你的联合包含不同长度的键数组。您的每个泛型都将其参数作为联合接收,因此当我们TypeOfPath<I, ['foo'] | ['foo', 'bar'] | ['baz']>
逐步评估时会出现以下情况:
// Set up the arguments to TypeOfPath.
type T = I;
type P = ['foo'] | ['foo', 'bar'] | ['baz'];
// Evaluate the first ternary condition.
type condition1 = T extends object ? true : false; // true
// So take the true branch and evaluate the next condition.
type condition2 = Head<P> extends keyof T ? true : false; // true
// So take the true branch and evaluate the next condition.
type condition3 = Tail<P> extends [] ? true : false; // false
// So take the false branch, calling TypeOfPath recursively.
// Set up the arguments to the new TypeOfPath call.
type Tbis = T[Head<P>]; // number | { bar: boolean } | undefined
type Pbis = Tail<P>; // [] | ['bar']
// Now back to the first ternary condition.
type condition1bis = Tbis extends object ? true : false; // false
// False, because not every branch is an object: two of them have already resolved.
// That leaves us with the false branch, which is never.
TypeOfPath
最简单的解决方法是在调用之外放置一个不同长度数组的联合;这将起作用:
TypeOfPath<I, ['foo'] | ['baz']> | TypeOfPath<I, ['foo', 'bar']>
如果您希望能够TypeOfPath
按照您的意图传递不同长度数组的联合,我认为解决方案将涉及将联合的对象Exclude
和Extract
非对象成员分开以进行单独处理的实用程序类型的组合,但是实施的总体复杂性会更大。
推荐阅读
- android - 通过 Android 从 Debian 发送 SMS
- microsoft-teams - 自定义 Microsoft Teams 连接器根本不与外部服务通信
- angular - ngbTypeAhead 从不显示建议
- android - 屏幕锁定时未调用 onLoadFinished
- javascript - 查询Javascript执行顺序
- python - 用于同时观看文件夹的多线程
- javascript - 解析日期时间的有效方法
- jquery - 即使响应包含 ajax 请求的“Set-Cookie”标头,cookie 也不会存储在浏览器中
- java - 用杰克逊重命名对象中的字段
- assembly - x86 ax 寄存器得到不同的值?