How to define a TypeScript recursive object with a defined endpoint?

Welcome to my first question!

I am currently facing an issue with defining an object to store strings in multiple languages. I am looking for a flexible solution and have considered using a nested object structure. However, I want the final object to adhere to a specific type or interface.

Initially, I attempted the following:

interface Text {
  [key: string | number]: ComponentText | Language;
}

interface Language {
  [key: string]: string;
}

text: Text = {
  title: {
    en: "English text",
    sv: "Swedish text",
    fr: "French Text",
    es: "Spanish Text",
  }
  paragraph: {
    subText: {
      en: "English text",
      sv: "Swedish text",
      fr: "French Text",
      es: "Spanish Text",
    }
  }
}

// getCurrLang(s: any) handles getting the correct language string or throws an error when "s" isn't "Language".

However, I encountered errors when trying to retrieve

getCurrLang(text['paragraph']['subText'])
, which displayed:

Argument of type 'string | Text | Language' is not assignable to parameter of type 'Text | Language'. Type 'string' is not assignable to type 'Text | Language'.

This led me to another attempt where I thought defining the key value as 'language' might help, but it did not fully address the issue:

type LangCode = 'en' | 'sv' | 'fr' | 'es';

interface Text {
  [key: string]: ComponentText | Array<ComponentText>;
  language?: Language;
}

type Language = {
  [key in LangCode]: string;
};

Is there a more effective way to define this type of structure or any other suggestions to improve the current approach?

Answer №1

In order to better organize the content, I decided to break down the structure into two distinct concepts: a collection of translations for a specific text element named TextTranslation, and a framework to dynamically store these translations for use in a component using ComponentText.

type LanguageCode = 'en' | 'sv' | 'fr' | 'es';

type TextTranslation = {
  [key in LanguageCode]: string;
};

interface ComponentText {
  [key: string | number]: ComponentText | TextTranslation;
}


let textContent: ComponentText = {
  title: {
    en: "English text",
    sv: "Swedish text",
    fr: "French Text",
    es: "Spanish Text",
  },
  paragraph: {
    subText: {
      en: "English text",
      sv: "Swedish text",
      fr: "French Text",
      es: "Spanish Text",
    }
  }
}

Answer №2

The issue arose due to our approach in handling the nested objects. Even with the guidance from @David Culberth, TypeScript mandates the use of type narrowing for union types like ComponentText | TextTranslation

A simple call like

getCurrLang(text['paragraph']['subText'])
was insufficient.

To resolve this, I expanded the functionality of getCurrLang(s: any) as follows:

public getLangString(
  s: ComponentText | TextTranslation,
  selector?: Array<string>
): string {
  if (this.isTextTranslation(s)) {
    return s[this.getLanguage()];
  } else if (this.isComponentText(s) && selector) {
    return this.getLangString(
      s[selector[0]],
      selector.length > 1 ? selector.slice(1) : undefined
    );
  }

  return 'error: ' + s + 'is not of type TextTranslation or ComponentText';
}

As a result, the updated retrieval method now appears as

getLangString(text, ['paragraph', 'subText'])

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

Issue during deployment: The type 'MiniCssExtractPlugin' cannot be assigned to the parameter type 'Plugin'

I'm working on deploying a Typescript / React project and have completed the necessary steps so far: Created a deployment branch Installed gh-pages for running the deployed application Added a deploy command as a script in the package.j ...

What is the best way to transpile TypeScript within the Astro framework?

Recently, I decided to dive into exploring Astro for a couple of upcoming projects. In my research, I delved into the script and typescript sections of the documentation (), as well as (). However, I found the workflow somewhat counterintuitive and struggl ...

Removing the AM and PM from OwlDateTime in Angular is simple since the time format is already in 24-hour time

Using OwlDateTime in a 24-hour format: <div *ngIf="isSchedule" class="form-inline"> <label style='margin-right:5px ;margin-left:210px'> Date Time: <input [owlDateTimeTrigger]="dt" [owlDateTime]="dt" class="form-control" placeh ...

Troubleshooting: The issue of importing Angular 2 service in @NgModule

