4

How should I address the following error message:

Failed to compile /.../SoftwareLicenseCodes/index.tsx (14,20): Type declaration of 'any' loses type-safety. Consider replacing it with a more precise type. This error occurred during the build time and cannot be dismissed.

See the following code:

import * as React from 'react';
import './SoftwareLicenseCodes.css';

interface SoftwareLicenseCodesProps {
}

interface SoftwareLicenseCodesState {
    count: string;
    oneTimeUsage: boolean;
    duration: string;
    validFrom: string;
    validTo: string;
    distributor: string;
    [key: string]: any;
}

class SoftwareLicenseCodes extends React.Component<SoftwareLicenseCodesProps, SoftwareLicenseCodesState> {
    constructor(props: SoftwareLicenseCodesProps) {
        super(props);

        this.state = {
            distributor: '',
            count:'',
            oneTimeUsage: false,
            duration: '',
            validFrom: '',
            validTo: ''
        };

        this.onInputChange = this.onInputChange.bind(this);
    }

    handleSubmit(event: React.FormEvent<HTMLFormElement>) {
        alert('submit');
        event.preventDefault();
    }

    onInputChange = (event: React.FormEvent<HTMLInputElement>) => {
        const value = event.currentTarget.type === 'checkbox' ? event.currentTarget.checked : event.currentTarget.value;

        this.setState({
            [name]: value
        });
    }

    render() {
        return (
            <div className="user-container software-codes">
                <div className="user-single-container">
                    <h1>Software License Codes</h1>

                    <form className="software-codes__form" onSubmit={this.handleSubmit}>
                        <label>
                            <span className="software-codes__input-element">Count</span>
                            <input
                                name="count"
                                type="number"
                                value={this.state.count}
                            />
                        </label>

                        <label>
                            <span className="software-codes__input-element">Distributor</span>
                            <input
                                name="distributor"
                                type="text"
                                value={this.state.distributor}
                            />
                        </label>

                        <label>
                            <span className="software-codes__input-element">One time usage</span>
                            <input
                                name="oneTimeUsage"
                                type="checkbox"
                                checked={this.state.oneTimeUsage}
                            />
                        </label>

                        <label>
                            <span className="software-codes__input-element">Duration</span>
                            <input
                                name="duration"
                                type="number"
                                value={this.state.duration}
                            />
                        </label>
                        <input className="software-codes__input-element" type="submit" value="Submit" />
                    </form>
                </div>
            </div>
        );
    }
}

export default SoftwareLicenseCodes;
veben
  • 19,637
  • 14
  • 60
  • 80
user1283776
  • 19,640
  • 49
  • 136
  • 276
  • How the `name` variable is set inside the `onInputChange` function before being used as property name? I can't see any assignment to it... – Cerberus Sep 04 '18 at 09:06

3 Answers3

5

Your code only sets either string or boolean values, so you could lock it down a bit more:

interface SoftwareLicenseCodesState {
    count: string;
    oneTimeUsage: boolean;
    duration: string;
    validFrom: string;
    validTo: string;
    distributor: string;
    [key: string]: string|boolean;
    // ------------^^^^^^^^^^^^^^
}

Alternately, if you want to have full type safety, you could remove the string index signature and write extra code that switches on the name of the input and then uses an explicit property name. That maximizes your use of type-checking, while (obviously) increasing code size/complexity:

function setNamed(target: SoftwareLicenseCodesState, name: string, value: string|boolean): SoftwareLicenseCodesState {
    if (name === "oneTimeUsage") {
        // Probably add assertion here that value is a boolean
        target.oneTimeUsage = value as boolean;
    } else {
        // Probably add assertion here that value is a string
        const strValue = value as string;
        switch (name) {
            case "count":
                target.count = strValue;
                break;
            case "duration":
                target.duration = strValue;
                break;
            case "validFrom":
                target.validFrom = strValue;
                break;
            case "validTo":
                target.validTo = strValue;
                break;
            case "distributor":
                target.distributor = strValue;
                break;
            default:
                // Failed assertion here
        }
    }
    return target;
}

Then

this.setState(setNamed({}, name, value));

Clumsy as all get-out, but maximizes type-checking.

I really want to find a way for you to use index types, but with the name coming from the name property of an input element, I can't see how to do that without the switch above. Which bothers me, because I seem to recall some uber-clever way of using keyof to build a union type for the name...

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
1

You can maybe disable this TSLint rule , though I don't know how safe it is :

interface SoftwareLicenseCodesState {
  count: string;
  oneTimeUsage: boolean;
  duration: string;
  validFrom: string;
  validTo: string;
  distributor: string;
  // tslint:disable-next-line: no-any
  [key: string]: any;
}
sagi
  • 40,026
  • 6
  • 59
  • 84
1

Sometimes you need to use any. For example when you override the intercept() method of HttpInterceptor class: https://angular.io/api/common/http/HttpInterceptor

I personnaly disable this rule. To do that, go into you tslint.json file and comment this line:

// "no-any": true,
veben
  • 19,637
  • 14
  • 60
  • 80