Utilize an unspecified object key in TypeScript generics for advanced referencing

Upon initial inspection, the function below addPropertyIfValueIsNotUndefined appears to be functioning:

function addPropertyIfValueIsNotUndefined<Value>(key: string, value: Value): { key?: Value; } {
    return isNotUndefined(value) ? { [key]: value } : {};
}

function isNotUndefined<TargetValue>(targetValue: TargetValue | undefined): targetValue is TargetValue {
  return typeof targetValue !== "undefined";
}


type Test = {
    alpha: string;
    bravo?: string; 
}

const test1: Test = { 
    alpha: "ALPHA1" ,
    // bravo: "BRAVO1"
};

const test2: Test = {
    alpha: "ALPHA2",
    ...addPropertyIfValueIsNotUndefined("bravo", test1.bravo)
};

console.log(test2)

// No errors or warnings from TypeScript compiler or JavaScript VM up to this point

However, what happens when attempting to add a property that is not declared in the Test type? I anticipated an error message like

TS2322 Type { gibberish: string; bravo: string | undefined; alpha: string; }' is not assignable to type 'Test'
, but no error or warning is emitted by TypeScript!

const test2: Type = {
    alpha: "ALPHA2",
    ...addPropertyIfValueIsNotUndefined("gibberish", test1.bravo)
};

I believe I understand why this happens. But how can we annotate the return value of addPropertyIfValueIsNotUndefined? We don't know the key in advance but still need to reference it.

🌎 Fiddle

Answer â„–1

Here is an updated method that I believe achieves your objective. Instead of creating a separate function to generate the {key: value} object, we now pass the existing object into the function.

function addPropertyIfValueIsNotUndefined<T extends {}, K extends keyof T>(
   obj: T, key: K, val: Required<T>[K]
): T & Required<Pick<T, K>> {
    return checkIfValueUndefined(obj, key) ? obj : { ...obj, [key]: val } as T & Required<Pick<T, K>>
}

It is important to use as when returning the correct type due to how typescript handles dynamic keys. Additionally, the generic K could potentially encompass more than just a single key, which may impact our return type.

The boolean checker now inspects the property on the object:

function checkIfValueUndefined<T extends {}, K extends keyof T>(o: T, k: K): o is T & Required<Pick<T, K>> {
  return o[k] !== undefined;
}

Implementation would look like this:

const example2 = addPropertyIfValueIsNotUndefined(example1, "bravo", "value") // valid

const example3 = addPropertyIfValueIsNotUndefined(example1, "bravo", undefined) 
// error: Argument of type 'undefined' is not assignable to parameter of type 'string'

const example4 = addPropertyIfValueIsNotUndefined(example1, "wrongKey", "value") 
// error: Argument of type '"wrongKey"' is not assignable to parameter of type '"bravo" | "alpha"'

Typescript Playground Link

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

Clicking on the "Select All" option in the angular2-multiselect-dropdown triggers the onDeSelectAll event

I'm facing an issue with angular2-multiselect-dropdown where the "SelectAll" functionality is not working correctly. When I click on "SelectAll", it also triggers the deSelectAll event, causing the onDeSelectAll() function to be called and preventing ...

The utilization of functions from a implemented interface results in the generation of a 'non-function' error

I recently created an interface that includes variables and a function. However, I encountered an issue when trying to utilize the implemented function for a specific class as it resulted in an 'ERROR TypeError: ...getPrice is not a function" Below ...

Observing rxjs Observable - loop through the results and exit when a certain condition is met / encountering an issue with reading property 'subscribe' of an undefined value

I'm fairly new to working with rxjs and I've been struggling to find the right operator for what I want to achieve. Here's my scenario - I have an array that needs to be populated with results from another observable. Once this array has en ...

Redis is prepared and awaiting authorization for authentication. ReplyError: NOAUTH Authentication needed

When attempting to connect to a Redis instance hosted on redislab, an error message is received indicating Redis ready ReplyError: NOAUTH Authentication required. const pubClient = createClient({ url: `${config.redisLabHost}:${config.redisLabPort}` }); pub ...

Creating an Angular table using reactive forms: a step-by-step guide

