Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript: deep partial?

Tags:

typescript

People also ask

What is deep partial?

Second-degree burns are injuries to the skin caused by heat, radiation, electricity, chemicals, or friction. A deep second-degree burn injures the top layer of skin (epidermis) and the tissue below the skin (dermis). This type of burn is also called a deep partial-thickness burn.

What are partials in TypeScript?

The Partial type in TypeScript is a utility type which does the opposite of Required. It sets all properties in a type to optional.

What are utility types in TypeScript?

Partial, Required, Readonly, Pick, Omit, Record, NonNullable, Extract, Exclude Types. Photo by Ash Edmonds on Unsplash. In TypeScript, there are multiple built-in utility (default) types. It would be a good thing to know they exist because they are handy in some situations. Some are easier to understand than others.

What is TypeScript omit?

The TypeScript Omit utility type Like the Pick type, the Omit can be used to modify an existing interface or type. However, this one works the other way around. It will remove the fields you defined. We want to remove the id field from our user object when we want to create a user.


You can simply create a new type, say, DeepPartial, which basically references itself:

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

Then, you can use it as such:

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

See proof-of-concept example on TypeScript Playground.


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.


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]>;
    };