Challenge encountered when setting new values to an object depending on its existing values

I am facing an issue with a data object that stores values and their previous values. The keys for the previous values are suffixed with ":previous" e.g. foo and foo:previous. However, I encountered a type error when trying to assign values to the previous values, stating

Type "foo" is not assignable to type "${K}:previous"
. It seems that TypeScript does not recognize that both types have the same structure. I attempted to resolve this by:

type PostfixKeys<T> = {
  [K in keyof T as `${string & K}:previous`]: T[K];
}

type Foo2 = Foo & PostfixKeys<Foo>
type Foo = { "foo": string; "bar": boolean }

const test = <K extends keyof Foo>(key: K, foo: Foo2) => {
  foo[`${key}:previous`] = foo[key]; // <= Type '"foo"' is not assignable to type '`${K}:previous`'.(2322)
}

What would be the most effective way to resolve this issue? Here is a Playground with this code.

I attempted using T[keyof T] instead of T[K] which resolved the typing issue. However, TypeScript did not correctly infer the value associated with the key in relation to the ":previous" suffix:

type PostfixKeys<T> = {
  [K in keyof T as `${string & K}:previous`]: T[keyof T];
}

type Foo2 = Foo & PostfixKeys<Foo>
type Foo = { "foo": string; "bar": boolean }

const test = <K extends keyof Foo>(key: K, foo: Foo2) => {
  foo[`${key}:previous`] = foo[key]; // <= works but
  const bat = foo["bar:previous"]; // <= is not type `string | boolean` instead of `boolean` as it was correctly when using T[K]
}

This approach also compromises type safety, making it unsuitable for my requirements.

Answer №1

You are almost there. Take a closer look at your assignment

  foo[`${key}:previous`] = foo[key];

To better understand the error, consider that the relationship between the values for K and ${K}:previous is not explicit in the type Foo2.

When TypeScript evaluates it, it simply sees a collection of values in Foo2, with one being assigned to another – the error disappears if you also specify bar as type string.

While not necessarily recommended, imagine if you had this for demonstration:

type WithPrevious<T> = {
  [K in keyof T]: { value: T[K], previous: T[K] }
}
const testWithPrevious = <K extends keyof Foo>(key: K, foo: WithPrevious<Foo>) => {
  foo[key].previous = foo[key].value;
}

In this scenario, the types of value and previous are linked, allowing the reassignment.

So, revisiting your assignment, you can assert that anything assignable to a Key slot can be assigned to a ${Key}:previous slot:

  foo[`${key}:previous` as K] = foo[key];

Playground

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

Encountering a TypeScript error when using Redux dispatch action, specifically stating `Property does not exist on type`

In my code, there is a redux-thunk action implemented as follows: import { Action } from "redux"; import { ThunkAction as ReduxThunkAction } from "redux-thunk"; import { IState } from "./store"; type TThunkAction = ReduxThunk ...

What is the process for including a new item in the p-breadcrumb list?

