Retrieve the generic type parameter of an interface implementation

I am attempting to extract a type parameter from an interface in order to use it as a parameter for a generic function.

In my particular scenario, I have the following generic types:

interface ITranslatable<T, K extends keyof T> {
  translations: ITranslation<T, K>
}

export type ITranslation<T, K extends keyof T> =
  {
    lang: string
  } & {
    [P in K]?: T[P];
  };

I aim to employ these types in the following manner:

interface MyType extends ITranslatable<MyType, "name" | "description"> {
  id: string;
  name: string;
  description: string;
}

The generic function I utilize is defined as follows:

export type TranslationKeys<T> = T extends ITranslatable<T, infer K>
  ? K
  : never;

export function translate<T extends ITranslatable<T, TranslationKeys<T>>>(item: T, key: TranslationKeys<T>, language: string = DEFAULT_LANGUAGE) {
  return language === DEFAULT_LANGUAGE
    ? item[key]
    : item.translations?.find(translation => translation.lang === language)?.[key] ?? item[key];
}

To call this function, I use translate(myItem, 'name', 'en');, for example.

While everything appears to compile correctly, each time I invoke my translate function, the autocompletion for the second argument provides me with the entire list of keys present in T. I desire to properly type my function so that the autocompletion only displays the keys I provided as the second parameter when extending it.

For instance, when using:

interface Foo extends ITranslatable<Foo, 'name' | 'description'> {
  id: string;
  name: string;
  description: string;
}

it currently shows

'id' | 'name' | 'description' | 'translations'
, whereas I would prefer it to display 'name' | 'description' exclusively.

Any suggestions?

Answer №1

I encountered a problem and managed to find a solution by making adjustments in my code. Instead of modifying the interfaces, I made changes to my function as shown below:

type Extracted<T> = T extends (infer U)[] ? U : T;

export function localization<T extends TranslatableType<T, K>, K extends keyof T>(
  item: T,
  key: Exclude<keyof Extracted<T['translations']>, 'language'>,
  language: string = DEFAULT_LANGUAGE
): string {
  return (
    language === DEFAULT_LANGUAGE
      ? item[key as keyof T]
      : (item.translations
        ?.find(translation => translation.language === language)
        ?.[key as keyof TranslationType<T, K>]
        ?? item[key as keyof T])
  ) as string;
}

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

Unexpected behavior with Node js event listener

I am currently working on emitting and listening to specific events on different typescript classes. The first event is being listened to properly on the other class, but when I try to emit another event after a timeout of 10 seconds, it seems like the lis ...

Encountering: error TS1128 - Expecting declaration or statement in a ReactJS and TypeScript application

My current code for the new component I created is causing this error to be thrown. Error: Failed to compile ./src/components/Hello.tsx (5,1): error TS1128: Declaration or statement expected. I've reviewed other solutions but haven't pinpointed ...

Exploring the nuances of checking lists in TypeScript

In the following list: empList = ['one','two','finish','one','three'] I am required to evaluate conditions based on empList: Condition 1: If 'two' appears before 'finish', set this ...

Step-by-step guide on invoking an asynchronous method in canActivate for Ionic and Angular

My objective is to acquire a token for authenticating users. I am utilizing the import { Storage } from '@ionic/storage-angular'; to store the data, but I am encountering an issue where the Storage methods only function in asynchronous mode. Her ...

Encountering a 405 error when making an OpenAI API call with next.js, typescript, and tailwind CSS

I encountered a 405 error indicating that the method request is not allowed. I am attempting to trigger an API route call upon clicking a button, which then connects to the OpenAI API. Unsure of my mistake here, any guidance would be highly appreciated. E ...

What steps can I take to ensure that AstroJS components do not conceal SVG elements when the SVG is incorporated into another file with client:load?

Currently, I am developing a weather application using Astro.js in conjunction with React. One of the features includes an SVG component that serves as the project logo and is implemented in the initial page loader. Upon the page loading, the SVG functions ...

Integrating Immutable.js with Angular 2

Looking to optimize performance in your Angular 2 app with immutable.js? Although my app is functioning properly, I am aiming to enhance its performance through optimization and refactoring. I recently discovered immutable.js and want to convert the data ...

When passing e: EventTarget as a forwarded prop through a wrapper component, Typescript raises an error about the missing "value" property in the onChange function

In my project, there is a custom component called SelectField that serves as a wrapper for rendering label, helper text, and select input (inspired by TextField from @material-UI). The SelectField component exposes props like value and onChange, which are ...

Exploring the integration of multiple HTTP requests in Angular with the power of RxJS

Is there a way to make multiple HTTP calls simultaneously in an Angular service and then combine the responses into one object using RxJS? import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; im ...

Issue with Date generation in TypeScript class causing incorrect date output

I have a simple code snippet where I am creating a new Date object: var currentDate = new Date(); After running this code, the output value is: Sat May 11 2019 13:52:10 GMT-0400 (Eastern Daylight Time) {} ...

Issue with Angular 11: Unable to bind to 'ngForOf' as it is not recognized as a valid property of 'tr' element

My issue lies with a particular page that is not functioning correctly, even though it uses the same service as another working page. The error seems to occur before the array is populated. Why is this happening? I appreciate any help in resolving this p ...

Tips for styling the Button component in the shadcn/ui library for maximum impact

I'm currently working on a project using the shadcn/ui library. How can I properly customize it to meet my specific needs? For example, let's say I require an extra large red rounded Button for a call-to-action button in my project. What would be ...

Using TypeScript to Declare Third Party Modules in Quasar

I'm currently trying to integrate Dropzone-vue into my Quasar project. However, I've encountered an issue as I can't directly install and declare it in a main.js file due to the lack of one in Quasar's structure. Additionally, an error ...

Modifying data types within complex nested object structures

I am looking to traverse the data structure recursively and create a custom type with specific fields changed to a different type based on a condition. Using the example structure below, I aim to generate a type (Result) where all instances of A are repla ...

Comparing plain objects and class instances for modeling data objects

What is the recommended approach for creating model objects in Angular using TypeScript? Is it advisable to use type annotation with object notation (where objects are plain instances of Object)? For example, let m: MyModel = { name: 'foo' } ...

Circular Dependencies in Angular (only the file name)

Previously, I used to keep interfaces and services in separate files but later combined them into one file since they were always requested together. For example, instead of having user.interface.ts and user.service.ts as separate files, I now have all the ...

Creating test cases for a service that relies on a Repository<Entity> to consume another service

Having trouble creating tests for an auth.service that seems pretty straightforward from the title. However, every time I run the tests, I encounter this error: TypeError: Converting circular structure to JSON --> starting at object with cons ...

What is the best approach to implementing a blur function for a specific input within a parent component?

I have created a custom input field as a separate component. I want to include multiple input fields in the parent component using directives: <app-input ...></app-input> My goal is to pass the blur event/function to the parent component speci ...

Unusual problem arises with scoping when employing typeguards

Consider the following TypeScript code snippet: interface A { bar: string; } const isA = <T>(obj: T): obj is T & A => { obj['bar'] = 'world'; return true; } let obj = { foo: 'hello' }; if (!isA(obj)) thro ...

What is the best way to fetch the chosen item in an Angular select dropdown

I am working on a dropdown that populates with items of type ObjectA. item.component.html: <label>items list: <select formControlName="itemsCtl" (change)="onChange()"> <option *ngFor="let item of itemList" [value]="item">{{i ...