TypeScript:具有可选值的歧视联合

时间:2017-01-05 21:53:02

标签: typescript

考虑以下类型:

interface FullName {
  fullName?: string
}

interface Name {
  firstName: string
  lastName: string
}

type Person = FullName | Name;

const p1: Person = {};
const p2: Person = { fullName: 'test' };
const p3: Person = { firstName: 'test' }; // Does not throw
const p4: Person = { badProp: true }; // Does throw, as badProp is not on FullName | Name;

我希望p3导致编译错误,因为firstName不存在lastName,但它没有 - 这是一个错误还是预期?

此外,要求使用FullName.fullName导致p3(和p1)导致错误。

3 个答案:

答案 0 :(得分:5)

首先,你的界面FullName只包含一个可选属性,它基本上使它匹配任何东西。然后当你用它做一个联合类型时,结果类型将与所有东西兼容。

然而,考虑声明和分配文字对象还有另一个问题,那就是您只能声明已知属性:Why am I getting an error "Object literal may only specify known properties"?

所以你可以毫无问题地做到这一点:

var test = { otherStuff: 23 };
const p4: Person = test;

但不是这个

const p4: Person = { otherStuff: 23 };

在您的情况下,firstNameFullName | Name的已知属性,所以一切正常。

正如@artem回答的那样,discriminated unions除了常规的工会之外,在打字稿中有特殊含义,需要特殊的结构假设。

答案 1 :(得分:2)

在通常意义上,您的问题中的类型不是受歧视的联盟 - 您的工会成员不具有称为discriminant的共同的,非可选的文字属性。

所以,正如@Alex在他的回答中指出的,你的联盟有点类似于

type Person = {
  fullName?: string
  firstName?: string
  lastName?: string
}

因此可以使用{ firstName: 'test' }

进行初始化

使用真正区分的联合,您可以返回检查非可选属性的逻辑,以及检查对象文字只能指定已知属性:

interface FullName {
  kind: 'fullname';  
  fullName?: string
}

interface Name {
  kind: 'name';  
  firstName: string
  lastName: string
}

type Person = FullName | Name;

const p1: Person = {kind: 'fullname'};  // ok
const p2: Person = {kind: 'fullname', fullName: 'test' };  // ok

检查非可选属性:

const p3: Person = {kind: 'name', firstName: 'test' }; 

错误:

Type '{ kind: "name"; firstName: string; }' is not assignable to type 'Person'.
  Type '{ kind: "name"; firstName: string; }' is not assignable to type 'Name'.
    Property 'lastName' is missing in type '{ kind: "name"; firstName: string; }'.

检查额外属性:

const p5: Person = { kind: 'fullname', bar: 42 }

错误:

Type '{ kind: "fullname"; bar: number; }' is not assignable to type 'Person'.
  Object literal may only specify known properties, and 'bar' does not exist in type 'Person'.

然而,正如@JeffMercado发现的那样,类型检查仍然有点过时了:

const p6: Person = { kind: 'fullname', firstName: 42 };  // no error. why?

我考虑为typescript github项目发布一个问题。

答案 2 :(得分:0)

2021 年更新:问题中的示例现在按预期工作。

至少因为 TypeScript 版本 3.3.3(目前可以在 TypeScript 游乐场上测试的最旧版本),所以您不需要判别式(即常见的、非可选的文字属性)。

给定

interface FullName {
  fullName?: string
}

interface Name {
  firstName: string
  lastName: string
}

type Person = FullName | Name;

在问题中,以下示例(被提出此问题的人标记为“不抛出”)

const p3: Person = { firstName: 'test' }; // Does not throw

现在导致此 TypeScript 错误:

Property 'lastName' is missing in type '{ firstName: string; }' but required in type 'Name'.

@artem 想知道为什么

const p6: Person = { kind: 'fullname', firstName: 42 };

不会在他的示例中使用判别式 kind 抛出错误。

好吧,自从最后一个 TypeScript 版本 3.3.3 它确实抛出了一个错误,正是预期的错误:

Object literal may only specify known properties, and 'firstName' does not exist in type 'FullName'.

参见 this TypeScript playground,其中包括两个示例(有和没有歧视联合)。