TypeScript:深偏?

Ben*_*ick 45 typescript

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

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

const foobar: Partial<Foobar> = {
  foo: 1,
  bar: { baz: true }
};
Run Code Online (Sandbox Code Playgroud)

这会引发以下错误:

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

有没有办法使子节点也部分化?

Ter*_*rry 79

您可以简单地创建一个新类型,例如,DeepPartial,它基本上引用自身:

type DeepPartial<T> = {
    [P in keyof T]?: DeepPartial<T[P]>;
};
Run Code Online (Sandbox Code Playgroud)

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

const foobar: DeepPartial<Foobar> = {
  foo: 1,
  bar: { baz: true }
};
Run Code Online (Sandbox Code Playgroud)

请参阅 TypeScript Playground 上的概念验证示例

  • 为了修复 DeepPartial&lt;native&gt; 奇怪的类型 - 使用这个:`type DeepPartial&lt;T&gt; = T extends object ? { [P in keyof T]?: DeepPartial&lt;T[P]&gt;; } : T;` (6认同)
  • 请注意,该线程中的一些人遇到了递归类型的性能问题:https://github.com/microsoft/TypeScript/issues/35729 (5认同)
  • 我的意思是编译速度慢时的性能问题。然而,自从写下那条评论以来,我看到了 TS 的很多改进。我目前在使用 DeepPartial 的代码库中工作,我们没有遇到任何问题。 (5认同)
  • 也就是说,这是一个数组友好的版本:`type DeepPartial&lt;T&gt; = T extends any[] ?T : { [P in keyof T]?: DeepPartial&lt;T[P]&gt; }` 我会更新答案。 (3认同)

Ben*_*ick 18

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

有关更具技术性和可定制性的解决方案,请参阅此答案

  • 我认为只为单一类型安装整个包不是一个好主意。这种选择导致 javascript/ts 开发人员产生大量冗余的 node_modules。 (5认同)
  • `type-fest` 似乎是一个更受欢迎的选项:https://www.npmtrends.com/type-fest-vs-utility-types (2认同)

gus*_*nke 13

我从这个问题的答案中得到启发,创建了我自己的 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]>;
    };
Run Code Online (Sandbox Code Playgroud)

  • 谢谢!这种方法对我有用(较短的版本),而“T extends object”在可选嵌套字段方面存在问题。 (3认同)

ash*_*sut 10

只需创建一个新类型DeepPartial

DeepPartial当其属性的object类型适用DeepPartial于该属性的属性时,基本上会引用自身,依此类推

type DeepPartial<T> = {
  [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K]
}
Run Code Online (Sandbox Code Playgroud)

用法

interface Foobar {
  foo: number;
  bar: {
    foo1: boolean;
    bar1: string;
  };
}

const foobar: DeepPartial<Foobar> = {
  foo: 1,
  bar: { foo1: true }
};
Run Code Online (Sandbox Code Playgroud)

TS 游乐场示例


use*_*949 5

我必须使用这个版本来防止数组具有未定义的元素:

type DeepPartial<T> = T extends any[]? T : T extends Record<string, any> ? {
  [P in keyof T]?: DeepPartial<T[P]>;
} : T;
Run Code Online (Sandbox Code Playgroud)