Creating the option for nested type properties to be optional

Working with auto generated types from a library poses a challenge for me. The types currently have all values as required, but I would like to mark some of them as optional. While I know this can be achieved using generics in typescript, I am unsure about the exact approach. To better illustrate, consider the following example:

Here is a sample type structure with nested types:

interface Person {
    name: string;
    hometown: string;
    nickname: string;
    data:{
        address:string,
        phone:number
    }
}

I intend to apply an operation similar to the following to designate the name property in the root type and the address property in the nested type as optional:

type TransformedPerson = MakeOptional<Person,{name:string, data:{address:string}}>

or:

type TransformedPerson = MakeOptional<Person,"name"|"data.address">

The expected result should resemble the type created below:

/*
type TransformedPerson = {
    name?: string;
    hometown: string;
    nickname: string;
    data:{
        address?:string,
        phone:number
    }
}
*/

An attempt was made to utilize the Partial in nested property with typescript method to make properties optional in the root object but it proved ineffective for nested types:

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

type PartialExcept<T, K extends keyof T> = RecursivePartial<T> & Pick<T, K>;

type TransformedType = PartialExcept<Person, "name"> /// This works only for root type

type TransformedType = PartialExcept<Person, "name"|"data.address"> /// throws error

Answer №1

Let's explore a unique approach utilizing tuples to define optional keys:

interface Person {
    name: string;
    hometown: string;
    nickname: string;
    data: {
        address: string;
        phone: number;
    }
}

type KeyUnion<T extends string, U extends string[]> = {
  [K in keyof U]: U[K] extends `${T}.${infer V}` ? V : never;
}

type ExceptPartial<T, U extends string[]> = {
  [K in keyof T as K extends U[number] ? K : never]?: T[K]
} & {
  [K in keyof T as K extends U[number] ? never : K]: K extends string
    ? ExceptPartial<T[K], KeyUnion<K, U>>
    : T[K]
}

type ModifiedPerson = ExceptPartial<Person, ['name', 'data.address']>

const individual: ModifiedPerson = {
  hometown: 'Citytown',
  nickname: 'Cityname',
  data: {
    phone: 987654321
  }
};

The ExceptPartial type introduces a fresh type based on the intersection of two mapped types: one comprising optional properties with keys found in the specified tuple, and another containing the remaining mandatory properties.

This type employs recursion by calling itself when dealing with nested objects. For nested objects, the KeyUnion type is utilized to form a tuple solely containing descendant keys of a specific property.


Explore Further

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

Dealing with Typescript: Reducing an object containing other objects

Having some difficulties implementing a reduce function with TypeScript - struggling with types and return value. Facing issues with omitting controls from Storybook, causing two TypeScript errors indicated in the code marked ** ERROR ** Seeking advice on ...

Vue 3 Single Page Application. When selecting, it emits the language and the contentStore does not update the content exclusively on mobile devices

My Vue 3 Single Page Application is built on Vite 4.2 and TypeScript 5.02. When I click to select a language, it emits lang.value and in the parent component App.vue, contentStore should update the content. It works flawlessly on my Linux Ubuntu desktop i ...

The 'split' property is not found in the type 'string | ArrayBuffer'. The property 'split' is not available in the type 'ArrayBuffer'.ts(2339)

"I need assistance with splitting a base64 audio file. Specifically, I want to extract only the audio data without the 'data:audio/wav;base64' text included. Can someone provide the correct code for this?" “This code snippet is intended for us ...

Incorporate service providers into models with Ionic3/Angular4

I am seeking feedback from individuals with more experience than me to determine if my approach is correct. I am currently working on an Ionic3-Angular app that involves a CRUD functionality for "Clientes". From what I have researched, the recommended st ...

Conceal the React button once it has been pressed

In my checklist of questions, I have set up a system where the first button is shown if any checkboxes are selected. If no checkbox is selected, then the second "Submit" button is displayed. Upon clicking submit, a message appears inside. Additionally, for ...

Accessing Nested FormGroup in Angular 6 by its name

