Skip to main content

Command Palette

Search for a command to run...

<Programming with Types>随想: Chapter 2. Basic types

Updated
<Programming with Types>随想: Chapter 2. Basic types

类型限制了一个变量可以接受的有效值的集合,对数据可以进行的操作,数据的意义。

空类型(The empty type)

根据类型的定义,类型定义了可以接受的有效值集合,那么这个集合有没有可能为空?答案是有可能的,TypeScirpt 的 never 就是这种类型。

需要注意的是,空类型不同与 void, 后者是有效值集合当中只有一个值,但这个值没有任何意义。而空类型的有效值集合本身是空的。

使用场景

控制流分析

在函数调用时,标志一个函数不会返回任何值: 在调用过程中抛出异常、死循环或者程序崩溃。即返回值类型为 never 的函数,不会将CPU的控制权返回给被调函数。

function raise(message: string): never {
    throw new Error(message);
}

function forever(): never {
    while (true) {}
}

如果返回实际的值或者在调用处后面的继续编写代码逻辑,会引起编译错误。

function main() {
    forever()
    console.log(1); // Unreachable code detected.(7027)
}

ADT 的 Exhaustive(穷尽) Check

当业务中有一个 union type

interface Foo {
  type: 'foo'
}

interface Bar {
  type: 'bar'
}

type All = Foo | Bar

在 switch 当中判断 type,TS 是可以收窄类型的 (discriminated union):

function handleValue(val: All) {
  switch (val.type) {
    case 'foo':
      // 这里 val 被收窄为 Foo
      break
    case 'bar':
      // val 在这里是 Bar
      break
    default:
      // val 在这里是 never
      const exhaustiveCheck: never = val
      break
  }
}

注意在 default 里面我们把被收窄为 never 的 val 赋值给一个显式声明为 never 的变量。如果一切逻辑正确,那么这里应该能够编译通过。但是假如后来有一天你的同事改了 All 的类型:

type All = Foo | Bar | Baz

然而他忘记了在 handleValue 里面加上针对 Baz 的处理逻辑,这个时候在 default branch 里面 val 会被收窄为 Baz,导致无法赋值给 never,产生一个编译错误。所以通过这个办法,你可以确保 handleValue 总是穷尽 (exhaust) 了所有 All 的可能类型。

参考链接:https://www.zhihu.com/question/354601204

类型运算

never 是任何类型的 subtype(子类型), 除了never,没有其他类型是never的subtype。

这个例子体现 never 的取值有效集是空集,在 TS 中,可以对类型做集合的交并运算。 neverunion(并集) 运算的幺元,intersection(交集)运算的零元.

T | never // 结果为T
T & never // 结果为never

单元类型(unit type)

单元类型的取值有效集集合中,只有一个值, 因此编译器不需要处理单元类型的具体值。

常见的单元类型有

// undefined类型。
let a: undefined = undefined;
// null类型。
let b: null = null;
// unique symbol类型。
const c: unique symbol = Symbol();
// void类型。
let d: void = undefined;
// 字面量类型。
const e: 'hello' = 'hello';
// 联合枚举成员类型。
enum Foo { A, B }
const f: Foo.A = Foo.A;

也有人认为 void is not a unit type in TypeScript

使用场景

当一个函数返回值为单元类型时,这个值没有任何含义,因为无论函数内部发生什么,正常执行都会返回这个类型,并且只有该类型只有一个值。

这种没有返回任何有意义值的函数,通常被称为actions 或者 consumers, 他们存在的目的是为了产生副作用,对现实世界产生影响,例如 console.log()

Boolean类型

boolean 类型存在短路逻辑,如果第一个操作数(operand)提供了足够的信息评估(evaluate)整个表达式,那么第二个操作数就不会被 evaluate.

数字类型

基础的二进制编码,补码,IEEE754方式,此处略去。

编码字符串

Unicode编码有几个相似但是截然不同的概念:

  • 字符(Characters): 文本的计算机表示, 警官emoji(👮),零宽字符,女性标志(🚺)都是字符。字符这个词在不同的上下文环境中经常会被赋予不同的意思。

  • 字位(graphemes): 用户看到的符号,在渲染文本时,我们使用的是字位(graphemes),我们不想把一个多字的字位(multiple-character grapheme)拆开。

  • glygh(字形): "字形 "是一个字符的特殊表现形式。"C"(粗体)和 "C"(斜体)是 "C "字的两种不同的视觉表现形式。

  • code point(码点) 是文本信息的原子单位。文本是一连串的代码点。每个代码点是一个数字,由Unicode标准赋予其意义。

  • code unit(码元) 编码组成 code point(码点) 的一部分. In UTF-8 this means 8 bits, in UTF-16 this means 16 bits. A single code unit may represent a full code point, or part of a code point. For example, the snowman glyph (☃) is a single code point but 3 UTF-8 code units, and 1 UTF-16 code unit.

详细可以参考: https://stackoverflow.com/questions/27331819/whats-the-difference-between-a-character-a-code-point-a-glyph-and-a-grapheme

长度可变的编码方式

UTF-32 是一种长度不可变的字符串编码方式,对于每一个grapheme都使用,32bit存储,但是会造成格外的空间浪费。

UTF-8UTF-16 是长度可变的编码方式(variable-length encodings).

More from this blog

精读《设计机器学习系统》-ch04: 训练数据

不同于 Chapter03 从系统的角度来处理数据,这一章从数据科学的视角来处理数据。这章的标题是“training Data”,而非“training dataset”,因为 数据集(dataset) 意味着有限(finite)和固定(stationary), 而现实生产环境中的 数据(data) 通常是 无限 并且 不固定 的。 抽样 抽样方法在 ML 项目的生命周期中无处不在,在这一节中,我们使用生成训练数据作为例子。 那为什么需要抽样?直接使用全部数据不可以吗? 首先,在现实世界中,并不...

Dec 10, 2022
精读《设计机器学习系统》-ch04: 训练数据
7

70 Talk

19 posts