TypeScript:深度部分?

3yhwsihp  于 2022-12-19  发布在  TypeScript
关注(0)|答案(6)|浏览(184)

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

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

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

这将引发以下错误:
第一个月
有什么方法可以使子节点也成为局部的吗?

zyfwsgd6

zyfwsgd61#

您可以简单地创建一个新的类型,比如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 }
};

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

bqf10yzr

bqf10yzr2#

如果您正在寻找一个快速而简单的解决方案,请查看type-fest包,其中包含许多有用的预构建TypeScript类型,包括PartialDeep类型。
有关更具技术性和可定制性的解决方案,请参见this answer

rkkpypqq

rkkpypqq3#

我从这个问题的答案中获得灵感,创建了自己的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]>;
    };
ej83mcc0

ej83mcc04#

只需创建新类型DeepPartial

DeepPartial的属性是object类型时,DeepPartial基本上引用其自身以将DeepPartial应用于该属性的属性,等等

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

用法

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

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

TSPlayground示例

ttcibm8c

ttcibm8c5#

我不得不使用这个版本来防止数组包含未定义的元素:

type DeepPartial<T> = T extends any[]? T : T extends Record<string, any> ? {
  [P in keyof T]?: DeepPartial<T[P]>;
} : T;
cs7cruho

cs7cruho6#

我发现上面的DeepPartial示例导致了我更复杂的接口的一些奇怪行为,但它们是一个很好的起点。下面是我决定创建一个非主观的DeepPartial(顺便说一下,也适用于非对象类型)

type OptionalArrayOr<T, Otherwise> = T extends T[] ? T[] | undefined : Otherwise;
type OptionalUndefinedOr<T, Otherwise> = T extends undefined ? undefined : Otherwise;
type OptionalNullOr<T, Otherwise> = T extends null ? null | undefined : Otherwise;
type OptionalStringOr<T, Otherwise> = T extends string ? T | undefined : Otherwise;
type OptionalNumberOr<T, Otherwise> = T extends number ? T | undefined : Otherwise;
type OptionalBooleanOr<T, Otherwise> = T extends boolean ? boolean | undefined : Otherwise;

type DeepPartial<T> =
    OptionalStringOr<T,
        OptionalNumberOr<T,
            OptionalBooleanOr<
                T,
                OptionalNullOr<
                    T,
                    OptionalUndefinedOr<
                        T,
                        OptionalArrayOr<
                            T,
                            T extends object ? { [Key in keyof T]?: DeepPartial<T[Key]>} : undefined
                        >
                    >
                >
            >
        >
    >

相关问题