Using TypeScript to dynamically assign types to object properties

As a newcomer to TypeScript, I am in the process of migrating some of my custom components/plugins to TS.

One of the challenges I'm facing is setting object properties dynamically when the property name is a variable.

I would greatly appreciate a best-practice solution or pattern to help me with this.

Here is my code:

interface Options {
    repeat: boolean;
    speed: number;
}

class MyPlugIn {
    $el:HTMLElement;
    options:Options;

    constructor ($el:HTMLElement, options:Partial<Options> = {}) {
        this.$el = $el;

        // Set default options, override with provided ones
        this.options = {
            repeat: true,
            speed: 0.5,
            ...options
        };

        // Set options from eponymous data-* attributes
        for (const option in this.options) {
            if (this.$el.dataset[option] !== undefined) {
                let value: any = this.$el.dataset[option];

                // Cast numeric strings to numbers
                value = isNaN(value) ? value : +value;

                // Cast 'true' and 'false' strings to booleans
                value = (value === 'true') ? true : ((value === 'false') ? false : value)

                // Attempt 1:
                // ERROR: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Options'.
                this.options[option] = value;
                ~~~~~~~~~~~~~~~~~~~~

                // Attempt 2 (with assertions):
                // ERROR (left-hand): Type 'string' is not assignable to type 'never'
                // ERROR (right-hand): Type 'option' cannot be used as an index type.
                this.options[option as keyof Options] = value as typeof this.options[option];
                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                                ~~~~~~
                          
            } 
        }

        /* ... */
    }

    /* ... */
}

Appreciate any help!

Answer №1

To simplify, inform TSC that your Preference data type will include changing attributes.

interface Preferences {
    [key: string]: unknown;
    theme: string;
    font_size: number;
}

From there, you can dynamically set the values as needed

this.preferences[setting] = value;

Your situation bears resemblance to the issue discussed here

Answer №2

While I cannot guarantee that this method is considered a best practice, I find this approach to be quite logical. My suggestion would be to enhance the Options interface by introducing an additional property named genericOptions:

interface Options {
    repeat: boolean;
    speed: number;
    genericOptions: { [key: string]: string };
}

This particular property can be of any type that can be accessed via a string to retrieve a string value.

Your constructor function would now resemble the following:

constructor ($el:HTMLElement, options:Partial<Options> = {}) {
        this.$el = $el;

        // Default options are set first, and then any provided options will override them
        this.options = {
            repeat: true,
            speed: 0.5,
            // Initially an empty object, but this can be adjusted based on personal choice
            genericOptions: {},
            ...options
        };

        // Extract options from corresponding data-* attributes
        for (const option in this.options) {
            if (this.$el.dataset[option] !== undefined) {
                let value: any = this.$el.dataset[option];

                // Convert numeric strings to numbers
                value = isNaN(value) ? value : +value;

                // Convert 'true' and 'false' strings to booleans
                value = (value === 'true') ? true : ((value === 'false') ? false : value)

                // Define any required generic options here.
                this.options.genericOptions[option] = value;
            }
        }
         /* ... */
    }

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

A method for arranging an array of nested objects based on the objects' names

