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

What's the deal with the Zod getter function?

Is it possible to create a getter function within a Zod object? For instance, in ES5 you can achieve the following: const person = { firstName: "John", lastName: "Doe", get fullName() {return `${this.firstName} ${this.lastName}`} } person.fullNam ...

Encountering an issue when attempting to attach an event listener to the entire document

I need help troubleshooting an issue with a function that is supposed to perform certain operations when the scrollbar is moved. I attached an event listener to the document using an ID, but it's resulting in an error. ERROR Message: TypeError: Canno ...

Utilizing the 'as' prop for polymorphism in styled-components with TypeScript

Attempting to create a Typography react component. Using the variant input prop as an index in the VariantsMap object to retrieve the corresponding HTML tag name. Utilizing the styled-components 'as' polymorphic prop to display it as the select ...

When attempting to print a Rectangle on my webpage using JavaScript and taking user input, I encountered an error stating that 'str' is not defined

I'm trying to implement a feature where clicking on the "try it" button will prompt the user for the number of lines and then print that many lines in the form of a rectangle. However, when I run my code, nothing appears on the DOM and there is an err ...

Attach an event listener to a particular textarea element

Currently, I am developing a project in Next.js13 and my focus is on creating a custom textarea component. The goal is to have this component add an event listener to itself for auto-adjusting its height as the user types. Below is the relevant section of ...

Animating with Angular 2

As I delve into this informative resource, my goal is to incorporate some animations into my applications, but I find myself grappling with understanding how the animations are triggered. HTML Component <div class="navbar navbar-default navbar-fixed-t ...

"Encountering connectivity issues between NestJs and TypeORM when trying to establish a

My current challenge involves connecting to MySQL. I have set the database connection variables in a .env file located in the root directory, and I am initializing the connection in the app.module.ts file. The problem arises when I attempt to create or run ...

The binding element 'dispatch' is assumed to have the 'any' type by default. Variable dispatch is now of type any

I came across this guide on implementing redux, but I decided to use TypeScript for my project. Check out the example here I encountered an issue in the AddTodo.tsx file. import * as React from 'react' import { connect } from 'react-redux ...

Retrieving class properties in typescript

I am working with a class that has numerous methods, which I refer to as myClass. When calling it, the syntax is as follows: myClass[key]() Is there a way to retrieve the valid values for key? I tried using keyof myClass, but received an error message st ...

Using absolute imports to resolve modules in TypeScript and Next.js

When I import functions from files using absolute imports, I keep encountering errors that I have been trying to resolve. The errors manifest in a certain way, as shown here: Despite the errors, the functions are successfully imported and usable. It seems ...

Why does the property of {{hero.name}} function properly in a <h> tag but not in an <img> tag?

Within this template, the code below functions correctly: <h3>{{hero.name}}</h3> It also works for: <a routerLink="/details/{{hero.id}}">{{hero.name}}</a> However, there seems to be an issue with the following image path ...

Angular 4: Conditional CSS classes causing issues with transitions

After scouring through stackoverflow, I have yet to find a solution to my current issue. I am utilizing a conditional class on a div that is applied when a boolean variable becomes true. Below is the code snippet in question: <div [class.modalwindow-sh ...

Executing a dual ajax request in Angular 5

I am attempting to perform two HTTP requests consecutively, with the second request depending on the result of the first. However, it seems like I am overlooking something: getParkingSpots(date) { var gmt = this.getTimezone().subscribe(data=>{ if(d ...

Angular 8 delivers an observable as a result following a series of asynchronous requests

I am working on a simple function that executes 3 asynchronous functions in sequence: fetchData() { this.fetchUsers('2') .pipe( flatMap((data: any) => { return this.fetchPosts(data.id); }), fl ...

"Exploring the dynamic duo of Angular2 and ng2Material

I am currently facing an issue with the styling in my code while using ng2Material with Angular2. First: A demonstration of Material style functioning properly can be seen in this plunker. When you click on the button, you will notice an animation effect. ...

Selecting ion-tabs causes the margin-top of scroll-content to be destroyed

Check out the Stackblitz Demo I'm encountering a major issue with the Navigation of Tabs. On my main page (without Tabs), there are simple buttons that pass different navparams to pre-select a specific tab. If you take a look at the demo and click t ...

What is the best way to store an audio Blob in the repository file system?

Currently, I have set up a system to record user audio through the device's microphone and can successfully download it on the same device. However, my goal now is to store this audio in my database by making an API call. How can I efficiently send th ...

Having trouble uploading a pdf file into a .tsx file

As a beginner in learning TypeScript, I recently embarked on building a portfolio website using React JS and TypeScript. However, I encountered a problem when trying to import a PDF file from my images into the resume.tsx file within my project folder. Th ...

Choosing a default selected value from a dropdown list with multiple editing options

When loading my "multiple edit" screen, I default the values as follows: private createFormGroupItem(item: ...): FormGroup { return this.formBuilder.group({ title: new FormControl(item.title, [Validators.required]), effectiveDate: new FormC ...

Is there a specific method for conducting a production build using AngularCLI rc.1?

Just recently upgraded to angular-cli version 1.0.0-rc1 by following the guidelines provided on the wiki. The application functions properly when I execute ng serve. Similarly, the app works as expected when I run ng build. However, encountering an issu ...