subtyping 与 assignment 在 Ts 中的区别是:assignment是对subtype这个概念的扩展,它们的少数区别在于,赋值扩展了子类型与规则的兼容性,以允许赋值到和从以及到和从相应的数值,可以认为没有subtype这个概念。
一、subtyping 与 assignment 在 Ts 中的区别
assignment是对subtype这个概念的扩展,它们的少数区别在于,赋值扩展了子类型与规则的兼容性,以允许赋值到和从以及到和从相应的数值。你当然可以认为没有subtype这个概念。出于实际目的,类型兼容性由赋值兼容性决定,即使在 theand 子句的情况下也是如此。Implementsextends,甚至“兼容性”这个概念也是ts自己为了方便理解提出来的,并不出现在他的语言标准中。
Subtyping
我们在 λ→\lambda_{\rightarrow}λ→ 上加上一个扩展叫做 subtyping,这个扩展添加了两个内容
- 新引入了一个类型与类型之间的关系,称为子类型关系, 我们用符号 <: 来表示。A 是 B 的子类型表示为 A <: B。
- 新引入一个类型 unknown。
我们有时候叫 unknown 为 顶类型(Top Type),而且对于新引入的 <: 和 unknown ,我们同时添加了四条 Subtyping Rule 和一条 Typing Rule
Subtyping Rule:
- S-Top: 对于任何类型 SSS: S<:unknownS <: \text{unknown}S<:unknown
- S-Arrow: 对于任何类型 T1,T2,S1,S2T_1,T_2,S_1,S_2T1,T2,S1,S2: 如果 T1<:S1T_1 <: S_1T1<:S1 而且 S2<:T2S_2 <: T_2S2<:T2,那么 S1→S2<:T1→T2S_1 \to S_2 <: T_1 \to T_2S1→S2<:T1→T2
- S-Refl: 对于任何类型 SSS: S<:SS <: SS<:S
- S-Trans: 对于任何类型 S,U,TS, U, TS,U,T: 如果 S<:US <: US<:U 而且 U<:TU <: TU<:T,那么 S<:TS <: TS<:T
Typing Rule:
- T-Sub:如果在上下文中我们得知 ttt 的类型是 SSS, 并且 S<:TS <: TS<:T,那么我们可以知道,在当下上下文中,能推出 ttt 的类型也是 TTT
当然还有 λ→\lambda_{\rightarrow}λ→ 之前就带有 T-Var, T-Abs,T-App 等多条规则,太数学了,我们就不描述了。
例子
const foo: number = 1;
const bar: unknown = foo;
复制代码
下面的 h1 h2 h3 的 h 是 hypothesis 的意思,就是假设。
- 当类型检查器,开始检查第三行代码时候,我们得到的信息是
- h1: 在当前上下文中,foo 的类型是 number
- 根据 S-Top,把 meta variable「S」 替换为 实际的类型「number」得到 h2
- h2: number<:unknown\text{number} <: \text{unknown}number<:unknown
- 根据 T-Sub,把 meta variable 「t」 换成 实际的项 「foo」,把 meta variable「S」 替换为 实际的类型「number」,把 meta variable「T」 替换为 实际的类型「unknown」得到 h3
- h3: 在当前上下文中,foo 的类型是 unknown
- bar 标注的类型是 unknown,它需要接受一个 unknown 的值,刚刚好 h3 告诉我们 foo 的类型是 unknown,所以 const bar: unknown = foo; 能通过类型检查
延伸阅读:
二、类型运算符
类型运算符(type operator), 我的理解就是,部分内容是类型,它整体还是类型的东西。
比如:TS 中 number 是类型,number[] 也是类型,这里面有个固定的方法,把某个类型变成另一个类型,就是 ?₁ [] 这里的 ?₁ 可以换成任意的类型。
我们把这个类型运算符写出来,这是一元运算符 ?₁[],其中
- ?₁ 是一个 meta variable,类似于我们之前学的形式参数。可以被实际参数替换,比如 number。这里我们叫它 meta variable,是为了和编程语言里的 variable 做区分。
- 这个类型构造器接受一个参数,所以是「一元」
- 在之前的例子中,它的作用是把 number 类型转变成为 number 数组类型。number 是一个类型,number []是一个类型,number [] 是这个构造器接受 number 类型后生成的新类型。
我们给一些常见的类型运算符的例子:
- readonly [?₁, ?₂,?₃], 就是一个三元组类型运算符
- readonly ?₁[], 就是一个数组类型构造器,是一元类型运算符,有一个 meta variable ?₁
- { readonly a: ?₁, readonly b: ?₂ }, 就是一个对象类型运算符,是二元类型运算符,有两个 meta variable —— ?₁,?₂
- type aliastype Foo<X, Y> = X, 这条 type alias 声明了Foo<?₁, ?₂> 这个二元的类型运算符。
上面四个例子中,前三个不是 type alias,它构造出了的全新的类型,我们也可以叫它类型构造器。