Utilizing Typescript generics to define constraints for the type of T[K], where K is the key and T is the object

In the setting I'm dealing with, specific objects with an id attribute expire every "tick" and require retrieval using getObjectById. I am interested in creating a setter function to update a property of an object by mapping

thing.property => getObjectById(thing.property.id)
. This function should accept a thing T and a key K, where T[K] can either be an object with an id (HasID) or an array of such objects (HasID[]).

I believe I am close to a solution, but it requires some adjustments. The current implementation is as follows (a static method in a class named $):

static refresh<T extends {[K in keyof T]: HasID}, K extends keyof T>(thing: T, key: K): void;
static refresh<T extends {[K in keyof T]: HasID[]}, K extends keyof T>(thing: T, key: K): void;
static refresh<T extends {[K in keyof T]: HasID | HasID[]}, K extends keyof T>(thing: T, key: K): void {
    if (_.isArray(thing[key])) {
        thing[key] = _.map(thing[key] as HasID[], s => getObjectById(s.id)) as HasID[];
    } else {
        thing[key] = getObjectById(thing[key].id) as HasID;
    }
}

For instance, the desired outcome for

foo: {bar: HasID, baz: HasID[], biz: string[]}
would be:

$.refresh(foo, 'bar') // foo.bar = getObjectById(foo.bar.id)
$.refresh(foo, 'baz') // foo.baz = _.map(foo.baz, x=>getObjectById(x.id))
$.refresh(foo, 'biz') // error: foo.biz is not HasID or HasID[]
$.refresh(foo, 'boo') // error: 'boo' is not a key of foo

Can someone offer guidance on how to correctly define the type of T[K]?

Answer №1

Almost there, the issue is that only the key specified by K in object T needs to be HasID or HasID[], not every key.

class $ {
  static update<T extends { [P in K]: HasID }, K extends string>(item: T, key: K): void;
  static update<T extends { [P in K]: HasID[] }, K extends string>(item: T, key: K): void;
  static update<T extends { [P in K]: HasID | HasID[] }, K extends string>(item: T, key: K): void {

  }
}

let obj: { val1: HasID, val2: HasID[], val3: string[] };
$.update(obj, 'val1') // obj.val1 = getObjectById(obj.val1.id)
$.update(obj, 'val2') // obj.val2 = _.map(obj.val2, x=>getObjectById(x.id))
$.update(obj, 'val3') // error: obj.val3 is not HasID or HasID[]
$.update(obj, 'val4') // error: 'val4' is not a key in obj

Instead of { [P in K]: HasID }, you can use Record<K, HasId>. I retained your initial version for better comparison purposes. With Record, the signatures would be:

class $ {
  static update<T extends Record<K, HasID>, K extends string>(item: T, key: K): void;
  static update<T extends Record<K, HasID>[], K extends string>(item: T, key: K): void;
  static update<T extends Record<K, HasID | HasID[]>, K extends string>(item: T, key: K): void {

  }
}

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

Exploring methods to successfully upload a blob to Firebase and modify it using cloud functions

For days now, I've been attempting to successfully upload a file to firestorage using firebase functions but haven't had any luck. This is the progress I've made so far: export const tester = functions.https.onRequest(async (request, respons ...

The variable is accessed before it is initialized in the context of Next.js and Server Actions

Currently, I am utilizing the new Data Fetching feature in Next JS to retrieve data from an API and store it in a variable named 'contact.' However, I am facing the issue of receiving an error message stating that "variable 'contact' is ...

typescript: tips for selecting a data type within an object

I need help extracting the type of the 'name' property from an object belonging to the Action interface. interface Action { type: string, payload: { name: string } } I attempted to use Pick<Action, "payload.name">, but it didn&apos ...

What is the correct way to extract a value from a keyvalue pair?

When dealing with an object that returns boolean "issues", I specify it as a string. If the value is true, I aim to show a checkmark; if false, I want to display a cross. <ul *ngFor="let filtered of reposFiltered | keyvalue"> <li *ngIf=& ...

The base class is invoking a function from its child class