Dealing with Nested Form Groups address = new FormGroup({ 'com.complex.Address':new FormGroup({ city: cityControl, streetName: streetNameControl, houseNumberAddition: houseNumberAdditionControl, ho ...

RC6 - What is the significance of encountering an 'Unexpected token <' error message?

After updating to RC.6, I am encountering a series of errors related to third-party components. Specifically, the error message displayed is: SyntaxError: Unexpected token <. This issue has arisen with ng2-bootstrap, ng2-select, and angular2-jwt. Howev ...

JavaScript: Navigating function passing between multiple React components

I'm currently working on a React Native application utilizing TypeScript. In my project, there is a component named EmotionsRater that can accept two types: either Emotion or Need. It should also be able to receive a function of type rateNeed or rate ...

Are there any alternatives to ui-ace specifically designed for Angular 2?

I am currently working on an Angular2 project and I'm looking to display my JSON data in an editor. Previously, while working with AngularJS, I was able to achieve this using ui-ace. Here is an example of how I did it: <textarea ui-ace="{ us ...

Utilize the object's ID to filter and display data based on specified criteria

I retrieved an array of objects from a database and am seeking to narrow down the results based on specific criteria. For instance, I want to display results only if a user's id matches the page's correct id. TS - async getResultsForId() { ...

Setting up NextJs in Visual Studio Code with Yarn

When I used yarn create next-app --typescript to set up a TypeScript Next.js application with Yarn, everything seemed to be working fine with the command yarn run dev. However, Visual Studio Code was not recognizing any of the yarn packages that were added ...

Node module for Nativescript angular project that enables multi select dropdown or picker functionality

Does anyone know of a Node NPM module for creating a multi-select dropdown in NativeScript Angular? I've searched through many plugins in the NativeScript marketplace, but haven't been able to find one that fits my needs. I need the plugin to wo ...

Utilize TypeScript to define the types based on the return values produced by an array of factory functions

My application features multiple factory functions, each returning an object with specific methods (more details provided below). I am interested in creating a type that combines the properties of all these returned objects. const factoryA = () => ({ ...

Distribute a TypeScript Project on NPM without exposing the source code

Issue: My library consists of numerous .ts files organized in structured folders. As I prepare to publish this library, I wish to withhold the source (typescript) files. Process: Executing the tsc command results in the creation of a corresponding .js fil ...

Angular 2 - The creation of cyclic dependencies is not allowed

Utilizing a custom XHRBackend class to globally capture 401 errors, I have encountered a dependency chain issue in my code. The hierarchy is as follows: Http -> customXHRBackend -> AuthService -> Http. How can this problem be resolved? export cla ...

Using the IN clause in a prepared statement with a single variable in TypeORM

I have a SQL file that contains a query like this: SELECT * WHERE id IN ($1) The SQL query is read and passed into a TypeORM query with an array of parameters. const result = await this.entityManager.query(myQuery, parameters); I want the parameters to b ...

io-ts: Defining mandatory and optional keys within an object using a literal union

I am currently in the process of defining a new codec using io-ts. Once completed, I want the structure to resemble the following: type General = unknown; type SupportedEnv = 'required' | 'optional' type Supported = { required: Gene ...

What benefits could you derive from utilizing an interface to generate a fresh Array? (Pulumi)

Just delving into the world of TypeScript and Pulumi/IaC. I'm trying to wrap my head around a code snippet where an array of key values is being created using an interface: import * as cPulumi from "@company/pulumi"; interface TestInterface ...

Place a new button at the bottom of the react-bootstrap-typeahead dropdown menu for additional functionality

Currently, I have successfully implemented the React Bootstrap Typeahead with the desired options which is a good start. Now, my next challenge is to integrate a custom button at the end of the dropdown list for performing a specific action that is not ne ...

Converting input/select string values into strongly-typed values with Angular 2

I've been searching for a solution for quite some time now, but I'm still a bit confused. The issue is simple: I have a model with a boolean property that I'm mapping to a select element in Angular. The select allows the user to choose betwe ...