打字稿:将标记的联合转换为联合类型

Posted

技术标签:

【中文标题】打字稿:将标记的联合转换为联合类型【英文标题】:Typescript: convert a tagged union into an union type 【发布时间】:2020-10-16 20:21:21 【问题描述】:

我有以下标记的联合接口

interface Example 
  a: TypeA;
  b: TypeB;

作为输出,我想将此标记的联合转换为联合类型,例如:

type oneOf<T> = ...

在哪里

var example: oneOf(Example); // would be  a: TypeA  |  b : TypeB 

我无法将类型键映射到只有键作为参数的对象中。有什么想法吗?

【问题讨论】:

【参考方案1】:

您可以通过以下组合来做到这一点:

Distributive conditional types Mapped types
interface Example 
  a: string;
  b: number;


type SingleProperty<T, K extends keyof T> = K extends any ? [Prop in K]: T[Prop] : never
type UnionOfProperties<T> =  [K in keyof T]: SingleProperty<T, K> [keyof T];
type ExamplePropertiesUnion = UnionOfProperties<Example>

这将返回预期:

type ExamplePropertiesUnion = 
    a: string;
 | 
    b: number;

虽然以上是正确的,但 TS 将允许以下情况

var t: ExamplePropertiesUnion = a: "", b: 42

这不是我们通常想要的:

下面是更严格类型检查的变体

type FixTsUnion<T, K extends keyof T> = [Prop in keyof T]?: Prop extends K ? T[Prop]: never
type oneOf<T> =  [K in keyof T]: Pick<T, K> & FixTsUnion<T, K>[keyof T];
// ok
var z1: oneOf<Example> =  a: "" ;
// ok
var z2: oneOf<Example> =  b: 5 ;
// error
var z3: oneOf<Example> =  a: "", b: 34 ;
// error
var z4: oneOf<Example> =  ;

Try it here

查看问题:

TypeScript: Map union type to another union type Transform union type to intersection type

【讨论】:

虽然这可行,但我现在想知道为什么 var t: a: string | b: number = a: "", b: 42 不会引发错误。 我认为 TS 不支持联合类型的多余属性检查。我设法在谷歌上搜索了一些建议:github.com/microsoft/TypeScript/issues/23535github.com/Microsoft/TypeScript/issues/14094你可以尝试明确地解决这个问题:var t: a: string, b: never | b: number, a: never = a: "", b: 42 好主意,但任何简单的做作都会失败,例如:var t: a: string , b: never | a: never, b: string = a: "" 我的错。很抱歉造成混乱。 因为 b 应该是数字。我编辑了你的答案,你应该得到所有的荣誉;-)【参考方案2】:
type EntryUnion<T> =  [K in keyof T]:  [Q in K]: T[Q]  [keyof T];

这实际上与

相同
type EntryUnion<T> =  [K in keyof T]: Pick<T, K> [keyof T];

但是每当我使用诸如 Pick 之类的 util 类型时,Intellij 和 VS Code 中的类型推断都会在 Pick 处停止并拒绝进一步扩展。尽管它们在执行分配时执行类型正确性时所做的工作完全相同,但当出现问题时,使用 Pick 的类型定义不正确的地方就不那么明显了(除非您非常熟悉 Pick 的作用)

【讨论】:

嗨,我记下了快捷方式,您的解决方案也可以确保至少存在一个键,但不能确保没有重复项(键上的独占联合)check here 是的,你完全正确。然而,对我来说,这个特殊的问题涉及严格性和清晰性之间的权衡。实际上,这里最严格的类型定义应该是 (inferred as) a: string, b: never | a: never, b: number 。但它缺乏清晰性,尤其是当您有更多案例以及分配错误并且代码无法编译时。与将类型定义为 a: string | b: number 相比,IDE 的帮助较小。但是您仍然是对的,我找不到在打字稿中表达独占键的简单方法

以上是关于打字稿:将标记的联合转换为联合类型的主要内容,如果未能解决你的问题,请参考以下文章

为啥打字稿将联合中的属性标记为不存在?

如何从打字稿中的标记联合类型中提取类型?

打字稿将联合转换为交集[重复]

打字稿:检查类型是不是为联合

函数的打字稿联合/交集类型

差异联合类型和区分联合打字稿/ F#