打字稿请求/响应的条件类型

时间:2018-02-06 21:37:50

标签: typescript typescript-typings

我目前正在玩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> {
    ...
}

问题是双重的:

  1. T[K] DeepPick定义错误中使用type K cannot be used to index type T DeepSelect
  2. 我认为,鉴于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只是昨天合并,没有多少人会对这些问题有很深入的了解,但如果有人能帮助我理解如何正确构建这些类型,我将非常感激。

    更新1

    好吧,我想我已经使用了新的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]>}
    

1 个答案:

答案 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。删除约束可确保递归起作用,并且ST的键的交集可确保编译器识别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

我不知道为什么会这样,或者它是一个错误,一个限制,或者是什么。但是你应该记住它,也许吧。或者不是,如果功能在发布之前发生变化。谁知道!

祝你好运!