There are two classes, a base class and a derived one, each with an init function. When constructing the derived class, it should: Call its base constructor which: 1.1. Calls its init function Call its own (derived) init function. The issue is that ...

Discovering the most recent 10 date elements in a JSON object within a React application

I have an array of objects containing a date element. My goal is to identify the 10 most recent dates from this element and display them in a table format. When I attempt to render these dates using a mapping technique that targets each data with data.date ...

MikroORM - Conditional join without foreign key constraints on the ID

I came across a rather peculiar database schema that includes a jsonb field with userId and userType attributes, along with two different user tables for distinct user types. The selection of the table to join based on the userType is crucial. Although I ...

In the scenario where I have a nested readonly array within an object, what is the best way to duplicate that object and transform the array to allow for mutations (such as inserting into Akita)?

Suppose I have the following TypeScript interface: interface Member { readonly id: number; readonly name: string; readonly email: string; groups: <ReadonlyArray>Group } interface Group { readonly id: number; readonly name: string; ...

What is preventing me from setting the User object to null in my Angular application?

Currently, I am working on a project in Angular and encountering a specific issue. In my service class, the structure looks like this: export class AuthService { authchange: new Subject<boolean>(); private user: User; registerUser(authD ...

"Converting to Typescript resulted in the absence of a default export in the module

I converted the JavaScript code to TypeScript and encountered an issue: The module has no default export I tried importing using curly braces and exporting with module.exports, but neither solution worked. contactController.ts const contacts: String[ ...

Facing a 'No provider for' error in my Angular 2.0.0 application

I recently developed a service called SecurityService to handle authentication. Check out the code for this service below: import { Injectable } from '@angular/core'; @Injectable() export class SecurityService { items: any[]; construct ...

The parameter must be of type 'string', but you are attempting to assign a 'Promise<any>'

Starting a React App requires fetching the user's information from localStorage and initiating a socket connection with the server based on the user's id. However, when trying to pass the user's id to the socket function, TypeScript throws ...

The inclusion of individual CSS files in a TypeScript React project does not have any effect

My issue involves creating a new react project with typescript and adding a custom component with a separate CSS file for styling. The folder structure is as follows: https://i.sstatic.net/UNtEP.png In the Header.css file, I have defined a class: .mainHe ...

Enhancing Angular2 authentication with Auth0 for enabling Cross-Origin Resource Sharing

I have been working on implementing user authentication through Auth0. I followed the instructions provided on their website, but I am encountering authentication issues. Whenever I try to authenticate, an error message appears in the console stating that ...

Executing NestJS code after applying a pipe but before reaching the method handler

Is it possible to insert additional code after all the pipes have transformed but before the method handler is called? https://i.sstatic.net/IjQvv.png ...

leveraging parcel for importing typescript dependencies

I am currently using parcel to process typescript for a web extension. I have installed JQuery and its type definitions via npm. In my typescript file, I have the following at the top: import $ from "jquery"; import "bootstrap"; However, when running run ...

Apologies, but there was an error attempting to differentiate 'nombreyo'. Please note that only arrays and iterables are permitted for this action

Encountering an error while attempting to display a class in the HTML. <li> <ul> <li *ngFor="let refac of refactormodel" > -- word_to_rename: {{refac.word_to_rename}} -- renowned_word: {{refac.renowned_word}} ...

Creating cohesive stories in Storybook with multiple components

I need assistance with my storybook setup. I have four different icon components and I want to create a single story for all of them instead of individual stories. In my AllIcons.stories.tsx file, I currently have the following: The issue I am facing is ...

Retrieve data upon component mounting and deactivate the query in React-query

When navigating to a search result page, query parameters are passed to useQuery. I want the data to be fetched only when the user clicks the "Search" button after changing the search prompt. I attempted to use enabled: false and call refetch() on button ...

Using const enums across multiple files with react-scripts-ts

Within our project, we have two distinct subprojects (backend and frontend) that are compiled independently. The frontend utilizes react-scripts-ts, so it is crucial to avoid cross-imports between the two subprojects to maintain the integrity of the transp ...