Custom "set attribute" feature in TypeScript

One issue I faced was resolved by creating the function shown below :

function setProperty<T extends Record<string, string>>(obj: T, key: keyof T) {
  obj[key] = "hello";
}

However, when I tried to compile the code, I encountered an error with obj[key] highlighted in red and the following message displayed :

Type 'string' is not assignable to type 'T[keyof T]'.ts(2322)

I believe this error is due to the usage of the keyword "extends" but I am unsure how to resolve it.

Any guidance on this matter would be greatly appreciated. Thank you!

Answer №1

If you are facing a problem, there are two different approaches to solve it based on your scenario. If your goal is simply to assign any string as a value to a specific key without the need for generics:

function setProperty(obj: Record<string, string>, key: string) {
  obj[key] = "hello";
}

However, if your object has predefined keys and values, you can utilize generics to ensure type safety in your code:

function setProperty<T extends Record<string, string>, K extends keyof T, V extends T[K]>(obj: T, key: K, value: V) {
  obj[key] = value
}

type ObjectValue = 'foo' | 'bar'

type A = {
  foo: ObjectValue,
  baz: 'baz'
}

const a: A = {
  foo: 'foo',
  baz: 'baz'
}

setProperty(a, 'foo', 'bar') // works
setProperty(a, 'foo', 'baz') // type error

This approach also provides the advantage of InteliSense suggestions.

EDIT:

As per the OP's request for type checking the key and setting an arbitrary string, this revised solution should be more suitable:

function setProperty<K extends string>(obj: Record<K, string>, key: K) {
  obj[key] = "hello";
}

Answer №2

Simply utilize the Object.assign method.

type StringValue<Obj> = {
    [Prop in keyof Obj]: Obj[Prop] extends string ? Prop : never
}[keyof Obj]

/**
 * Retrieve all keys with string values
 * Only these keys are permitted
 */
type Result = StringValue<{ age: number, name: string }> // name

function setProperty<
    Value,
    Obj extends Record<string, Value>
>(obj: Obj, key: StringValue<Obj>) {
    Object.assign(obj, { [key]: 'hello' })
}

const user = {
    age: 42,
    name: 'John',
    surname: 'Doe'
}

setProperty(user, 'name') // okay
setProperty(user, 'surname') // alright alright

setProperty(user, 'age') // expected error, as age is a number

By utilizing the StringValue utility type, TS will only permit keys with string values.

Playground

Please attempt to avoid mutations in TS. Refer to this article for more information.

Answer №3

Not entirely certain if this method will yield the desired outcome for your particular situation, but if your goal is simply to ensure compilation, a type assertion can be used.

function setProperty<T extends Record<string, string>>(obj: T, key: keyof T) {
  obj[key] = "hello" as T[keyof T];
}

The literal value "hello" has a specified type of hello. By explicitly asserting this in TypeScript, it indicates to the compiler that you intend to assign a value to obj, which contains keys of type keyof T. Without this assertion, the compiler may not recognize hello as the correct type.

For setting the value of obj[key] to hello, following Michael Vrana's initial suggestion would be my preferred approach.

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

Erase Typescript Service

