【问题标题】: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 }

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

【问题讨论】:

    标签: typescript union-types


    【解决方案1】:

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

    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

    查看问题:

    【讨论】:

    • 虽然这可行,但我现在想知道为什么 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 的帮助较小。但是您仍然是对的,我找不到在打字稿中表达独占键的简单方法
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-01-15
    • 2022-01-04
    • 2018-12-06
    • 1970-01-01
    • 2021-10-27
    • 2019-07-23
    • 2021-08-05
    相关资源
    最近更新 更多