在包含联合类型的类型上使用类型谓词

huangapple go评论79阅读模式
英文:

Using Type Predicates on a type that contains a Union type

问题

抱歉,您提供的代码片段中的英文部分已经翻译如下:

我定义了一些类型:

type Selected = {
	ID: number
}

type DetailsV1 = {
    Status: number
}

type DetailsV2 = {
	Status: number,
    Date: string
}

type DetailsDTO =
	| DetailsV1
	| DetailsV2

type SelectedDetails = DetailsDTO & Selected

我还定义了一个用户自定义的类型保护函数:

function detailsDTOIsV2(
	details: DetailsDTO
): details is DetailsV2{
	return (
		(details as DetailsV2)
			.Date !== undefined
	)
}

当我有一个类型为 SelectedDetails 的对象时,如何访问 .Date 属性呢?
如果没有某种类型检查,我会得到 SelectedDetails 类型上不存在属性 Date 的错误。但是我看不到如何在 SelectedDetails 是一个交叉类型时检查类型,所以是否有其他方法可以解决这个问题?

英文:

Sorry for the weird title, super hard to word that

I have a few types defined:

type Selected = {
	ID: number
}

type DetailsV1 = {
    Status: number
}
type DetailsV2 = {
	Status: number,
    Date: string
}
type DetailsDTO =
	| DetailsV1
	| DetailsV2

type SelectedDetails = DetailsDTO & Selected

I also have a user defined type guard:

function detailsDTOIsV2(
		details: DetailsDTO
	): details is DetailsV2{
		return (
			(details as DetailsV2)
				.Date !== undefined
		)
	}

When I have an object typed as SelectedDetails how can I access the .Date property?
Without some sort of type checking I get the Property Date does not exist on type SelectedDetails. But I don't see how to check the types when SelectedDetails is an intersection type, so is there another way to go about this?

答案1

得分: 1

你只需缩小到具有该属性的类型。在这种情况下,您可以使用 in 运算符来做到这一点。

selectedDetails.Date // 类型错误

if ('Date' in selectedDetails) {
  selectedDetails.Date // 正常
}

这里的 'prop' in object 如果该属性存在于该对象上,将返回 true。TypeScript会注意到这一点,并将您的联合类型缩小为只包含具有该属性的类型。

查看 playground

英文:

You simply have to narrow to the type that has that property. In this case you do that with the in operator.

selectedDetails.Date // type error

if ('Date' in selectedDetails) {
  selectedDetails.Date // fine
}

Here 'prop' in object returns true if that property is exists on that object. And TypeScript will notice this and narrow your union to just the ones that have property.

See playground

答案2

得分: 1

由于您已经拥有名为 detailsDTOIsV2DetailsV2 类型守卫,您实际上可以将 SelectedDetails 传递给它,因为您的类型守卫期望一个 DetailsDTO,而 SelectedDetails 是它的子类型。之所以允许这样的赋值是因为 TypeScript 的类型系统,它被称为结构类型系统,它只检查分配的值是否与分配的类型匹配,如果有一些未在类型中指定的额外字段,它不会触发错误。

只需将 selectedDetails 传递给类型守卫:

if(detailsDTOIsV2(selectedDetails)){
    selectedDetails.Date //有效
}

或者,您可以使用 in 运算符也可以实现相同的结果:

if('Date' in selectedDetails) {
  selectedDetails.Date // 有效
}
英文:

Since you already have a type-guard for the DetailsV2 named detailsDTOIsV2, you can actually pass a SelectedDetails to it, since your type-guard expects a DetailsDTO and SelectedDetails is a sub-type of it. The reason why such assignment is allowed is due to Typescript's type system, which is known as structural type system, that only checks if the assigned value matches the assigned type and doesn't fire an error if there are some extra fields that are not specified by the type.

Simply just pass the selectedDetails to the type-guard:

if(detailsDTOIsV2(selectedDetails)){
    selectedDetails.Date //works
}

Alternatively, you could use the in operator that would achieve the same result:

if('Date' in selectedDetails) {
  selectedDetails.Date // works
}

答案3

得分: 0

我认为这是无法绕过的。

TS编译器无法推断出SelectedDetails是否具有日期字段,除非有类型保护。

英文:

I think there is no way around this.

The TS Compiler cannot infer whether SelectedDetails will have Date field or no without type guard

huangapple
  • 本文由 发表于 2023年6月14日 23:45:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/76475350.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定