Is TypeScript capable of handling deep partials?

Can a partial type in TypeScript be specified in a way that applies partiality to all child objects as well? For instance:

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

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

This code snippet raises the following error:

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

Is there a method to designate child nodes as partials as well?

Answer №1

To simplify, you can create a brand new type called DeepPartial. This type essentially points back to itself (recently updated in Jan 2022 to handle potential non-objects):

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

After defining this type, you can utilize it like this:

const example: DeepPartial<Example> = {
  foo: 1,
  bar: { baz: true }
};

Take a look at a proof-of-concept example in TypeScript Playground.

Answer №2

If you need a simple and efficient way to solve your problem, consider utilizing the type-fest library, which offers a variety of ready-to-use TypeScript types like the PartialDeep type.

For a more in-depth and customizable approach, take a look at this detailed response.

Answer №3

After consulting various responses to a specific question, I decided to construct my own interpretation of PartialDeep.

Throughout the process, I encountered difficulties with certain built-in objects. In my scenario, I would not anticipate a Date object to be devoid of some of its essential methods. It should either be fully present or entirely absent.

Below is the customized version I devised:

// Primitive types (+ Date) are left as they are, or possibly undefined.
type PartialDeep<T> = T extends string | number | bigint | boolean | null | undefined | symbol | Date
  ? T | undefined
  // Arrays, Sets, Maps, and their readonly counterparts have their inner elements partially modified, while maintaining the original instance structure
  : 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 lastly, all other objects.
  : {
      [K in keyof T]?: PartialDeep<T[K]>;
    };

Answer №4

To easily create a new type called DeepPartial

The DeepPartial type essentially refers back to itself when a property is of type object, allowing DeepPartial to be applied recursively to the properties of that property and so on

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

How to Use

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

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

Check out this TypeScript Playground Example

Answer №5

To avoid undefined elements in arrays, I found a solution using this specific version:

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

Answer №6

After experimenting with various implementations of DeepPartial, I encountered some unexpected behavior when dealing with complex interfaces. Nevertheless, I found them to be a valuable starting point. Here is the approach I found most suitable for creating a flexible DeepPartial solution that can also handle non-object types:

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

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Setting various colors for different plots within a single chart: A step-by-step guide

I'm currently tackling a project that requires me to showcase two different plots on the same chart, one being a "SPLINE" and the other a "COLUMN". My aim is to assign distinct background colors to each of these plots. Please note that I am referring ...

Hidden input fields do not get populated by Angular submit prior to submission

I am in the process of implementing a login feature in Angular that supports multiple providers. However, I have encountered an issue with submitting the form as the loginProvider value is not being sent to the server. Below is the structure of my form: &l ...

Angular application that features a material table created using *ngFor directive and displayedColumns defined as an array

I am trying to create a table that displays columns with the format {key: string, display: string} where 'display' is the header and 'key' is used to display the value. <ng-container *ngFor="let col of displayedColumns"> ...

What type of HTML tag does the MUI Autocomplete represent?

Having trouble calling a function to handle the onchange event on an autocomplete MUI element. I've tried using `e: React.ChangeEvent`, but I can't seem to locate the element for the autocomplete component as it throws this error: The type &apos ...

Is it possible to utilize multiple TypeScript files without a module loader?

My goal here is to include two TypeScript files: app.ts and decorator.ts. The app.ts file utilizes a class defined in decorator.ts. Initially, I attempted to create a basic example without using a module loader by adding script references to the files in ...

Dynamic addition of script to <head> element in Angular is a common task for many developers

I have explored some examples that illustrate how to dynamically add a script with a URL in Angular However, I am interested in adding this type of content to the <head> <script> !function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(! ...

Angular 2: Encounter with 504 Error (Gateway Timeout)

I am struggling with handling errors in my Angular 2 application. Whenever the backend server is offline, an uncaught error appears in the console: GET http://localhost:4200/api/public/index.php/data 504 (Gateway Timeout) This is how my http.get me ...

Switch up the default font in your Nuxt 3 and Vuetify 3 project

I've been doing a lot of searching on Google, but I can't seem to find the solution. It seems like the challenge might be that the Nuxt 3 + Vuetify 3 combination isn't widely used yet? My current task is to implement a custom default font. ...

Generating a JavaScript bundle using the npm TypeScript compiler

Is there a way to compile TypeScript source files into one JavaScript bundle file? We have developed a tool on node.js and are currently using the following TypeScript compilation code: var ts = require('typescript'); ... var program = ts.creat ...

Sortable layouts and tables in Ionic 3

I found a great example of an Ionic table that I'm using as reference: https://codepen.io/anon/pen/pjzKMZ <ion-content> <div class="row header"> <div class="col">Utility Company Name</div> <div c ...

The type {properties .....} is incompatible with the type ActionReducer<AdminState, Action> in Angular 12 using NGRX

Implementing NGRX library for redux to organize the state of the application in a structured way: export interface ApplicationState { adminState: AdminState } export interface AdminState { adminProductCategory: ProductCategoryState; adminProdu ...

Creating an enum from an associative array for restructuring conditions

Hey everyone, I have a situation where my current condition is working fine, but now I need to convert it into an enum. Unfortunately, the enum doesn't seem to work with my existing condition as mentioned by the team lead. Currently, my condition loo ...

Show the current time using Moment.js

I am currently working on developing a clock component that displays the current time in real-time. Issue: The initial time is correctly displayed when the page loads (HH:mm A), but the clock does not update dynamically. clock.component.ts : import { ...

Add the onclick() functionality to a personalized Angular 4 directive

I'm facing an issue with accessing the style of a button in my directive. I want to add a margin-left property to the button using an onclick() function in the directive. However, it doesn't seem to be working. Strangely, setting the CSS from the ...

Encountering a Typescript issue with the updated Apollo server version within a NestJS application

After upgrading my nestJS application to use version 3.2 of apollo-server-plugin-base, I encountered two TypeScript errors related to a simple nestJS plugin: import { Plugin } from '@nestjs/graphql' import { ApolloServerPlugin, GraphQLRequest ...

Angular 2 file upload encountering CORS issue leading to 401 unauthorized error

Encountered a "401 Unauthorized" error in Chrome and Firefox while attempting to upload files using angular 2 CLI to an apache2-server with a PHP backend. Despite trying three different node modules, the issue persists from the OPTIONS-preflight stage, ...

The properties of the extended Array class in TypeScript are not able to be accessed

It might be the late hour or my brain overloaded with programming, but I'm completely puzzled by this simple class: export class Path extends Array { constructor(...params:Array<any>) { super(...Object.assign([], arguments)); } ...

How to handle type errors when using properties in Vue3 Single File Components with TypeScript

I've hit a roadblock while attempting to utilize properties in Vue3. Despite trying various methods, I keep facing issues during the type-check phase (e.g.: yarn build). The project I'm working on is a fresh Vue3-ts project created using Vite. B ...

Instantiate an object of the ng.IQService type without using injection

Is it possible to define a local variable in the controller of type ng.IQService ( private _q: ng.IQService;) without requiring injection? My technology stack includes typescript and angular. The reason for this requirement is due to existing legacy code ...

Typescript: The property isComposing is not found on Event type

While working on a React app with Typescript, I encountered a Typescript error both during compile time and in the editor: TS2339: Property isComposing does not exist on type Event This issue arises when handling an OnChange event in an HTML Input element ...