【问题标题】:Conditional Types for Typescript Request/ResponseTypescript 请求/响应的条件类型
【发布时间】:2018-02-07 08:33:41
【问题描述】:

我目前正在使用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. DeepPick 定义中使用T[K] 时出现type K cannot be used to index type T 错误。

我觉得考虑到DeepSelect 的定义,其中所有键都来自通用T 中的键,T[K] 在这里完全有效,因为S 中的任何键也将是@987654337 中的键@。

2。在DeepPick 定义中最后一次使用S[K] 错误说type boolean | DeepSelect&lt;T[K]&gt; is not assignable to type DeepSelect&lt;T[K]&gt;

在这里,我觉得因为这部分类型条件只有在S[K]没有扩展布尔值时才会被命中,那么它应该能够推断出S[K]不是boolean | DeepSelect&lt;T[K]&gt;而是只是@ 987654344@

我意识到,由于这个 PR 是昨天才合并的,所以没有多少人会对这些问题有深入的了解,但如果有人能帮助我了解如何正确构建这些类型,我将非常感激。

更新 1

好的,我想我已经使用同样新的 Type Inference 解决了问题 #2。我已将 DeepPick 类型的定义从:

type DeepPick<T, S extends DeepSelect<T>> = {[K in keyof S]: S[K] extends boolean ? T[K] : DeepPick<T[K], S[K]>}

到:

type DeepPick<T, S extends DeepSelect<T>> = {[K in keyof S]: S[K] extends DeepSelect<infer U> ? DeepPick<T[K], U> : T[K]}

【问题讨论】:

    标签: typescript typescript-typings


    【解决方案1】:

    这确实是最前沿的东西,所以我不知道我给出的建议是否会在下一个版本中相关,更不用说好的 .也就是说,我有这样的东西:

    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 表示应该包含该属性(是吗?)。

    如果属性是object,我还更改了DeepSelect 以递归向下,因此如果User 具有一些非string 属性(例如age: number?),它应该继续工作。这使它更通用一些。

    最后,我删除了对DeepPick 的约束,即S 需要扩展DeepSelect&lt;T&gt;,而不是映射到keyof S,而是映射到keyof S &amp; 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
    

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

    祝你好运!

    【讨论】:

    • 谢谢!我确实喜欢从booleantrue 的更改,但是我确实注意到,如果您将其键入为true,那么您无法在将select 对象传递给loadUser 之前构建它,因为它会解释对象中的trues 类型为boolean 而不是true,所以我现在可能会坚持使用boolean。但这一切都很好。再次感谢。
    猜你喜欢
    • 2021-08-17
    • 1970-01-01
    • 2017-09-30
    • 1970-01-01
    • 2020-12-24
    • 2020-10-01
    • 1970-01-01
    • 2021-02-21
    • 2020-03-29
    相关资源
    最近更新 更多