区分联合类型和判别联合类型 Typescript/F#

6
我正在阅读有关TypeScript联合类型的官方文档,并且认为它与F#中的“区分联合”相同(尽管它们具有不同的语法但是概念相同),因为我有F#背景并且两者都由微软支持。但是查看文档,F#并没有真正区分“联合类型”和“区分联合”:https://fsharpforfunandprofit.com/posts/discriminated-unions/ 然而,TypeScript确实区分这两个概念:
联合类型:https://www.typescriptlang.org/docs/handbook/advanced-types.html#union-types 区分联合:https://www.typescriptlang.org/docs/handbook/advanced-types.html#discriminated-unions 我在想这些概念本身是否有区别,还是只是依赖于语言的概念?
据我所知,在F#中,联合类型也是区分式联合类型,因为您可以使用匹配表达式和解构来区分联合类型。
但是,在TypeScript中,您无法进行区分,因为该语言没有提供特定的表达式来区分联合类型,因此您需要通过值进行区分,所有联合类型都具有鉴别器。这正确吗?

是的,在TS中,判别式只是您选择的任意对象属性。使用带有标记联合的判别式,您不需要这样做,因为类型本身携带了此信息。由于标记在编译期间不会被删除,因此您可以在运行时进行模式匹配。 - user5536315
2个回答

9
主要区别在于,TypeScript Union Type 实际上是 F# Discriminated Union 的超集。
TypeScript Union Type 是一种无标签联合类型。 F# Discriminated Union 是一种有标签联合类型。
换句话说,可以将可以在 F# 中建模的每个带标记联合都等同地建模为 TypeScript Union Type,但反之则不成立。
例如,在 F# 中以下是一个具有标记联合的示例:
type a' Option = Some of 'a | None 

可以用Typescript同构地建模,如下所示:
type Option<T> = {tag: 'Some', value: T} | {tag: 'None'}

然而,下面这个 TypeScript 联合类型无法在 F# 中以同构方式建模:
type UserInput = number | string

这里的主要区别在于TypeScript联合类型不需要被标记,然而F#联合类型必须被标记
因此,我们可以看到TypeScript实际上比F#更灵活,但这并不是没有代价的,未标记的联合类型实际上是有漏洞的,这意味着有些类型联合在TypeScript中将无法通过类型检查。
就像不带类型的λ演算是类型化λ演算的超集,但是类型化λ演算更容易证明正确。

1
联合类型基本上允许使用类型别名来表示可能的多种类型,而区分联合类型无法做到这一点,因为它们只能用于一种类型。正如@Lee在他的答案中提到的那样,对于区分联合类型,我们需要类型构造函数,它们是“情况”或标记标签。定义区分联合类型就是为类型指定不同的类型构造函数。而对于联合类型,我们可以定义其他类型的类型别名。 - FraK

6
在类型联合(A | B)中,操作数都是类型,而在带有鉴别器的联合类型type U = A | B中,每个案例都是类型U的构造函数,它们本身不是类型。类型U的值在运行时被标记,因此您可以区分可能的情况。
一个结果是,鉴别式联合可以以一种联合类型可能无法实现的方式进行嵌套。某些类型A的可选值在联合类型系统中可能表示为:
type A? = (A | null)

null 是表示“空”值的单例类型。

在有辨别联合的情况下,通常会将其表示为

type a' option = Some of 'a | None 

使用这个公式计算出数值

let o: int option option = Some None

由于(A?)? == (A | null) | null == A | null == A?,因此无法使用联合类型表示。


你可以扩展未标记的联合类型 - user5536315
我真的很喜欢你基于类型构造函数思想的解释,这更有意义。但是为什么嵌套联合在联合类型中不起作用,谁会像你展示的那样“减少”类型?为什么带判别联合的嵌套类型可以工作? - FraK

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接