Modifying the values of various data types within a function

Is there a more refined approach to enhancing updateWidget() in order to address the warning in the else scenario?

type Widget = {
    name: string;
    quantity: number;
    properties: Record<string,any>
}

const widget: Widget = {
    name: "value",
    quantity: 1,
    properties: {name: "property1", name: "property2"}
}

function updateWidget(key: string|number, value: string|number, property:boolean) {
    if(property) {
        widget.properties[key] = value;
    } else {
        // TODO: Find an elegant way to handle this case
    }
    return widget;
}

Error:

Element implicitly has an 'any' type because expression of type 'string | number' can't be used to index type 'Widget'. No index signature with a parameter of type 'string' was found on type 'Widget'.(7053)

Answer №1

Transform your function into a versatile one where the key and value types can be dynamically matched :

function updateWidget<K extends keyof Widget>(key: K, value: Widget[K], property:boolean) {
    if(property) {
        widget.properties[key] = value;
    } else {
        widget[key] = value;
    }
    return widget;
}

Interactive Demo

Answer №2

If you want to achieve strict typing on overload definitions and looser types on the implementation signature, consider using function overloads combined with generics. This way, you can maintain good type safety even without passing in the object being updated as an argument. However, keep in mind that altering the signature for the implementation won't be possible without casting it to a looser type.

Here's an example of how this approach could work:

function updateWidget<K extends keyof Widget>(
  key: K,
  value: Widget[K],
  property: false,
): Widget;
function updateWidget<K extends keyof Widget["properties"]>(
  key: K,
  value: Widget["properties"],
  property: true,
): Widget;
function updateWidget(
  key: string,
  value: unknown,
  property: boolean,
) {
  if (property) {
    widget.properties[key] = value;
  } else {
    (widget as Record<string,unknown>)[key] = value;
  }
  return widget;
}

Using a flag like 'property' to determine which properties to access may not be the most straightforward solution, and managing the typings can become cumbersome.

Instead, consider a generic update object function like this:

type Obj = Record<PropertyKey, unknown>;

const update =
  <T extends Obj>(obj: T) =>
  <K extends keyof T>(key: K, value: T[K] | ((current: T[K]) => T[K])) => {
    const current = obj[key];
    if (value instanceof Function) {
      return { ...obj, [key]: value(current) };
    }
    return { ...obj, [key]: value };
  };

You can then call this function like so:

const updated = update(widget)('properties',(p)=>({...p, 'any':5}))

This method is versatile and can be used for other objects as well. You can chain updates like this:

const next = update(widget)("properties", (p) => update(p)("field", "value"));

For further experimentation, check out the playground.

Answer №3

function updateWidget<K extends keyof Widget>(key: K, value: Widget[K], property?:false): Widget;
function updateWidget(key: string, value: string|number, property:true): Widget;
function updateWidget(key: string|number, value: string|number, property:boolean) {
    if(property) {
        widget.properties[key] = value;
    } else {
        widget[key as keyof Widget] = value;
    }
    return widget;
}

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

TypeError: Unable to find TextEncoder in mongoose and jest when using TypeScript

Currently, I am working on a project using Node 14 along with Express v4.16.3 and Typescript (v4.7.4). Recently, I added Mongoose (v6.5.2) to the project, and while the logic code seems fine, most of the tests executed by Jest (v26.4.2) are failing with th ...

Using injected services within static methods may seem tricky at first, but once you

I am exploring the integration of angularjs and typescript in my project. Currently, I am working on creating an Orm factory using typescript but have encountered some challenges. The structure of my factory class is as follows: class OrmModel implements ...

Encountering 'no overload matches this call' while developing a useReducer using React with Typescript

import { useCallback, useReducer } from "react"; const ARTIST_REDUCER_TYPES = { ADD: "ADD", }; const artistArray = [...Object.values(ARTIST_REDUCER_TYPES)] as const; type ActionReturnedTypes = (typeof artistArray)[number]; type Re ...

Navigating through elements in Angular

I am working with multiple Angular components housed within a display:flex div container. I am fetching datatable from an API, and it contains the same number of rows as there are components. Each row in the datatable corresponds to data for each compone ...

How to retrieve the value of a variable accessible to all users in Angular?

