0

I'm new to TypeScript and I'm trying to port some of my custom components/plugins to TS.

One of the things I just can't seem to get right is setting (typed) object properties dynamically (i.e. when the property name is a variable).

A best-practice solution/pattern for this would really help me along.

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];
                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                                ~~~~~~
                          
            } 
        }

        /* ... */
    }

    /* ... */
}

Thanks!

2 Answers 2

1

In a nutshell, just let TSC know that your Option type will have dynamic properties.

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

Then you can continue assigning the values dynamically

this.options[option] = value;

Your issue is quite similar to the one described here

Sign up to request clarification or add additional context in comments.

Comments

1

I can't speak to whether or not this is a best practice, but this approach seems sensible to me. I'd amend the Options interface by another property genericOptions:

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

This property is any old type that can be indexed by a string to return a string.

And your constructor now looks like this:

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

        // Set default options, override with provided ones
        this.options = {
            repeat: true,
            speed: 0.5,
            // Just an empty object by default, or you could make the `genericOptions`
            // optional, depending on preference.
            genericOptions: {},
            ...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)

                // Set whatever generic options you need to.
                this.options.genericOptions[option] = value;
            }
        }
         /* ... */
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.