Typescript 使用鉴别器的联合字符串类型创建 ADT(可鉴别联合类型)的实例

标签 typescript

让我们考虑以下代码。我有一个 ADT,其中具体类型之间的区别是 kind 属性。我想要一个实用方法来创建这些类的实例。

interface I {
    id: number;
}

interface A extends I {
    kind: "a";
}

interface B extends I {
    kind: "b";
}

type O = A | B;

function create(id: number, kind: "a" | "b"): O {
    return {
        id: 1,
        kind: kind
    };
}

显然上面的代码给出的错误为 "a"| “b” 不可分配给 “a”“b”。有没有办法表达这个功能?以某种方式表达类型 oneOf "a"| “b”

最佳答案

更新:2019 年 5 月 30 日,随着 TypeScript 3.5 的发布,此问题应由 smarter union type checking 解决。以下内容适用于 3.4 及以下版本:


我认为您已经正确表达了代码,并且 TypeScript 正在阻止有效的分配。对于你我来说,显然 O 在结构上应该与

相同
type OPrime = {
  id: number;
  kind: "a" | "b"
}

由于 create() 返回一个 OPrime 类型的值,因此它应该可以分配给 O。唉,TypeScript 将 O 视为可分配给 OPrime,但反之则不然。

最简单的解决方法是使用类型断言:

function create(id: number, kind: "a" | "b"): O {
    return {
        id: 1,
        kind: kind
    } as O; // assertion
}

类型断言通常并不安全,但当您了解的表达式类型超出了编译器所能计算出的范围时,类型断言非常有用。但是很难区分编译器警告您的合法错误和编译器无法识别有效表达式之间的区别。在这种情况下,我相当确定您的做法是正确的,并且编译器正在报告错误的误报。


结果是intended behavior对于 typescript 。虽然 OOPrime 恰好相同,但编译器需要额外的工作才能识别这一点。 通常这是 not warranted ,因为两个联合组成部分很少仅因单个同名属性的类型而有所不同。在大多数情况下(甚至可能是您实际的非玩具用例),您的 AB 类型中会有其他类型。例如:

interface AExtra extends I {
    kind: "a";
    name: string;
}

interface BExtra extends I {
    kind: "b";
    age: number;
}

type OExtra = AExtra | BExtra;

现在不再可能将 OExtra 表示为具有联合值属性的单个非联合类型。考虑OExtraPrime:

type OExtraPrime = {
  id: number;
  kind: "a" | "b";
  name?: string;
  age?: number;
}

确实,任何 OExtra 都可以分配给 OExtraPrime,但反之则不然。因此,由于在一般实践中,OOPrime 之间的等价关系不会出现,因此编译器不会费心去建立它。所以你能做的最好的事情就是类型断言。

希望这有帮助;祝你好运!

关于Typescript 使用鉴别器的联合字符串类型创建 ADT(可鉴别联合类型)的实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47018496/

相关文章:

angular - Datalayer.push 在 Angular 6 中使用 Typescript

javascript - typescript :将方法添加到类的数组

typescript - 如何抛出对象交集类型的语法错误?

javascript - navTrl.push() 中的 promise 错误

angular - 如何替换 Ionic 中的当前页面?

node.js - sequelize/sequelize-typescript - findAll 和 HasMany 返回一个对象而不是数组

javascript - 使用POST方法给用户授权::Angular2&TypeScript

typescript - 是否可以在接口(interface)上制作 "inner join"

javascript - 如何在不返回 Angular 数据的 http.post 请求上使用 'toPromise()'?

typescript - 初始化 Sentry 的 Vuejs(带有 Typescript)集成的代码处构建失败