打字稿:对象和原语之间的keyof typeof联合总是永远不会

Posted

技术标签:

【中文标题】打字稿:对象和原语之间的keyof typeof联合总是永远不会【英文标题】:Typescript: keyof typeof union between object and primitive is always never 【发布时间】:2019-02-12 18:00:35 【问题描述】:

首先,我的问题的一些上下文:我有一个项目,我通过 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】:

如果您有联合,则只能访问公共属性。 keyof 将为您提供一种类型的可公开访问的密钥。

对于strUnionTypedstring 和字符串文字类型'hi' 之间的联合),结果类型将具有与字符串相同的属性,因为联合中的两种类型具有与字符串相同的键。

对于objUnionTypedcomplexUnionTyped,联合没有公共键,所以结果将是never

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

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

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

编辑

条件类型有助于获取所有联合成员的键的原因是条件类型distribute 优于裸类型参数。所以在我们的例子中

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

【讨论】:

谢谢,条件句运行良好。但是,我不明白为什么。在我看来,在运行条件逻辑之后(我认为它应该总是评估为真?),发生的事情与keyof typeof something 完全相同,不是吗? @JanHettenkofer 添加了一点解释和相关链接(搜索分发,我无法链接到该部分),希望对您有所帮助

以上是关于打字稿:对象和原语之间的keyof typeof联合总是永远不会的主要内容,如果未能解决你的问题,请参考以下文章

在打字稿映射类型中过滤 keyof 意外工作

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

从打字稿中的对象获取特定类型的所有键[重复]

如何设置 VSCode 以内联显示打字稿错误

类型“typeof File”离子打字稿上不存在属性“moveFile”

expressjs:打字稿:“typeof <express.Router>”类型的参数不可分配给“RequestHandlerParams”类型的参数