Typescript:对象和基本类型联合的keyof typeof始终为never

6
首先,让我们来了解一下我的问题背景:我有一个项目,在该项目中,我通过Socket.IO接收一个对象,因此我没有关于它的类型信息。此外,它是一种相当复杂的类型,因此需要进行大量检查以确保接收到的数据是好的。
问题在于,我需要访问由接收到的对象中字符串指定的本地对象的属性。对于第一个维度,这可以正常工作,因为我可以将任何类型的属性说明符强制转换为要访问的keyof typeof(例如:this.property[<keyof typeof this.property> data.property])。
结果变量的类型显然是一个相当冗长的联合类型(将所有属性this.property的类型合并)。只要其中一个属性是非原始类型,keyof typeof subproperty 就会被推断为never
通过之前的检查,我可以保证该属性存在,并且我99%确定代码在编译后会运行。只是编译器在抱怨。
下面是一些非常简单的代码,重现了这种行为以及观察到的和期望的类型。
const str = 'hi';
const obj = {};
const complexObj = {
    name: 'complexObject',
    innerObj: {
        name: 'InnerObject',
    },
};

let strUnion: typeof str | string;              // type: string
let objUnion: typeof obj | string;              // type: string | {}
let complexUnion: typeof complexObj | string;   // type: string | { ... as expected ... }

let strTyped: keyof typeof str;                 // type: number | "toString" | "charAt" | ...
let objTyped: keyof typeof obj;                 // type: never (which makes sense as there are no keys)
let complexObjTyped: keyof typeof complexObj;   // type: "name" | "innerObject"

let strUnionTyped: keyof typeof strUnion;       // type: number | "toString" | ...
let objUnionTyped: keyof typeof objUnion;       // type: never (expected: number | "toString" | ... (same as string))
let complexUnionTyped: keyof typeof complexUnion;   // type: never (expected: "name" | "innerObject" | number | "toString" | ... and all the rest of the string properties ...)
let manuallyComplexUnionTyped: keyof string | { name: string, innerObj: { name: string }};  // type: number | "toString" | ... (works as expected)

这是 TypeScript(3 版本)的已知限制,还是我理解有误?

1个回答

11

如果你有一个union,只有共同的属性是可访问的。 keyof 会给出类型公开可访问的键。

对于 strUnionTyped,它是 string 和字符串字面类型 'hi' 的联合体,结果类型将具有与 string 相同的属性,因为联合体中的两种类型具有与 string 相同的键。

对于 objUnionTypedcomplexUnionTyped 联合体没有共同的键,所以结果将为 never

对于 manuallyComplexUnionTyped 你获得了 string 的键,因为你写的实际上是 (keyof string) | { name: string, innerObj: { name: string }} 而不是 keyof (string | { name: string, innerObj: { name: string }}) 所以你得到了与你指定的对象类型联合的 string 的键。

要获取 union 的所有成员的键,可以使用条件类型:

type AllUnionMemberKeys<T> = T extends any ? keyof T : never;
let objUnionTyped: AllUnionMemberKeys<typeof objUnion>;
let complexUnionTyped: AllUnionMemberKeys<typeof complexUnion>;

编辑

条件类型有助于获取所有联合成员的键,原因是条件类型会分布到裸类型参数上。因此在我们的情况下

AllUnionMemberKeys<typeof objUnion> = (keyof typeof obj) | (keyof string)

谢谢,条件语句完美地运行了。但是我不明白为什么会这样。在我的理解中,经过条件逻辑的运算后(应该总是评估为真,我想?),发生的事情与 keyof typeof something 完全相同,不是吗? - Jan Hettenkofer
1
@JanHettenkofer 添加了一些解释和相关链接(搜索分发,我无法链接到该部分),希望能有所帮助。 - Titian Cernicova-Dragomir

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接