Recently, I received a complex object from an API: let curr = { "base_currency_code": "EUR", "base_currency_name": "Euro", "amount": "10.0000", "updated_date": "2024 ...

Refresh a function following modifications to an array (such as exchanging values)

Looking to re-render a function after swapping array values, but the useEffect hook is not triggering it. I need assistance with this as I plan to integrate this code into my main project. Below are the JSX and CSS files attached. In App.js, I am creating ...

Discovering the technique to unearth a specific value within an array nested within another array

I am encountering an issue with finding a value in one array inside another array and utilizing the resulting value to update the state using setState(). Here is the initial state: this.state = { initialStudents:[ {name:"str1",tags;["str","s ...

What is the recommended approach in Angular for unassigned @Input() variables when using TypeScript strict mode?

Issue at Hand After upgrading my Angular version from 10 to 13, I encountered a problem with the new TypeScript strict compiler mode. The upgrade required me to assign values to all declared variables upon initialization, causing issues with properties de ...

The input value does not update in the controller when using AngularJS ng-model

Why is it that when I print out console.log($scope.inputvalue), the variable does not update with the values I enter in the input field? Did I misunderstand the purpose of ng-model? If so, how can I pass a value from the view to the controller? (functi ...

Including a JavaScript file in an HTML document initiates a server request, which can lead to potential errors

I am currently developing a web application using Express and Node.js on the backend, with EJS as the templating engine on the frontend. Here is a snippet of my app.js file: app.get('/book/:id', (req, res)=>{ var book_id = req.params.id; cons ...

Webpack is throwing an error due to the Vue component type being labeled as "any"

When using ts 4.2.4 and Vue3, I encountered a strange error while building my vue project: > <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="c3a2a7aeaaededb3a2a0a083f3edf2edf3">[email protected]</a> build > v ...

What is the best way to create a dynamic sitemap in Next.js version 14?

I've encountered an issue with the code snippet I'm using for a dynamic sitemap on my blog post website built with Next.js 14. While it works perfectly fine in development, it fails to generate a dynamic sitemap in the build or production environ ...

Organize intricate JavaScript Arrays

Similar Question: How to sort an array of javascript objects? I'm in the process of transitioning some of my older ActionScript 2.0 code to JavaScript. While most things are running smoothly, I've hit a roadblock when trying to numerically s ...

Efficiently select multiple classes in Material UI with just one target

I'm currently working with Material UI and I want to update the color of my icon class when the active class is triggered by react-router-dom's NavLink Here is my code: import React from "react"; import { makeStyles } from "@mater ...

Challenges encountered when redirecting users with a combination of javascript and php

I have a login form that triggers a JavaScript function upon submission. This function calls a PHP page to process the input. The issue I'm facing is with how the redirections are displayed based on the user's role type. It attempts to display t ...

What is the best way to set up the login page and logged-in homepage using Node, Express, and Passport with the "/" route?

I am currently in the process of developing an application using Node.js, Express.js, and Passport.js. My goal is to create a seamless user experience on the homepage where if you are not logged in, you will see a login form (with potential for additional ...

Unable to fetch data from MongoDB in Node.js when using objectid

After passing objectid of hospital 1 from Postman to this program, it only returns an empty array. However, there is data that matches that objectid. Can you assist me in resolving this issue? When attempting to debug the program in the console, it shows t ...

Tips on implementing a script injected through the JS console to update form data in an Angular webpage and ensure that the changes are successfully saved

I am currently working on enhancing an existing electron app integrated with Angular. The main goal is to inject a script once the application is loaded in order to add a hotkey for efficiency. This hotkey should automatically click an edit button, input s ...

What is the best way to divide an array while extracting data from a JSON object using

Currently, I am parsing the json data. My goal is to find a specific property within the json object that contains two nested arrays (list and array). However, when extracting the values, they are all being stored in a single array. Is there a way to separ ...

Retrieving a targeted data point from a JSON object

I am working with a json data that contains various properties, but I am only interested in extracting the uniqueIDs. Is there a way to retrieve ONLY the uniqueID values and have them returned to me as a comma separated list, for example: 11111, 22222? (I ...

Climbing the ladder of function chains, promises are making their

Here is the code structure that I've created to upload multiple files to a server using AJAX. After all the uploads are complete, it should perform a certain action. function uploadFiles(files){ const results = [] for (let i=0; i<files.length; i ...

React hooks eliminating unnecessary rendering

Having recently delved into React and hooks, I'm facing an issue with refreshing the list of files in my app after clicking on the convert button. The correct file only shows up if I manually refresh the page. The React part of the code involves uplo ...

How can I take a screenshot from the client side and save it on the server side using PHP?

Currently, I am exploring the possibility of screen capturing at the client side. It appears that the "imagegrabscreen()" function can only capture screens on the server side. After some research, I discovered a new function that allows for screen capture ...

Is it possible to generate a Token in Nexus for private packages without using the UI interface?

We have implemented Sonatype Nexus Repository ManagerOSS 3.29.0-02 and are currently facing an issue in generating a TOKEN that can be used with .npmrc following this specific structure: registry=http://NEXUS-IP:8081/repository/GROUP-NAME http://NEXUS-IP:8 ...