Creating a mapped union type from nested objects based on object keys can be achieved by following these steps

I am currently attempting to generate a custom type based on a design token JSON file.

The JSON data is structured as follows:

{
    "Black": {
        "4": { "value": "#f6f6f6" },
        "6": { "value": "#f2f2f2" },
        "10": { "value": "#e8e8e8" },
        "20": { "value": "#d3d3d3" },
        "30": { "value": "#bcbcbc" },
        "40": { "value": "#a7a7a7" },
        "50": { "value": "#909090" },
        "60": { "value": "#7b7b7b" },
        "70": { "value": "#646464" },
        "80": { "value": "#4e4e4e" },
        "90": { "value": "#383838" },
        "100": { "value": "#222222" }
    },
    "Blue": {
        "4": { "value": "#F5F8F8" },
        "8": { "value": "#EBF0F1" },
        "10": { "value": "#E5EBED" },
        "20": { "value": "#CCD9DC" },
        "30": { "value": "#B2C5CA" },
        "40": { "value": "#99B3B9" },
        "50": { "value": "#7F9FA7" },
        "60": { "value": "#668C96" },
        "70": { "value": "#4C7984" },
        "80": { "value": "#336673" },
        "100": { "value": "#004050" }
    },
    "Teal": {
        "20": { "value": "#ccfbf0" },
        "40": { "value": "#99f8e1" },
        "60": { "value": "#66f4d3" },
        "80": { "value": "#33f1c4" },
        "100": { "value": "#00edb5" }
    }
}

Currently, I have created a union type for the colors using

type Color = keyof typeof colorJSON;

My next task is to establish another union type for all the nested keys within each color object that represent different tint levels like

type Tint = '4' | '6' | '8' etc...
. However, I'm struggling to recursively iterate through the colors and access these keys.

---Edit---

My goal is to ensure that the type automatically updates if new colors are added to the JSON file, so it needs to iterate through the structure rather than directly reference color names.

Answer №1

Take a look at this illustration:

