Expanding the property of an established type to a nested type

Let's talk about titles. Well, maybe not this one.

I tried to come up with a good title, but it didn't work out as planned.

In my coding journey, I have created a cool interface called DefaultTheme:

  export interface DefaultTheme {
    colors: {
      background: '#fff'
      text: '#1d1d1f'
      secondaryText: '#515154'
      primary: '#06c'
    }
    pages: PageTheme
  }

Next, I decided to extend this interface with another one called PageTheme for specific pages:

  export interface PageTheme {
    sensei: {
      colors: {
        background: '#fff'
        text: '#1d1d1f'
        secondaryText: '#515154'
        background: '#06090D' //different
      }
    }
  }

But here's the challenge - I don't need all the properties from DefaultTheme, just want to modify the colors section by changing the background color.

This led me to experiment with the following approach:

 export interface PageTheme {
    sensei: {
      colors: DefaultTheme & {
        background: '#06090D'
      }
    }
  }

As I attempted to merge the colors into actual objects, I encountered an error:

const defaultTheme: Omit<DefaultTheme, 'pages'> = {
  colors: {
    background: '#fff',
    text: '#1d1d1f',
    secondaryText: '#515154',
    primary: '#06c',
  },
}

const pagesThemes: PageTheme = {
  sensei: {
    colors: {
      ...defaultTheme.colors,
      background: '#06090D',  // ERROR: Type string is not assignable to type 'never'.
    },
  },
}

The issue arises when trying to redefine the background color for

pagesThemes.sensei.colors.background
, resulting in a
Type string is not assignable to type 'never'
error.

All I really want to achieve is to spread my default values and only update the ones that are different.

Answer №1

If you find yourself in need of integrating colors into your type, despite it being an uncommon practice, here are two methods to achieve it:

  1. playground
export interface DefaultTheme {
  colors: {
    background: '#fff'
    text: '#1d1d1f'
    secondaryText: '#515154'
    primary: '#06c'
  }
  pages: PageTheme
}

 export interface PageTheme {
  sensei: {
    colors: Omit<DefaultTheme, "background"> & {
      background: '#06090D'
    }
  }
}

declare const z: PageTheme
z.sensei.colors.background // "#06090D"
  1. playground
interface Colors<BG extends string> {
  background: BG,
  text: '#1d1d1f'
  secondaryText: '#515154'
  primary: '#06c'
}

export interface DefaultTheme {
  colors: Colors<'#fff'>
  pages: PageTheme
}

export interface PageTheme {
  sensei: {
    colors: Colors<'#06090D'>
  }
}

declare const z: PageTheme
z.sensei.colors.background // "#06090D"
  1. Utilize a helper type: playground
type Override<T, U> = {
  [key in keyof T | keyof U]: key extends keyof U ? U[key] : T[key & keyof T];
}

export interface DefaultTheme {
  colors: {
    background: '#fff'
    text: '#1d1d1f'
    secondaryText: '#515154'
    primary: '#06c'
  }
  pages: PageTheme
}
 export interface PageTheme {
  sensei: {
    colors: Override<DefaultTheme, {
      background: '#06090D'
    }>
  }
}

declare const z: PageTheme
z.sensei.colors.background // "#06090D"

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

Typescript and Visual Studio Code Issue: Module "myimage.png" Not Found

I am encountering an issue where VS Code is complaining about not being able to find a module when trying to import an image from an assets directory within my project. Despite the fact that the image import works fine, I keep receiving the error message: ...

Is there a way for me to steer clear of having to rely on the Elvis Operator?

Throughout my journey building my Angular 2 website, I've found the Elvis Operator to be a crucial element that makes everything possible. It seems like every task I undertake involves mastering how to apply it correctly in every instance where data i ...

Is it possible to deduce the output type of a function based on its input?

In a web development project, the function getFormData() plays a crucial role in validating and sanitising a FormData object based on a specified schema. If the validation process goes smoothly without any errors, the function will return the cleansed Form ...

Exploring the process of introducing a new property to an existing type using d.ts in Typescript