In my Angular 2 application, I have created an ExchangeService class that is decorated with @Injectable. This service is included in the main module of my application: @NgModule({ imports: [ BrowserModule, HttpModule, FormsModu ...

Refining a collection of item elements by examining a string attribute, disregarding letter case differences

I'm working on a code snippet that generates item components from my list of objects (Frivillig). <app-frivillig-item *ngFor="let frivilligEl of frivillige" [frivillig]="frivilligEl"> </app-frivillig-item> Now, I have a new requireme ...

For Angular 4, simply add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of the component in order to permit any element

After using angular-cli to create a new project (ng new my-project-name), I ran npm run test successfully without any issues. To display font icons in my project, I added the Font Awesome module from https://www.npmjs.com/package/angular-font-awesome. In ...

Java Spring - Extracting a single, specific key value from a deeply nested JSON structure without the need for mapping to a particular POJO class

Looking for a solution to extract a specific key value from a deeply nested JSON without the need to map back to Java POJOs. I'm dealing with an API that returns a huge JSON response which is not easily readable on a screen. The JSON response has mul ...

Incorporating Common Types for Multiple Uses

Is there a way to efficiently store and reuse typings for multiple React components that share the same props? Consider the following: before: import * as React from 'react'; interface AnotherButtonProps { disabled?: boolean; onClick: (ev ...

Using async method in controller with NestJS Interceptor

I am seeking a way to capture both the result and any potential errors from an asynchronous method within a controller using an interceptor. When an error is thrown, the interceptor can respond accordingly. However, I am struggling to figure out how to tri ...

Send a variable from a next.js middleware to an API request

I've been attempting to pass a middleware variable to my API pages via "req" but have encountered some issues Even after trying to send the user token to pages using "req", it consistently returns null The middleware file in question is: pages/api/u ...

A guide on retrieving an object property in keen.io

When using the extractions API in keen.io, I'm facing an issue where specific properties that are objects are not being retrieved. curl "https://api.keen.io/3.0/projects/PROJECT_ID/queries/extraction?api_key=READ_KEY&event_collection=COLLECTION_N ...

Setting up roles and permissions for the admin user in Strapi v4 during the bootstrap process

This project is built using Typescript. To streamline the development process, all data needs to be bootstrapped in advance so that new team members working on the project do not have to manually insert data into the strapi admin panel. While inserting ne ...

Is there a way for me to extract information from the FAQs on this website?

Here is the URL I need to access for this project. This webpage presents questions in collapsed form, and clicking on them reveals corresponding answers. My objective is to convert these questions into keys and their respective answers into values. For i ...

Remove a row by performing a type cast deletion

Trying to delete a row from a table without a primary key can be a challenge. Hibernate sometimes creates them, but in this case, there is no OID or CTID to rely on. Fortunately, I have the entire row data and I am using that instead. This method has been ...

What is the best way to return JSON from a 403 error using Express?

Express has the ability to handle errors, and you can send back JSON data when returning a 403 status code like this: let error = {errorCode:"1234"} res.sendStatus(403, {error: error}); To catch and inspect the error in your frontend JavaScript, you can ...

Guide on calculating the quantity of rows in a Json data set

How can I determine the number of rows where my ID is present in one of the fields, which are stored as JSON objects: { "Monday":{"1":"15","2":"27","3":"74","4":"47","5":"42","6":"53"}, "Tuesday":{"1":"11","2":"28","3":"68","4":"48","5":"43","6":"82"} ...

Capturing user input with Angular Material forms in HTML

In the process of working on a project in Angular, I am utilizing the Angular Material component library. As part of this project, I am creating a form with multiple fields. Everything is functioning properly, however, the layout appears slightly off: ht ...

Display a D3 Collapsible Tree visualization using information stored in a variable

I am currently working on an app that requires the display of a collapsible tree graph using D3. The data needed for this graph is not stored in a file, but rather within the database. It is retrieved through an Ajax call to a rest service and then passed ...

The total number of rows in each section is determined by the count specified in the "numberOfRowsInSection

When attempting to configure this segmented table to showcase different JSON arrays in separate sections, I encountered an issue where having a larger row count in any section following the first resulted in the error message: [__NSArrayM objectAtIndex:]: ...

Bringing PNGs and SVGs into React - Error TS2307: Module not found

While attempting to import PNGs/SVGs (or any other image format) into my React project, TypeScript presents the following error: TS2307: Cannot find module '../assets/images/homeHeroImage.svg' or its corresponding type declarations. The frontend ...