In my code, I am working with a service variable called Users. Service: Users:BehaviorSubject<Array<any>> = new BehaviorSubject([]); I am updating the values in the component using this code: this.Service.Users.next([...this.Service.User ...

By utilizing custom typeRoots while continuing to export these types alongside the entry point

For my project setup, I employ rollup to bundle an associated index.d.ts (and index.js) for the entrypoint src/index.ts. Within the project structure, there exists a directory named src/types containing multiple .d.ts files. These types are globally acces ...

Build an object using a deeply nested JSON structure

I am working with a JSON object received from my server in Angular and I want to create a custom object based on this data. { "showsHall": [ { "movies": [ "5b428ceb9d5b8e4228d14225", "5b428d229d5b8e4 ...

Issue with arrow function not being invoked in a React TypeScript component's prop inside a function

My parent component holds a useState hook to determine if the mobile Nav is open or closed: const [showMobileMenu,setShowMobileMenu] = useState<boolean>(false);. To close the mobile menu, I created an arrow function and passed it down to a child comp ...

Testing React Hooks in TypeScript with Jest and @testing-library/react-hooks

I have a unique custom hook designed to manage the toggling of a product id using a boolean value and toggle function as returns. As I attempt to write a unit test for it following a non-typescripted example, I encountered type-mismatch errors that I' ...

A TypeScript object with user-defined keys

I have a question about utilizing TypeScript records with a custom Type as keys. Essentially, I have a specific type (a limited set of strings) that I want to use as keys for my record. My goal is to start with an empty initialization of this record. type ...

Informing typescript that an argument is specifically an array when accepting both a single string and an array of strings

How can I inform TypeScript that the code is functionally valid? It keeps suggesting it could be a string, but I am unsure how that would happen. Is this a bug in my code or am I inputting something wrong? For example: const i18nInstance = { options ...

Guide to summing the values in an input box with TypeScript

https://i.stack.imgur.com/ezzVQ.png I am trying to calculate the total value of apple, orange, and mango and display it. Below is the code I have attempted: <div class="row col-12 " ngModelGroup="cntMap"> <div class="form-group col-6"> ...

Having trouble retrieving information from combineLatest in Angular?

I'm having some trouble with fetching files to include in the post logs. It seems that the data is not being passed down the chain correctly when I attempt to use the pipe function after combining the latest data. This code snippet is part of a data r ...

"Prisma vs. Supabase: A Comparison of Image Uploading

I am encountering an issue with adding image URLs to the Prisma database. I have successfully uploaded multiple images from an input file to Supabase storage, but when I try to add their URLs to the database, I receive an error regarding property compatibi ...

Using react-confetti to create numerous confetti effects simultaneously on a single webpage

I'm looking to showcase multiple confetti effects using the react-confetti library on a single page. However, every attempt to do so in my component seems to only display the confetti effect on the last element, rather than all of them. The canvas fo ...

Typescript is struggling to accurately infer extended types in some cases

My goal is to optimize the body of a NextApiRequest for TypeScript. I currently have this code snippet: // This is a type from a library I've imported export interface NextApiRequest { query: Partial<{ [key: string]: string | string[]; ...

A guide to using Angular to emphasize text based on specific conditions met

Currently, I am developing a testing application that requires users to choose radio type values for each question. The goal is to compare the selected answers with the correct answers stored in a JSON file. To achieve this, I have implemented an if-else ...

Is there any distinction between using glob wildcards in the tsconfig.json file when specifying "include" as "src" versus "include" as "src/**/*"?

Is there a distinction between these two entries in the tsconfig.json file? "include": ["src"] "include": ["src/**/*"] Most examples I've come across use the second version, but upon reviewing my repository, ...

Having trouble reloading a seekbar (input range) in Angular 7 with a function?

I am currently in the process of developing a music player using Angular 7, and below is the HTML code for my component: <div class="track-controller"> <small>{{musicPlayerService.getCurrentTime()}}</small> <div class="progress- ...

Exploring Typescript keyof in Storybook's User Interface customizations

Currently, I am working on developing components for integration with Storybook, but I am encountering an issue related to Typescript inferred types. While striving for code reusability, I prefer not to specify the options for a control within the story i ...