首页 > 解决方案 > TypeScript:具有已知键的对象上的 Object.keys() 类型过于通用

问题描述

我正在学习 TypeScript,我很难理解的一件事是如何正确迭代对象的键。一个非常常见的应用程序是这样的:

type T = {
    "a": number,
    "b": number
}

const obj : T = {
    a: 1,
    b: 2
};

const keys = Object.keys(obj);
const m = keys.map(k => 2*obj[k]);

我有一个obj已知类型的对象,T并希望获取所有键来调用map它们。的返回类型Object.keys([type T])根据string[]Visual Studio Code。我在别处读到,这样做的原因是传递给的对象Object.keys可能是T. 但是,在此示例中,很明显该对象具有类型T而不是子类型(具有更多键)。

由于返回类型过于笼统,TS 编译器在使用键map读取每个键的实际值时向我显示错误。这是有道理的,因为只允许“a”和“b”,但编译器认为任何其他字符串也可能出现(这是错误的,至少在这个例子中)

调用map对象键的推荐方法是什么?使用此方法强制转换类型有效:

function getKeysGen<T extends object>(o: T) {
    return Object.keys(o) as Array<keyof T>;
}

const keys = getKeysGen(obj); // correctly shown as Type ("a" | "b") 

调用通用键获取器时我什至不必指定类型,TS/VSCode 似乎能够从参数本身的类型中推断出来。但是为什么这不适用于原始Object.keys功能?

为什么这不起作用?

function getKeys(o: Object) {
    return Object.keys(o) as Array<keyof typeof o>;
}
const keys = getKeys(obj); // VSCode shows the type is ("constructor" | "toString" | "toLocaleString" | "valueOf" | "hasOwnProperty" | "isPrototypeOf" | "propertyIsEnumerable")[]

当我运行此代码时,keys实际上包含[ 'a', 'b' ]而不是 VSCode 显示给我的键。但是,当使用键将值映射到它们的双精度时,TS 编译器会拒绝:

const m = keys.map(k => 2*obj[k]);

错误是

错误 TS2363:算术运算的右侧必须是“any”、“number”、“bigint”类型或枚举类型。

很可能是因为k没有正确推断出的类型,所以2*undefined可能会发生类似的事情。

我在上面写的通用函数getKeysGen是要走的路还是有什么我遗漏的地方?

标签: javascripttypescriptgenericstypes

解决方案


Object.keys对于您要查找的内容,类型定义确实太笼统了。如果您查看lib.es2015.cor.d.ts,您会看到:

keys(o: {}): string[];

string[]不够好。

对于这样的事情,我认为最简单的方法是在收集键时断言键确实是对象的键:

const keys = Object.keys(obj) as (keyof T)[];

对于您的getKeysGen函数,一个问题是Object您想要的是 Object 构造函数object- 但即使那样,您也无法获得在没有泛型的情况下每次调用函数时传递的对象的类型。否则,你只有keyof typeof owhereo是一个普通的对象。


推荐阅读