TypeScript编译失败,因为“any”类型声明失去了类型安全性。

4

如何处理以下错误信息:

编译失败 /.../SoftwareLicenseCodes/index.tsx (14,20): 'any' 的类型声明丢失类型安全性。考虑将其替换为更精确的类型。此错误发生在构建时,并且无法被取消。

请查看以下代码:

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;

onInputChange函数内部如何设置name变量,然后再将其用作属性名?我没有看到任何对它的赋值... - Cerberus
3个回答

5

你的代码仅设置字符串或布尔值,因此你可以进一步加强其安全性:

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

另一方面,如果您希望具有完整的类型安全性,您可以删除字符串索引签名,并编写额外的代码来根据输入的名称进行选择,然后使用显式属性名称。 这最大化了您对类型检查的使用,但(显然)增加了代码大小/复杂性:

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

那么

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

虽然有些笨拙,但最大化了类型检查。

我真的想找到一种方法让您使用索引类型,但是由于名称来自元素的name属性,我无法想象在没有上述switch的情况下如何做到这一点。这让我感到困扰,因为我似乎记得有一种超级聪明的方法可以使用keyof来构建名称的联合类型...


1
您可以尝试禁用此TSLint规则,但我不知道它是否安全。
interface SoftwareLicenseCodesState {
  count: string;
  oneTimeUsage: boolean;
  duration: string;
  validFrom: string;
  validTo: string;
  distributor: string;
  // tslint:disable-next-line: no-any
  [key: string]: any;
}

1
有时候你需要使用any。例如,当你重写HttpInterceptor类的intercept()方法时:https://angular.io/api/common/http/HttpInterceptor 我个人会禁用这条规则。要做到这一点,请进入你的tslint.json文件并注释掉这行代码:
// "no-any": true,

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接