我目前正在玩typescript@next
现已合并Conditional Types PR。
我正在尝试做的是创建一种方法,该方法对数据模型类型进行深度记录,并根据记录中的字段返回一种深度选择。例如,我希望能够做到以下几点:
loadUser({
id: true,
name: {
first: true
}
});
当User
类型看起来像
type User = {
name: {
first: string;
last: string;
};
id: string;
}
并且在这种情况下loadUser
的返回值将匹配
{
id: string;
name: {
first: string;
}
}
到目前为止我所做的是以下内容:
type DeepSelect<T> = {[K in keyof T]?: T[K] extends string ? boolean : DeepSelect<T[K]>};
type DeepPick<T, S extends DeepSelect<T>> = {[K in keyof S]: S[K] extends DeepSelect<infer U> ? DeepPick<T[K], U> : T[K]}
function loadUser<S extends DeepSelect<User>>(select: S): DeepPick<User, S> {
...
}
问题是双重的:
T[K]
DeepPick
定义错误中使用type K cannot be used to index type T
DeepSelect
。 我认为,鉴于T
的定义,其中所有键都来自其通用T[K]
中的键,S
在此完全有效,因为T
中的任何键都是也将成为S[K]
中的密钥。
<击> 2。最后一次使用DeepPick
定义错误type boolean | DeepSelect<T[K]> is not assignable to type DeepSelect<T[K]>
在这里,我觉得因为如果 S[K]
没有扩展布尔值,那么类型条件的这一部分才会被击中,那么它应该能够推断出{{1} }不是S[K]
,而只是boolean | DeepSelect<T[K]>
我意识到,由于这个PR只是昨天合并,没有多少人会对这些问题有很深入的了解,但如果有人能帮助我理解如何正确构建这些类型,我将非常感激。
好吧,我想我已经使用了新的Type Inference来解决问题#2。我已经改变了DeepSelect<T[K]>
类型的定义:
DeepPick
为:
type DeepPick<T, S extends DeepSelect<T>> = {[K in keyof S]: S[K] extends boolean ? T[K] : DeepPick<T[K], S[K]>}
答案 0 :(得分:3)
这确实是最前沿的东西,所以我不知道我给出的建议在下一个版本中是否与相关,更不用说 good了。那就是说,我有这样的事情:
type DeepSelect<T> = {
[K in keyof T]? : T[K] extends object ? DeepSelect<T[K]> : true
};
type DeepPick<T, S> = {
[K in keyof S & keyof T]: S[K] extends true ? T[K] : DeepPick<T[K], S[K]>
}
我已将两者都更改为仅处理true
而不是boolean
,因为您似乎并不打算false
表示该属性应该包括在内(是吗?)。
如果属性为DeepSelect
,我还会更改object
以便递归,因此如果User
有一些非string
,它应继续有效属性(例如,age: number
?)。这使它更加通用。
最后,我删除了DeepPick
S
需要扩展DeepSelect<T>
的约束,而不是映射keyof S
我映射超过keyof S & keyof T
。删除约束可确保递归起作用,并且S
和T
的键的交集可确保编译器识别T[K]
和S[K]
都存在。编译器可能在一个完美的世界中意识到你编写它的方式是有效的,但我猜这个世界并不完美。
请注意,函数loadUser
仍然具有S
:
declare function loadUser<S extends DeepSelect<User>>(
select: S
): DeepPick<User, S>;
所以它会按预期工作:
const u = loadUser({
id: true,
name: {
first: true
}
});
u.id
u.name.first
请注意,在您和我的代码中,传递给loadUser()
的对象似乎不是excess property checks。例如:
const u = loadUser({
ib: true, // no error, oops
name: {
first: true
}
});
u.id // error
u.name.first
我不知道为什么会这样,或者它是一个错误,一个限制,或者是什么。但是你应该记住它,也许吧。或者不是,如果功能在发布之前发生变化。谁知道!
祝你好运!