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

Posted

技术标签:

【中文标题】Keyof 推断字符串 |键只是字符串时的数字【英文标题】:Keyof inferring string | number when key is only a string 【发布时间】: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) 的错误,还是我遗漏了什么?

【问题讨论】:

【参考方案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

【讨论】:

以上是关于Keyof 推断字符串 |键只是字符串时的数字的主要内容,如果未能解决你的问题,请参考以下文章

使用 keyof 提取仅具有特定类型值的键的字符串文字联合

如何使 TypeScript 字符串枚举适用于字符串文字并正确进行类型推断

推断字符串string是数字json结构xml结构

Python学习笔记-小记

输入特定字符时的 Java 重放

为啥打字稿不能用 <T extends Person, K extends keyof T> generic 正确推断 T[K]?