《Effective TypeScript 》-- 条款7: 将类型视为价值的集合

61 阅读5分钟

条款7:将类型视为价值的集合

JS 数据类型:

当我们谈到 “JS 的数据类型” 这个概念的时候,我们首先会想到的是“ JS 的数据类型有哪些 ”这个面试题,张口就来 那就是 “null undefined string number boolean symbol object” 7种常用类型,了然于心。

但是,对于 “JS 的数据类型是什么” 这个定义我们却很少关注。我们所看到的常用的数据类型,其实是 JS 这门语言的数据结构设计的具体表现形式,那什么又是数据结构呢?数据结构是一种组织、管理和存储数据的形式,这种形式方便数据访问和修改;

那么由此可以推出,当我们谈论 “JS 的数据类型是什么” 的时候,其实是在谈论 JS 这门语言存储数据的具体形式有哪些,就是上面 7 中常用的存储形式;

总的来说,当我们说 “JS 的数据类型” 的时候,其实是在谈论 “JS 语言存储数据的具体形式” 有哪些;

TS 类型系统:

因为 TS 是 构建于 JS 之上的,那么对于存储数据的形式肯定和 JS 保持一致,那么 TS 的数据类型不会再是讨论存储数据的形式了,而是在讨论 变量声明时为变量设置的类型 ;

TS 产生的原因是因为 JS 在开发阶段不能够进行类型约束和检查,导致很多错误发生,所以 TS的类型系统希望能够在开发阶段通过静态检查的方式来检查代码中的错误信息;而约定变量声明的类型以及类型推导等方式是静态检查的主要手段; 我们首先来看一下 TS 中的数据类型有哪些,即可约定变量的类型有哪些:

  • boolean(布尔类型)
  • number(数字类型)
  • string(字符串类型)
  • array(数组类型)
  • tuple(元组类型)
  • enum(枚举类型)
  • any(任意类型)
  • null 和 undefined 类型
  • void 类型
  • never 类型
  • object 对象类型
  • 以及自定义类型 type 、interface

我们可以看到 TS 的类型是可以自定义的,所以 TS 中的类型(变量的类型)是无限、不可枚举的

如何理解 TS 中的类型:

将类型视作为值的集合;

当我们定义一个变量时,约定其类型,其实是想显示的要告诉类型检查器,约定的变量是一个符合什么类型的值,如果操作中有类型不匹配的情况,则需要进行报错;

如下韦恩图所示,表示 TS 中 number类型所包含的值,所有数字的值组成的集合构成了完整的 number 类型;

流程图 (4).jpg 我们约束变量 a 的类型为 number,那么当 a 被赋予任何数字类型的值时,都不会报错,但是当其被赋予其他类型的值时,就会发生报错;

let a: number = 1;
a = 2; // ✅ ok
a = -3.15; // ✅ ok
a = 'hello'; // ❌ Type 'string' is not assignable to type 'number'.

变量 a 可以是 number 值集合中的任意一个值,但是 'hello'字符串类型的集合并非是 number 值集的子集,没有任何交集,所以当我们想要将字符串赋值给变量 a 是会报出,“不能将类型 string 分配给类型 number”;

流程图 (5).jpg

什么是 “可赋值(assignable)” 

在 “类型是值的集合” 的语境下,我们可以理解 “Type 'string' is not assignable to type 'number'” 实际上是告诉我们这个复制操作并没有达到可赋值的要求:

  • 类型与类型(是否子集关系) => 集合 A 是否是 集合 B 的子集
  • 值与类型(是否成员)=> 元素 A 是否是 集合 B 的成员

满足两种条件的任意一种条件时,在赋值时就不会有报错;

// 成员
type A = 'A';
type B = 'B';
type AB = A | B;

const a: AB = 'A'; // ✅ 因为 值“A” 是 集合 (A 并 B) 的成员
const c: AB = 'C'; // ❌ Type '"C"' is not assignable to type 'AB'.

// 子集
type AB12 = 'A' | 'B' | 12; // 三个值的集合 { 'A', 'B', 12}
const ab = Math.random() < 0.5 ? 'A' : 'B'; // 两个值的集合 {'A', 'B'}
const ab12: AB12 = ab; // ✅ 集合{ 'A', 'B' }是集合{ 'A', 'B', 12}的子集

TS 术语和集合术语的映射

TS 术语集合术语(类型所对应的值集合)维恩图描述
never空集 是集合中最小的集合,他不包含任何值,所以没有值可以赋给一个 never 类型的变量
字面类型(type A = ‘A')单元素集合除空集之外的最小集合,也称为单位类型,通常的是多个单位类型组成联合类型
值 可赋值到 T (let a: number = 1;a = 2;)值 是 T 的一个成员单个值可以复制到 T 是,值属于集合的成员;
T1 可赋值到 T2  ( type A = 'this is type A'; type B = A)T1 是 T2 的子集两个类型全等,即类型对应的值集合相同也可赋值;
T1 extends T2 (interface A textends B {})T1 是 T2 的子集值集合父子集关系也可赋值;
T1   T2(联合类型)T1 和 T2 的交集联合类型的 keyof 一定是空集;因为不能保证任何键属于联合类型中的一个值;
T1 & T2(交叉类型)T1 和 T2 的交集集合取交集;类型是每个类型中属性的并集;
Unknown全集

总结:

  • 类型是什么

值的集合;集合可以是有限的(如boolean或字面类型)也可以是无限的(如number string 类型等);

  • 什么情况下可以赋值:

    • 值是集合的一个成员;
    • 两个集合是父子关系;