【问题标题】:Typescript index signature and methods打字稿索引签名和方法
【发布时间】:2019-03-07 23:05:04
【问题描述】:

为什么下面的代码会出现ts(2411) 错误?

class Greeter {
    [key: string]: string | number[];
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet(): string {
        return "Hello, " + this.greeting;
    }
}

错误出现在greet(),上面写着type '() => string' not assignable to type 'string | number[]'

如果我将object 添加到索引签名中,错误就会消失。这是为什么呢?

同样对于索引签名,是否使用any不好的做法?

编辑:我还在签名中添加了接口Function。它也有效。不过,问题是为什么。

【问题讨论】:

    标签: typescript


    【解决方案1】:

    在具有索引签名的类或接口中声明的任何属性或方法的类型必须与您在索引中的类型兼容。这就是为什么在索引签名中添加Function 会有所帮助。

    原因是explained in the documentation:

    虽然字符串索引签名是描述 “字典”模式,它们还强制所有属性匹配 他们的返回类型。这是因为字符串索引声明 obj.property 也可用作 obj["property"]。在下面的 例如,名称的类型与字符串索引的类型不匹配,并且 类型检查器给出错误:

    interface NumberDictionary {
        [index: string]: number;
        length: number;    // ok, length is a number
        name: string;      // error, the type of 'name' is not a subtype of the indexer
    }
    

    any 添加到索引器签名可能被认为是一种不好的做法, 因为any 禁止类型检查,并且从any 以任何方式获得的任何值也具有any 类型,除非另有明确声明,因此any 会增加您遇到编译器未报告的类型错误的机会。

    在类型中添加Function 更好,因为它正确地描述了类中包含的实际数据——当您使用索引访问来获取这样的值时

      const key = 'greeting';
      const value = this[key];
    

    如果key 恰好等于'greet',您可能会得到一个函数作为值。此外,当您将字符串值分配给 greet:

      this['greet'] = 'hi!';
    

    该方法将被字符串值覆盖,您将无法再调用它。

    考虑到所有这些,最好将带有索引签名的字典保存在类的单独属性中,而不是类本身中。像这样的东西可以工作:

    class Greeter {
        data: { [key: string]: string | number[] } = {};
    
        get greeting(): string { return this.data.greeting.toString() }
        set greeting(value: string) { this.data.greeting = value };
    
        constructor(message: string) {
            this.greeting = message;
        }
        greet(): string {
            return "Hello, " + this.greeting;
        }
    }
    

    【讨论】:

    • 如何评估方法?我的意思是为什么objectFunction 都解决了这个问题?
    • 因为Functionobject的子类型,所以添加object也使得方法类型与索引签名兼容。
    【解决方案2】:

    有一种解决方法可以让您实现这一目标。 就我而言,我想要一个具有索引签名的类加上一个自定义设置器和一个返回所有值的属性all(这样我在使用该类时可以避免丑陋的Object.values)。

    这是一个例子。

    interface IAccessor<T> {
        [key: string]: T;
    }
    
    interface IGetter<T> {
        all: T[]
    }
    
    class Accessor<T> {
    
        get all(): T[] {
            return Object.values<T>(this);
        }
    
        [key: string]: T | any; // any will do the trick, but it's not enough
    }
    
    const accessor: IAccessor<string> & IGetter<string> = new Accessor<string>();
    accessor.first = "a";
    accessor.second = "b";
    accessor.third = "c";
    console.log(accessor.all); // ["a", "b", "c"]
    

    Plaground link

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-09-05
      • 1970-01-01
      • 1970-01-01
      • 2018-04-19
      • 2020-06-27
      • 2020-02-19
      • 2018-12-15
      • 2019-11-14
      相关资源
      最近更新 更多