Keyof推断字符串|当键只是一个字符串时的数字

时间:2018-08-12 11:02:19

标签: typescript typescript-typings

我这样定义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)的错误,还是我缺少某些东西?

3 个答案:

答案 0 :(得分:13)

如TypeScript 2.9发行说明中所定义,如果您对具有字符串索引签名的接口进行键控,则会返回字符串和数字的并集

  

给出对象类型X,X的键解析如下:

     

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

     

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

     

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(布尔值)默认false

     

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

source

答案 1 :(得分: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

答案 2 :(得分:2)

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

export type Keys = keyof AbstractModel & string;

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