【问题标题】:Keyof inferring string | number when key is only a stringKeyof 推断字符串 |键只是字符串时的数字
【发布时间】:2019-01-19 08:20:24
【问题描述】:

我这样定义AbstractModel

export interface AbstractModel {
   [key: string]: any
}

然后我声明类型Keys

export type Keys = keyof AbstractModel;

我希望任何具有 Keys 类型的东西都会被明确地解释为字符串,例如:

const test: Keys;
test.toLowercase(); // Error: Property 'toLowerCase' does not exist on type 'string | number'. Property 'toLowerCase' does not exist on type 'number'.

这是 Typescript (2.9.2) 的错误,还是我遗漏了什么?

【问题讨论】:

    标签: typescript typescript-typings


    【解决方案1】:

    根据 TypeScript 2.9 的发行说明中的​​定义,如果您对带有字符串索引签名的接口进行 keyof,它将返回字符串和数字的联合

    给定一个对象类型 X,keyof X 的解析如下:

    如果 X 包含字符串索引签名,则 keyof X 是字符串、数字和表示类符号属性的文字类型的并集,否则

    如果 X 包含数字索引签名,则 keyof X 是数字和表示类字符串和类符号属性的文字类型的并集,否则

    keyof X 是表示类字符串、类数字和类符号属性的文字类型的联合。

    source

    这是因为:JavaScript 在索引对象时会将数字转换为字符串:

    [..] 使用数字索引时,JavaScript 实际上会在索引到对象之前将其转换为字符串。这意味着使用 100(一个数字)进行索引与使用“100”(一个字符串)进行索引是一回事,因此两者需要保持一致。

    source

    例子:

    let abc: AbstractModel = {
        1: "one",
    };
    
    console.log(abc[1] === abc["1"]); // true
    

    当您只需要字符串键时,您只能从界面中提取字符串键,如下所示:

    type StringKeys = Extract<keyof AbstractModel, string>;
    
    const test: StringKeys;
    test.toLowerCase(); // no error
    

    TypeScript 编译器还提供了一个选项来获取 keyof 的 2.9 之前的行为:

    keyofStringsOnly (boolean) 默认false

    仅将keyof 解析为字符串值的属性名称(无数字或符号)。

    source

    【讨论】:

      【解决方案2】:

      我也遇到过类似的问题。我通过强制 key 为字符串来解决它:

      export type Keys = keyof AbstractModel & string;
      

      其他选项是将密钥转换为字符串:test.toString().toLowercase()

      【讨论】:

      • 非常有用,非常适合全局设置 keyofStringsOnly: true 选项。
      • 嗯...那么为什么这不适用于type PickNumericProperties&lt;T&gt; = Pick&lt;T, keyof {[k: keyof T]: number}&gt;
      • 这和Extract&lt;keyof AbstractModel, string&gt;有什么实际区别吗??
      【解决方案3】:

      对于通用打字稿实用程序,您可以使用以下内容:

      type KeyOf<T extends object> = Extract<keyof T, string>;
      

      用法:

      const sym = Symbol();
      
      const obj = {
        [sym]: true,
        foo: 'foobar',
        bar: 'barfoo',
        1: 'lorem'
      }
      
      let key: KeyOf<typeof obj> = 'foo'; // 'foo' | 'bar'
      
      key = 'bar'; // ok
      key = 'fool'; // error
      key = 1; // error
      

      playground

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-10-15
        • 1970-01-01
        • 2017-02-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多