126

Is there a way to specify a partial type in TypeScript that also makes all child objects partials as well? For example:

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

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

This throws the following error:

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

Is there any way to make child nodes partials as well?

Ben Botvinick
  • 2,837
  • 3
  • 16
  • 41
  • Does this answer your question? [use Partial in nested property with typescript](https://stackoverflow.com/questions/47914536/use-partial-in-nested-property-with-typescript) – Debug Diva Jun 20 '22 at 07:37

6 Answers6

205

You can simply create a new type, say, DeepPartial, which basically references itself (updated Jan 2022 to handle possible non-objects):

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

Then, you can use it as such:

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

See proof-of-concept example on TypeScript Playground.

Terry
  • 63,248
  • 15
  • 96
  • 118
  • 10
    Just beware that some folks in this thread have run into performance problems with recursive types: https://github.com/microsoft/TypeScript/issues/35729 – kumar303 Sep 03 '20 at 02:02
  • @kumar303 they're not performance issues per se (at runtime there will be no TS code, just JS). They're more compile timing issues. – Javi Marzán Jun 23 '21 at 08:58
  • 5
    I meant performance issues as in slow compilation times. However, since writing that comment, I've seen a lot of improvements to TS. I currently work in a code base using DeepPartial where we do not have issues. – kumar303 Jun 26 '21 at 04:02
  • 5
    In order to fix the DeepPartial weird types - use this: `type DeepPartial = T extends object ? { [P in keyof T]?: DeepPartial; } : T;` – Shl Sep 02 '21 at 11:48
  • 1
    @Shl Nice. Problems came up everywhere after a recent TS upgrade & this fixed it; this answer should be updated – JoshuaDavid Oct 25 '21 at 16:32
  • 1
    @Shi And I was wondering where all those strange errors(type not assignable to some other type) were coming up from. Big thanks. – Eggy Dec 18 '21 at 19:58
  • @Terry it's a recursive type. So you will always end up calling it with primitive types. – Shl Dec 22 '21 at 16:23
  • you deserve a medal – Safi Habhab Apr 18 '22 at 08:29
  • Note that this doesn't handle arrays. Use type-fest's `PartialDeep` if you need that. – Trevor Robinson Aug 23 '22 at 16:06
  • 1
    This is a wrong type, as it transforms arrays into something, that you barely want: `string[] > (string | undefined)[]` – Onkeltem Jul 10 '23 at 13:55
  • @Onkeltem The answer is meant to work with objects like OP has asked for. If you’ve got gripes with using it with arrays… that’s because it wasn’t part of the question and you’re not supposed to use it as-is – Terry Jul 11 '23 at 05:31
  • That problem with how this utility type transforms arrays *does* apply to objects, because it does the same thing to any array properties on an object. – Mark Hanna Jul 29 '23 at 10:29
  • @MarkHanna I find it interesting how many people complain about how this recursive type messes with arrays where nowhere in OP's question nor my answer infers that it should be used on arrays ¯\\_(ツ)_/¯ – Terry Jul 30 '23 at 14:11
  • @Terry 1) Any object may have some array props. 2) OP didn't say they're interested in scalar/mapped types only. 3) We're discussing a recursive thing. From 1,2,3 => we have to process arrays correctly as well. Does it make sense now? – Onkeltem Aug 01 '23 at 10:16
  • That's said, here is an array-friendly version: `type DeepPartial = T extends any[] ? T : { [P in keyof T]?: DeepPartial }` I would update the answer. – Onkeltem Aug 01 '23 at 10:18
46

If you're looking for a quick and easy solution, check out the type-fest package, which has many useful prebuilt TypeScript types including the PartialDeep type.

For a more technical and customizable solution, see this answer.

Federico Grandi
  • 6,785
  • 5
  • 30
  • 50
Ben Botvinick
  • 2,837
  • 3
  • 16
  • 41
  • 3
    `type-fest` seems to be a more popular option: https://www.npmtrends.com/type-fest-vs-utility-types – Maxime Gélinas Aug 03 '20 at 17:58
  • Thank you! This even works in a plain JS project when using jsdoc and vscode intellisense. – Nick Bilyk Feb 01 '21 at 20:58
  • Thank you. Looking at the source code for `type-fest` helped me figure out how to make my `DeepPartial` implementation work when the passed type contains `any`. – Nathan Arthur Jun 11 '21 at 16:48
  • 4
    I don't think installing a whole package just for a single type is a good idea. This kinds of choices is what leads to huge redundant node_modules by javascript/ts developers. – John Doe May 10 '22 at 21:37
  • is DeepPartial usable with recursive types or it will get an inifinite loop? If for example I have a type Users, that has a field named "friend" of type User[]? – Apperside Apr 02 '23 at 14:07
11

I inspired myself on the answers on this question to create my own version of PartialDeep.

I stumbled upon some issues with built-in objects along the way; for my use case, I wouldn't expect a Date object to be missing some of its methods. It's either there, or it isn't.

Here's my version:

// 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]>;
    };
gustavohenke
  • 40,997
  • 14
  • 121
  • 129
  • 2
    Thanks! This approach worked for me (in shorter version), while `T extends object` had problem with optional nested fields. – Chakrit W Sep 20 '22 at 09:58
8

Simply create a new type DeepPartial

DeepPartial basically references itself when its property is of object type to apply DeepPartial to that property's properties and so on

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

Usage

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

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

TS Playground Example

ashuvssut
  • 1,725
  • 9
  • 17
2

I had to use this version to prevent arrays having undefined elements:

type DeepPartial<T> = T extends any[]? T : T extends Record<string, any> ? {
  [P in keyof T]?: DeepPartial<T[P]>;
} : T;
user5480949
  • 1,410
  • 1
  • 15
  • 22
1

I found that the examples of DeepPartial above resulted in some strange behavior for my more complicated interfaces, but they were a great place to start. Here is what I settled on for making an unopinionated DeepPartial (incidentally works for non-object types as well)

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
                        >
                    >
                >
            >
        >
    >