To remove a PostOffice from the array based on its ID, you can use a checkbox to select the desired element and then utilize its ID for the delete function. Here is an example: let postOffices = [ {postOfficeID: 15, postCode: '3006&ap ...

How can one access DOM elements (getting and setting values) that are nested within an *ngFor loop?

How can I access the <span> and <select> elements in my code template shown below? <div *ngFor="---"> <div> <span></span> <select> <option></option> <option></option> ...

No data being displayed or returned from API when using async await

My Ionic 6 + Angular 14 application is currently facing an issue with displaying data retrieved from an API... I have implemented a service to fetch the data from the API and then called this service in the component. The app compiles without any errors a ...

What is the importance of including "declare var angular" while working with Typescript and AngularJS?

I've been working on an AngularJS 1.7 application that's coded entirely in TypeScript, and there's always been one thing bothering me. Within my app.module.ts file, I have this piece of code that doesn't sit right with me: declare va ...

Are there any alternatives to ui-ace specifically designed for Angular 2?

I am currently working on an Angular2 project and I'm looking to display my JSON data in an editor. Previously, while working with AngularJS, I was able to achieve this using ui-ace. Here is an example of how I did it: <textarea ui-ace="{ us ...

Having trouble with @typescript-eslint/member-ordering feature not functioning properly?

Ensuring a precise ordering in TypeScript classes is my goal, with a specific emphasis on enforcing alphabetical order within groups. To achieve this, I am refering to the following documentation: Shown below is the member-ordering configuration extracte ...

Guide on exporting a submodule within a TypeScript package

My aspiration is to develop a Typescript library that emulates the structure of popular libraries like RxJS and Angular Material, which are divided into submodules. RxJS and Angular exhibit a way to import features using syntax like this: // RxJS import ...

What steps can I take to stop Vetur and TypeScript from displaying duplicate TypeScript warnings in VSCode?

I have a Vue2 project using TypeScript in VSCode with Vetur and TypeScript extensions installed. Whenever there is a TypeScript warning, both the TypeScript and Vetur overlays show duplicate warnings. Example of duplicate warnings Also, the intellisense ...

Struggling to retrieve data with arrow function in Vue

I'm currently learning Vue and facing an issue with fetching data from an API to my component. I have a service class that successfully retrieves data from the API, as the API itself is working fine. Here's the code snippet: import IReview from & ...

What is the best way to ensure that two promises are both resolved before triggering a function from within a promise?

In my code, I have a forEach loop on a matches fetch that looks like this: matches => { matches.forEach(match => { Promise.all([this.teamService.getTeam(match._links.homeTeam.href)]) .then(team => { match. ...

Creating an enum in TypeScript can be accomplished by using the enum

What transformations do enums undergo during runtime in the TypeScript environment? Fruit.ts enum Fruit {APPLE, ORANGE}; main.ts let basket = [Fruit.APPLE, Fruit.ORANGE]; console.log(basket); The resulting main.js file remains identical to the .ts ver ...

No types are assigned to any props

I recently began working on a SvelteKit skeleton project for my personal website. However, I encountered an error when using Svelte with TypeScript - specifically, I kept getting the message Type '<some prop type>' is not assignable to type ...

The Angular firestore is showing an error stating that the property 'toDate' is not found in the 'Date' type

I am currently working on converting a timestamp object from Firestore to a Date object in TypeScript by utilizing the toDate() method. import { AngularFirestore } from '@angular/fire/firestore'; ... constructor(private database?: AngularFirestor ...

Type of event target MouseEvent

I am currently working on a custom hook const hasIgnoredClass = (element: Element, ignoredClass: string) => (element.correspondingElement ? element.correspondingElement : element ).classList.contains(ignoredClass); const isInIgnoredElement = ( ...

Angular input box with integrated datepicker icons displayed inside

Currently, I have an input field and a datepicker displayed in a row. However, I need to show an icon inside the input box instead. Here is my code: <div class="mb-2" style=" float: left;" class="example-full-width" class= ...

Angular 5's data display glitch

Whenever I scroll down a page with a large amount of data, there is a delay in rendering the data into HTML which results in a white screen for a few seconds. Is there a solution to fix this issue? Link to issue I am experiencing HTML binding code snippe ...

The step-by-step guide to fixing a Gigwage client eslint error using nestJS

Whenever I utilize the gigwage client for my services, I encounter the following eslint error: TS2742: The inferred type of 'findAll' cannot be named without a reference to '@gigwage/client/node_modules/axios'. This is likely not porta ...

What is the best way to add all IDs to an array, except for the very first one

Is there a way to push all response IDs into the idList array, excluding the first ID? Currently, the code below pushes all IDs to the list. How can it be modified to exclude the first ID? const getAllId = async () => { let res = await axios({ m ...

Angular's getter value triggers the ExpressionChangedAfterItHasBeenCheckedError

I'm encountering the ExpressionChangedAfterItHasBeenCheckedError due to my getter function, selectedRows, in my component. public get selectedRows() { if (this.gridApi) { return this.gridApi.getSelectedRows(); } else { return null; } } ...

Enhancing Responses in NestJS with External API Data

I'm a beginner in NestJs, Graphql, and typescript. I am trying to make an external API call that is essentially a Graphql query itself. The goal is to modify the response, if necessary, and then return it for the original request or query, in this ca ...