首页 > 解决方案 > 如何键入检查名义类型的 ID

问题描述

我正在尝试此处描述的基于枚举的标称输入方法:https ://basarat.gitbooks.io/typescript/docs/tips/nominalTyping.html

enum PersonIdBrand {}
export type PersonId = PersonIdBrand & string

interface Person {
  id: PersonId
  firstName: string
  lastName: string
}

我遇到了向测试使用的一些工厂方法添加类型的问题。这些辅助方法允许使用可以选择性地覆盖的默认值创建测试数据:

const makeTestPerson = ({
  id = 'personId' as PersonId,
  firstName = 'Bob',
  lastName = 'Smith'
}: Partial<Person> = {}): Person => ({
  id,
  firstName,
  lastName
})

const person = makeTestPerson({ lastName: 'Ross' })

但是,tsc给出一个错误:

error TS2322: Type 'PersonId' is not assignable to type 'never'.

11   id = 'personId' as PersonId,

如果我改为使用id: string,它编译没有任何问题。有什么方法可以使用这些函数进行类型检查PersonId


更新探索了更多,我认为这种策略存在更根本的问题:

const maybePersonId: PersonId | undefined = ("personId" as PersonId)

这也失败了:

TS2322: Type 'PersonId' is not assignable to type 'undefined'.

那么为什么会失败呢?当然 anX应该总是可以分配给X | undefined?

标签: typescriptenums

解决方案


我认为自从编写代码以来,打字稿已经改变了它处理空的联合和交叉点(或它认为是空类型)的方式。我无法找到改变行为的 PR,但如果我找到它,我会尝试发布它(我找不到 PR,但中断发生在 2.9 中)

对于品牌类型,编译器团队使用与仅包含一个额外成员的类型的交集,而不是与枚举的交集:

export type Path = string & { __pathBrand: any };

我会采用这种方法:

export type PersonId = { __personIdBran: any } & string

interface Person {
  id: PersonId
  firstName: string
  lastName: string
}

const makeTestPerson = ({
  id = 'personId' as PersonId,
  firstName = 'Bob',
  lastName = 'Smith'
}: Partial<Person> = {}): Person => ({
  id,
  firstName,
  lastName
})

const person = makeTestPerson({ lastName: 'Ross' })

推荐阅读