目录

subtyping 与 assignment 在 Ts 中有什么区别

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,它构造出了的全新的类型,我们也可以叫它类型构造器。

一站式研发项目管理平台 PingCode

一站式研发项目管理平台 PingCode

支持敏捷\瀑布、知识库、迭代计划&跟踪、需求、缺陷、测试管理,同时满足非研发团队的流程规划、项目管理和在线办公需要。