After reviewing the HTML snippet provided below, it is evident that there is a table with looping through mat cell using *matCellDef="let model". Inside each cell, there are input fields which are reactive forms. Each row or cell needs to have it ...

Prevent a specific directory from being compiled in MSBuild using Typescript

I have a unique ASP.NET MVC / Angular2 project that utilizes MSBuild for compiling my Typescript files. Within the project, there is the complete Angular2 source code along with its NodeJS dependencies, in addition to my own Angular2 code (app.ts, etc.). ...

Guide on integrating an element into a different element in a Vue 3 Tree Viewer

In my current setup, I've implemented a TreeView component that holds a tree. Each tree entry includes Children with their own unique label, perm, and further children. Take a look at an example of the tree: App.vue let tree = ref({ label: 'o ...

Angular Form: displaying multiple hashtags within an input field

Utilizing Angular CLI and Angular Material, I have created a form to input new hashtags. I am facing difficulty in displaying previously added hashtags in the input field. Below is the code I have written: form.component.html <form [formGroup]="crea ...

"Learn how to dynamically update a value based on the selected option in a drop-down menu in Angular

Hello everyone. I am working on an angular project that includes a product page. Some products on this page can have multiple types, like so: Product ID-1 Type ID-1 Price-$10 Product ID-1 Type ID-2 Price-$15 Product ID-1 Type ID-3 Price-$25 In this sce ...

extracting the value of an option from a form control to utilize in the component's model

I'm currently facing an issue where I am unable to retrieve the value of an option selection in my component class for further processing. Despite setting the value as [value]="unit" in the view, it still shows up as undefined when passed through onMo ...

Enhance your UI experience with a beautifully styled button using Material-

I was using a Material UI button with a purple background. <Button component={Link} to={link} style={{ background: '#6c74cc', borderRadius: 3, border: 0, color: 'white', heig ...

Prevent redundancy by caching svg icons to minimize repeated requests

I have a collection of info cards on my page, each featuring its own unique illustration along with a set of common icons (SVG) for options such as edit, delete, and more. While the illustrations vary from card to card, the icons remain consistent across a ...

Tips for establishing optimal parameters for an object's dynamic property

I am working with an array of objects: export const inputsArray: InputAttributes[] = [ { label: 'Name', type: 'text', name: 'name', required: true }, { label: 'User name ...

Obtaining Header or Footer information from an XLSX file using Angular

CLICK HERE FOR XLSX HEADER IMAGE I am struggling to retrieve the header and footer data from an xlsx file. I have only managed to access the information inside the workbook so far. I attempted to extract the header/footer using the following method, but ...

Angular 6 Calendar Template issues with parsing: Unable to link to 'view' as it is not recognized as a valid property of 'div'

I am in the process of developing an application that utilizes this angular calendar. My tech stack includes Angular 6 with AngularFire2 and Firebase. Below is my app.module.ts file: import { BrowserModule } from '@angular/platform-browser'; imp ...

Filtering a key-value pair from an array of objects using Typescript

I am working with an array of objects containing elements such as position, name, and weight. const elements = [{ position: 3, name: "Lithium", weight: 6.941, ... },{ position: 5, name: "Boron", weight: 10.811, ... }, { position: 6, name: "Carbon", weight: ...

The Nestjs ClientMqtt now has the capability to publish both pattern and data to the broker, as opposed to just sending

I am currently utilizing Nestjs for sending data to a Mqtt Broker. However, I am facing an issue where it sends both the pattern and data instead of just the data in this format: { "pattern": "test/test", "data": " ...

Utilize Dinero.js to implement currency formatting for input fields in React applications

I am currently working on a form in React that requires certain input fields to be formatted as money currency. These inputs should be prefixed with the dollar sign ($), include commas for thousands separation, and have exactly two decimal points. During ...

Looping Angular Components are executed

I am currently developing an Angular application and encountering an issue with my navbar getting looped. The problem arises when I navigate to the /home route, causing the navbar.component.html components to duplicate and appear stacked on top of each oth ...

Having some issues with ng-hide in angular, it doesn't seem to be functioning properly

<nav class="menu-nav"> <ul> <li class="menu-li" ng-model="myVar"><a>Discover<i class="fa fa-chevron-down pull-right"></i></a> <div class="sub-menu" ng-hide="myVar"&g ...