Having trouble getting my code to add a new item to the p-breadcrumb list on click. Any assistance would be greatly appreciated. Thank you in advance! Check out the live demo here ngOnInit() { this.items = [ {label: 'Computer'}, ...

Can someone guide me on configuring Material-UI DataGrid in React to have multiple headers with column span?

Is there a way to achieve multiple headers with column span in the Material-UI DataGrid component? view image example ...

JavaScript - Assigning the same value to 2 properties but console log displays them as distinct values

While iterating through an array of documents, I am encountering a strange issue where setting two properties to the same value results in them having different values when logged using console.log. Here is the code snippet: this.logicItem.$promise.then( ...

The attribute 'split' is not found on the never data type

I have a function that can update a variable called `result`. If `result` is not a string, the function will stop. However, if it is a string, I then apply the `split()` method to the `result` string. This function always runs successfully without crashin ...

Disable and grey out the button while waiting for the Observable to broadcast successfully

component.html <button mat-raised-button color="primary" type="submit"> <mat-icon>account_box</mat-icon> <span *ngIf="!loading">&nbsp;&nbsp;&nbsp;Register</span> <span * ...

Can the tiles in a grid-list be organized in a specific order?

I am facing an issue with a class named 'scenario' that has properties such as 'id', 'name', and 'number' among others. In the HTML, scenarios are displayed in this format: <mat-grid-list [cols]="breakpoint" r ...

The error message "The type 'DynamicModule' from Nest.js cannot be assigned to the type 'ForwardReference' within the nest-modules/mailer" was encountered during development

Recently, I decided to enhance my Nest.js application by integrating the MailerModule. I thought of using the helpful guide provided at this link: Acting on this idea, I went ahead and performed the following steps: To start with, I executed the command ...

The code encountered an issue with TS2554 error, as it was anticipating two arguments but was only provided with one when

Before, I utilized ViewChild like this: @ViewChild("InternalMedia") localStream; @ViewChild("emoji") mEmoji; Everything was functioning well up until angular-7.x. However, after updating to angular-8.x, the following error started popping up: .../call_ ...

Error message "Property 'name' does not exist on type '{}'" is encountered when using Ionic/Angular HttpClient and no data type is specified

While working on my Ionic project, I encountered an error in Angular when trying to fetch data from an API using HttpClient. The error message that popped up was 'Property 'name' does not exist on type '{}'.'. Below is the cod ...

Retrieve the specific type of property from a generic data structure

I am currently working on a project where I need to determine the type of property within a given Type: type FooBarType { foo: string, bar: number } The function would be structured like this: getType<K extends keyof T>(key: K): string. The ...

The sequence of events in React Native following a navigation.navigate call

Seeking suggestions and advice, I currently have the following 2 lines of code within a react native expo component: this.props.navigation.navigate("App"); patchUser(this.state.dataSource.userInfo.username, this.state.dataSource.userInfo.firstN ...

The Vue 3 Composition API - The property retrieved by setup() "is accessed during rendering but is not defined in the instance."

I've been experimenting with Vue 3's Composition API by creating a small in-app message console, but I'm having trouble pinpointing the error in my code. When rendering this component, the state is being accessed during render (in the loop), ...

The Express server automatically shuts down following the completion of 5 GET requests

The functionality of this code is as expected, however, after the fifth GET request, it successfully executes the backend operation (storing data in the database) but does not log anything on the server and there are no frontend changes (ReactJS). const ex ...

Retrieve a type from a Union by matching a string property

Looking at the following structure: type Invitation = | { __typename?: 'ClientInvitation' email: string hash: string organizationName: string } | { __typename?: 'ExpertInvitation' email: strin ...

Utilizing RavenDB with NodeJS to fetch associated documents and generate a nested outcome within a query

My index is returning data in the following format: Company_All { name : string; id : string; agentDocumentId : string } I am wondering if it's possible to load the related agent document and then construct a nested result using selectFie ...

Tips for setting the state of a parent component in React JS when multiple children components are involved without causing conflicts

I have 3 child components that are updating a common parent state object. Each child component should only update its own field in the object. How can I achieve this? My objective is to keep track of the individual state of each child component. const Ch ...

Incorporating HTML code within a .ts file: a basic guide

I'm relatively new to Angular, but I've been given a project that's built with Angular. As I examine the .ts file containing the list of property types, I need to wrap a span around the label text. Is this doable? Here is the current list ...

I seem to be missing some properties in the request body schema. Why am I receiving an incomplete model for

Seeking assistance in grasping the working of models in loopback4. Here's a model I defined: @model() export class ProductViewConfig extends BaseConfig { @property({ type: 'string', id: true, generated: true, }) _id?: strin ...

Discover the magic of integrating FeathersJS REST functionality within Angular with these simple steps

I've encountered a challenge while trying to make Feathers work in Angular with a Feathers REST server. It seems that no requests are being made. My Feathers server hosts the resource http://example.com/app/experiences which returns data in paginated ...