【问题标题】:TypeScript: deep partial?TypeScript:深度偏?
【发布时间】:2022-01-13 05:11:34
【问题描述】:

有没有一种方法可以在 TypeScript 中指定一个部分类型,它也使所有子对象也成为部分对象?例如:

interface Foobar {
  foo: number;
  bar: {
    baz: boolean;
    qux: string;
  };
}

const foobar: Partial<Foobar> = {
  foo: 1,
  bar: { baz: true }
};

这会引发以下错误:

TS2741: Property 'qux' is missing in type '{ baz: true; }' but required in type '{ baz: boolean; qux: string; }'.

有没有办法让子节点也成为局部节点?

【问题讨论】:

    标签: typescript


    【解决方案1】:

    您可以简单地创建一个新类型,例如 DeepPartial,它基本上引用自身(2022 年 1 月更新以处理可能的非对象):

    type DeepPartial<T> = T extends object ? {
        [P in keyof T]?: DeepPartial<T[P]>;
    } : T;
    

    然后,你可以这样使用它:

    const foobar: DeepPartial<Foobar> = {
      foo: 1,
      bar: { baz: true }
    };
    

    See proof-of-concept example on TypeScript Playground.

    【讨论】:

    • 请注意,此线程中的某些人遇到了递归类型的性能问题:github.com/microsoft/TypeScript/issues/35729
    • 我的意思是编译速度慢的性能问题。但是,自从写了那条评论后,我看到了对 TS 的很多改进。我目前使用 DeepPartial 在没有问题的代码库中工作。
    • 为了修复 DeepPartial 奇怪的类型 - 使用这个:type DeepPartial&lt;T&gt; = T extends object ? { [P in keyof T]?: DeepPartial&lt;T[P]&gt;; } : T;
    • @Shl 不错。在最近的 TS 升级后,到处都出现了问题,这解决了它;这个答案应该更新
    • @Shi 我想知道所有这些奇怪的错误(类型不能分配给其他类型)是从哪里来的。非常感谢。
    【解决方案2】:

    如果您正在寻找快速简便的解决方案,请查看 type-fest 包,其中包含许多有用的预构建 TypeScript 类型,包括 PartialDeep 类型。

    如需更多技术性和可定制的解决方案,请参阅this answer

    【讨论】:

    • type-fest 似乎是一个更受欢迎的选项:npmtrends.com/type-fest-vs-utility-types
    • 谢谢!当使用 jsdoc 和 vscode intellisense 时,这甚至适用于普通的 JS 项目。
    • 谢谢。查看type-fest 的源代码帮助我弄清楚当传递的类型包含any 时如何使我的DeepPartial 实现工作。
    • 我不认为只为一种类型安装整个包是一个好主意。这种选择是导致 javascript/ts 开发人员大量冗余 node_modules 的原因。
    【解决方案3】:

    我在这个问题的答案上启发了自己来创建我自己的 PartialDeep 版本。

    在此过程中,我偶然发现了一些内置对象的问题;对于我的用例,我不希望 Date 对象缺少它的某些方法。 它要么存在,要么不存在。

    这是我的版本:

    // Primitive types (+ Date) are themselves. Or maybe undefined.
    type PartialDeep<T> = T extends string | number | bigint | boolean | null | undefined | symbol | Date
      ? T | undefined
      // Arrays, Sets and Maps and their readonly counterparts have their items made
      // deeply partial, but their own instances are left untouched
      : T extends Array<infer ArrayType>
      ? Array<PartialDeep<ArrayType>>
      : T extends ReadonlyArray<infer ArrayType>
      ? ReadonlyArray<ArrayType>
      : T extends Set<infer SetType>
      ? Set<PartialDeep<SetType>>
      : T extends ReadonlySet<infer SetType>
      ? ReadonlySet<SetType>
      : T extends Map<infer KeyType, infer ValueType>
      ? Map<PartialDeep<KeyType>, PartialDeep<ValueType>>
      : T extends ReadonlyMap<infer KeyType, infer ValueType>
      ? ReadonlyMap<PartialDeep<KeyType>, PartialDeep<ValueType>>
      // ...and finally, all other objects.
      : {
          [K in keyof T]?: PartialDeep<T[K]>;
        };
    

    【讨论】:

      猜你喜欢
      • 2018-01-01
      • 2020-07-28
      • 2016-05-31
      • 2019-02-27
      • 1970-01-01
      • 2020-05-26
      • 1970-01-01
      • 1970-01-01
      • 2019-07-23
      相关资源
      最近更新 更多