function doSomething(pt: Point) {
for (const k of Object.keys(pt)) {
// A valid call if Object.keys(pt) returns (keyof Point)[]
fn(k);
}
}
// Throws an exception
doSomething(origin);
Object.keys返回keyof T 如果去掉点1,keyof就几乎没用了,因为这意味着keyof Point可能不是"x"或"y"。 丢弃第2点会完全破坏TypeScript的类型系统,这不是一个选项。 丢弃第3点也会完全破坏TypeScript的类型系统。 抛开第4点是很好的,这会让程序员考虑您正在处理的对象是否可能是您认为拥有的对象的子类型的别名。 Exact Types是一个“缺少的特性”,它允许你声明一个新的类型,而不受第二点的约束。如果这个特性存在,那么大概可以让Object.keys只对声明为“精确”的T返回keyof T。
附录:不过,仿制药肯定是吧?
评论者暗示如果参数是泛型值,Object.keys可以安全地返回keyof T。这仍然是错误的。
class Holder<T> {
value: T;
constructor(arg: T) {
this.value = arg;
}
getKeys(): (keyof T)[] {
// Proposed: This should be OK
return Object.keys(this.value);
}
}
const MyPoint = { name: "origin", x: 0, y: 0 };
const h = new Holder<{ x: number, y: number }>(MyPoint);
// Value 'name' inhabits variable of type 'x' | 'y'
const v: "x" | "y" = (h.getKeys())[0];
或者这个例子,它甚至不需要任何显式类型参数:
function getKey<T>(x: T, y: T): keyof T {
// Proposed: This should be OK
return Object.keys(x)[0];
}
const obj1 = { name: "", x: 0, y: 0 };
const obj2 = { x: 0, y: 0 };
// Value "name" inhabits variable with type "x" | "y"
const s: "x" | "y" = getKey(obj1, obj2);
4条答案
按热度按时间ckx4rj1h1#
当前返回类型(
string[]
)是有意的。为什么?考虑如下类型:
您可以编写如下代码:
我们来问一个问题:
在类型良好的程序中,对
fn
的法律的调用是否会遇到错误情况?Object.keys
有什么关系呢?现在考虑这个 other 代码:
注意,根据TypeScript的类型系统,所有
NamedPoint
都是有效的Point
。现在让我们编写 * 更多代码 *:
我们的类型良好的程序刚刚抛出了一个异常!
这里出错了!从
Object.keys
返回keyof T
,我们违反了keyof T
形成一个穷举列表的假设,因为引用一个对象并不意味着引用的 * 类型 * 不是值的 * 类型 * 的超类型。基本上,(至少)以下四件事中的一件不可能是真的:
keyof T
是T
密钥的详尽列表1.具有附加属性的类型始终是其基类型的子类型
1.使用超类型引用作为子类型值的别名是法律的的
Object.keys
返回keyof T
如果去掉点1,
keyof
就几乎没用了,因为这意味着keyof Point
可能不是"x"
或"y"
。丢弃第2点会完全破坏TypeScript的类型系统,这不是一个选项。
丢弃第3点也会完全破坏TypeScript的类型系统。
抛开第4点是很好的,这会让程序员考虑您正在处理的对象是否可能是您认为拥有的对象的子类型的别名。
Exact Types是一个“缺少的特性”,它允许你声明一个新的类型,而不受第二点的约束。如果这个特性存在,那么大概可以让
Object.keys
只对声明为“精确”的T
返回keyof T
。附录:不过,仿制药肯定是吧?
评论者暗示如果参数是泛型值,
Object.keys
可以安全地返回keyof T
。这仍然是错误的。或者这个例子,它甚至不需要任何显式类型参数:
d6kp6zgx2#
如果您确信正在使用的对象中没有额外的属性,则可以执行以下操作:
如果愿意,可以将其提取到函数中:
作为额外的好处,下面是从a GitHub issue with context on why this isn't the default中提取的
Object.entries
:mkshixfv3#
这是谷歌上关于这类问题的热门文章,所以我想分享一些关于前进的帮助。
这些方法很大程度上是从各种问题页面上的长时间讨论中提取出来的,你可以在其他答案/评论部分找到链接。
假设你有这样的代码:
以下是一些前进的方法:
1.如果您不需要键,而实际上只需要值,则使用
.entries()
或.values()
,而不是对键进行迭代。1.创建辅助函数:
1.重新转换类型(这一点很有帮助,因为不必重写太多代码)
其中哪一个是最无痛的主要取决于您自己的项目。
d4so4syb4#
可能的解决办法