Within my src/router.ts file, I have the following code: export function resetRouter() { router.matcher = createRouter().matcher // Property 'matcher' does not exist on type 'VueRouter'. Did you mean 'match'? } In an ...

Creating mandatory reactive form fields in Angular 11's HTML code based on conditions

I am facing an issue with two select/dropdown fields in my form. The second dropdown field should render based on a condition *ngIf="selectedStdntList?.packages". However, the problem is that the submit form function stops working even when the c ...

What is the best way to detect object changes in typescript?

Having an object and the desire to listen for changes in order to execute certain actions, my initial approach in ES6 would have been: let members = {}; let targetProxy = new Proxy(members, { set: function (members, key, value) { console.log(k ...

Strategies for preventing multi-level inheritance of TypeScript class properties and methods

In my current JavaScript class structure, the DataService is defined as follows: // data.service.ts export class DataService { public url = environment.url; constructor( private uri: string, private httpClient: HttpClient, ) { } ...

What is the best way to iterate through the result of an HTTP request in Angular 11?

I am a beginner with Angular and currently working in Angular 11. I am facing issues with making an http request. Despite going through numerous Stack Overflow posts, none of the solutions seem to work for me, even though some questions are similar to mine ...

Why is it necessary to use "new" with a Mongoose model in TypeScript?

I'm a bit confused here, but let me try to explain. When creating a new mongoose.model, I do it like this: let MyModel = moongoose.model<IMyModel>("myModel", MyModelSchema); What exactly is the difference between MyModel and let newModel = ne ...

Unable to bring in an exported class from a TypeScript file

I have a TypeScript file named foo.ts that contains an exported class called "Foo" export default class Foo{ } I am attempting to import this class into another file within the same directory import {Foo} from './foo'; However, I am encounter ...

Steps for selectively targeting and updating a group of properties in a TypeScript class

Is there a way to consolidate this code into one function that can handle all the tasks below? I'm adding more text here to meet the requirements and hoping for a solution. Thank you! TypeScript is an amazing language that differs slightly from JavaS ...

What is the best way to save code snippets in Strapi for easy integration with SSG NextJS?

While I realize this may not be the typical scenario, please listen to my situation: I am using Strapi and creating components and collections. One of these collections needs to include code snippets (specifically typescript) that I have stored in a GitH ...

Organize elements within an array using TypeScript

I have an array that may contain multiple elements: "coachID" : [ "choice1", "choice2" ] If the user selects choice2, I want to rearrange the array like this: "coachID" : [ "choice2", "choice1" ] Similarly, if there are more tha ...

How can I wrap text in Angular for better readability?

I've created a calendar in my code that displays events for each day. However, some event descriptions are too long and get cut off on the display. Even after attempting to use Word Wrap, I still can't see the full text of these events unless I c ...

Ag-grid with Angular 2 allows users to easily edit entire columns with just a few

I need help modifying a column in my ag-grid. My ag-grid currently looks like this: grid image I want to update all values in the column Etat to be arrêté, but I'm struggling to make it work. This is the code I've been trying: private gridO ...

Palantir Forge: Enhancing Column Values with Typescript Functions

I am seeking assistance with a TypeScript function related to ontology objects. I want to develop a TypeScript program that accepts a dataframe as input. The objective is to nullify the values in other columns when a value from a row in a particular column ...

Application Initialization Error: appInits is not a valid function

When my Angular v17 application starts, I need to set some important values right away. This is how it's done in app.config.ts: export const appConfig: ApplicationConfig = { providers: [ ConfigService, ... { pr ...

Tips for accessing nested values post-subscription in Angular with JSON data?

I have implemented a getReports method that utilizes my web API's get method to retrieve JSON formatted responses. Step1 getReports() { return this._http.get(this.url) .map((response: Response) => response.json()) ...

Just completed the upgrade of my Angular project from version 9 to version 12, but now encountering issues with a module that utilizes Plotly

Here is the content of my app module file. All components and imports are in their respective places as specified in the documentation: import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from &apos ...

Tips for showing a DialogBox when a blur event occurs and avoiding the re-firing of onBlur when using the DialogBox

Using React and Material UI: In the code snippet provided below, there is a table with TextFields in one of its columns. When a TextField triggers an onBlur/focusOut event, it calls the validateItem() method that sends a server request to validate the ite ...