const colorJSON = {
  "Black": {
    "4": { "value": "#f6f6f6" },
    "6": { "value": "#f2f2f2" },
    "10": { "value": "#e8e8e8" },
    "20": { "value": "#d3d3d3" },
    "30": { "value": "#bcbcbc" },
    "40": { "value": "#a7a7a7" },
    "50": { "value": "#909090" },
    "60": { "value": "#7b7b7b" },
    "70": { "value": "#646464" },
    "80": { "value": "#4e4e4e" },
    "90": { "value": "#383838" },
    "100": { "value": "#222222" }
  },
  "Blue": {
    "4": { "value": "#F5F8F8" },
    "8": { "value": "#EBF0F1" },
    "10": { "value": "#E5EBED" },
    "20": { "value": "#CCD9DC" },
    "30": { "value": "#B2C5CA" },
    "40": { "value": "#99B3B9" },
    "50": { "value": "#7F9FA7" },
    "60": { "value": "#668C96" },
    "70": { "value": "#4C7984" },
    "80": { "value": "#336673" },
    "100": { "value": "#004050" }
  },
  "Teal": {
    "20": { "value": "#ccfbf0" },
    "40": { "value": "#99f8e1" },
    &...
(type ColorMap<T extends Record<string, Record<string, unknown>>>)

ColorMap - scans through json keys (Black/Blue/Teal) to get the union of keys of nested objects.

Values - provides the union of all object values.

The composition of ColorMap and Values yields the union of all keys of nested objects since object values are unions of keyof json nested objects.

P.S. Recursive iteration through this object type might not be necessary. However, if you want recursive solutions, refer to my article

Answer №2

To access the nested object's type, you can utilize bracket notation along with the keyof method to switch between different colors:

type ColorJSON = typeof colorJSON;
type Color = keyof ColorJSON;
type Shade = keyof ColorJSON['Black'] | keyof ColorJSON['Blue'] | keyof ColorJSON['Teal']

In this specific scenario, it is not necessary to include Teal as its keys are already included in Blue.

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

Difficulty with index.html file in Crypto-JS (Angular 2 app)

After successfully installing crypto-js in my node_modules folder using the command npm install crypto-js, I included the following script in my index.html file so that I could use the CryptoJS.SHA256() method: <html> <head> <script s ...

Arrange an array within an Angular component

I'm in the process of organizing my list-component made with Angular Material. <div style="width:30%;"> <mat-nav-list matSort (matSortChange)="sortData($event)"> <th mat-sort-header="stuff.name">Name</th> ...

When the React Native app is clicked for the first time, the animation fails to activate

Why is my animation effect not working the first time I click it? Also, can anyone provide an example of using TypeScript with ref binding in React Native for animations? const Test = () => { const messRef: any = React.useRef(new Animated.Value(0)). ...

Issue: Unable to call method "call" as the model "Model" has not been initialized within a Sequelize instance. Kindly ensure that "Model" is added to a Sequelize instance before attempting to use the "call" method

Author.ts import {Table, Model, Column, DataType} from 'sequelize-typescript' @Table export class Author extends Model<Author> { constructor(){ super(); } @Column(DataType.STRING) fname: string @Column(DataType.STRING) lname: strin ...

Does TypeScript lack awareness of type checking within nested functions?

Currently, I am using TypeScript with React and encountering a particular issue. Below is a function I have created to ensure that the variable 'arr' is an Array and not empty. export const isNotEmptyArr = (arr?: unknown[]) => { return Arra ...

The readiness status of the mongoose connection is resulting in a TypeError: Unable to access undefined properties (reading 'readyState')

I've been utilizing Mongo Memory Server for my unit tests successfully, but all of a sudden mongoose.connection is returning as undefined. This has left me completely baffled! I would have anticipated readyState to at least be 0. import * as mongoose ...

Facing the dreaded "ECONNREFUSED" error when trying to connect NodeJS and InfluxDb

I'm encountering an issue when trying to connect to my InfluxDB instance running in a Docker container. To get the InfluxDB image, I used the following command: docker pull influxdb:2.4.0 When I run it locally using Docker Desktop, everything works ...

Does Angular perform tree shaking on a service that is provided at the root level, as long as it is not explicitly injected into a component instance?

Suppose we implement a service similar to this as part of a library: @Injectable({ providedIn: 'root' }) export class ChartJSProvider { constructor() { Chart.register(...registerables); } } and our application makes use of the aforem ...

What is the best way to exempt a unique situation from a directive's operation?

While troubleshooting a bug related to search functionality on my page, I encountered an issue with the search component. The search feature is working correctly and returning the expected values. However, when I clear the search criteria, I noticed that t ...

Unexpected behavior with AWS DynamoDB ScanInput ExpressionAttributeValue

I crafted a scan query to only retrieve enabled data in the following way: const FilterExpression = 'enabled = :enabled'; const ExpressionAttributeValues = { ':enabled': { 'BOOL': true } }; const scanParameters: Sc ...

Connection to mongo is currently unavailable for Middleware next

This code snippet shows a middleware for Next, which is designed to read the subdomain and check if it exists in the database. import { getValidSubdomain } from '@/lib/getValidSubdomain'; import { NextResponse } from 'next/server' impor ...

What is the best way to clear all content from the "textarea" and input fields after submitting?

I'm currently using a Devextreme library for my project. I am having trouble finding a way to clear all the textarea information in the component along with other inputs when clicking on the Save button. Despite trying various methods, I have not bee ...

Issue with Vue property's sub-object not inheriting methods from prototype

Incorporating Vue into my project to showcase an intricate hexagonal grid has been quite challenging. Utilizing the Honeycomb library for handling the grid data in memory has proven to be both beneficial and complex. The library introduces a specialized Gr ...

Customizing Column Headers in Ag-Grid when Implementing Aggregation Functions

When using aggFunc in a column definition, the headerName appears in the format func(string) instead of just displaying the string as defined. This behavior is not present when AggFunc is null. const columnDef: any = { headerName: 'Test', ...

Is it possible for ngModelChange to function with a custom form control?

Suppose I want to create a custom form control. Is it possible to achieve this? <custom-control [ngModel]="myModelVariable" (ngModelChange)="modelHasChanged($event)"></custom-control> I've successfully implemented [(ngModel)] with all th ...

The (functionName) does not exist within the subclass as a valid function

I am currently developing an extension for a web-based text editor. However, I am facing some unexpected results due to the class hierarchy in my code. Despite attempting to relocate the "validate" function to the base class, I have not been successful in ...

Specifications for TypeScript Columns

Is there a way to create a comprehensive column definition map for various model types with proper typings in JavaScript? Let's explore how to achieve this: export type User = { id: number; admin: boolean; email: string; }; export type Book = { ...

Utilize Angular 4 to effectively update objects within Firebase Cloud Firestore

Hey there! I've been working with firebase and angular 4 on this new thing called firestore. I've been trying to update one of the documents, but I keep encountering this error. https://i.sstatic.net/638E1.png Here's my component: https:/ ...

I require the ability to identify and capture the ID of the button (touchable opacity) that has been

When trying to delete a selected button by obtaining its specific id, I am facing an issue where after the first execution, the id of the deleted item is retained instead of the newly selected one. I am in need of a solution that allows me to retrieve the ...

Using webpack's hash in JavaScript for cache busting

Can someone guide me on adding a hash to my js file name for cache-busting purposes? I've researched online but still uncertain about where to include the [hash] element. Any help would be appreciated. Provided below is a snippet from my webpack.conf ...