前言

关于 TypeScript 的一些基本情况以及与 JavaScript 之间的比较,大家都很清楚了,毕竟到写这篇博客时,TypeScript 最新版本都已经是到 3.9 了,也不算是新东西了。我自己使用它来开发都好久了,但是之前一直是没有从头到尾的去整理一遍,都是在码代码的时候直接现查现用。前段时间因为工作需要,去看了一些别人写的库,一些写法看得是非常的陌生,想了想,还是要开始补课了。

重新学习一遍,最先就先从类型开始搞起。作为 JavaScript 的超集,自然是要支持 JavaScript 的数据类型,包括 布尔值、数字和字符串,还有对象,这些与 JavaScript 并没有什么区别,就不在详细展开了。这里就简单说一下 JavaScript 所不支持的类型,比较重要的就是元组类型枚举类型

元组 Tuple

元组类型允许表示一个已知数量类型的数组,各元素的类型不必相同。

const x: [string, number] = ['hello', 10]

可以把它看做是数组的扩展,其实就是一个严格的数组类型。

一般的数组,只是指定了元素的类型。但是对于元组来说,它在声明数组的时候,同时规定了数组中每个索引处的元素类型。这里需要注意一点的就是对于越界元素,也就是超出定义长度的索引处的元素时,在2.6及之前的版本,越界元素的类型会使用联合类型替代,而在2.6之后不允许访问越界元素。

let x: [string, number] = ['hello', 10]
console.log(x[0].substring(1))  // 'ello'
x = [10, 'hello']  // 错误 
x[2] = "world"; // 错误

枚举 Enum

enum类型是对JavaScript标准数据类型的一个补充,声明一组带名字的常量。

其实枚举类型的本质就是键值对形式的对象。

// 声明
enum nums {
    zero,
    one,
    two
}

// 编译后的 js
var nums;
(function (nums) {
    nums[nums["zero"] = 0] = "zero";
    nums[nums["one"] = 1] = "one";
    nums[nums["two"] = 2] = "two";
})(nums || (nums = {}));

枚举成员

从枚举类型的定义来看,它是声明了一组常量,所以枚举成员又可以成为枚举常量,所以只要值是常量即可。

既然是常量,那么就是规定了枚举成员是只读的,不能修改重新赋值。

我们可以通过 2 种方式指定枚举成员值

  • 字面量

    通过数字字面量或字符串字面量指定

  • 表达式或声明

    返回计算结果或常量的运算表达式或函数表达式、声明等

基本分类

枚举可以按照枚举成员分为不同的类别:

  • 数字枚举类型

    enum nums {
        zero,  // 第一个成员的值默认为0
        one,  // 1 不带初始化器 值为上一成员的值+1
        three = 3,  // 3 使用初始化器并指定初始化的常数
        four,  // 4 不带初始化器 值为上一成员的值+1
        five = nums.zero,  // 0 也可以使用已经声明的枚举的枚举成员的引用
        six = returnNumber()  // 6 可以使用表达式或函数调用
      }
    
      function returnNumber() {
        return 6
      }
  • 字符串枚举类型

    要注意的一个是,字符串枚举中,每个成员都必须用字符串字面量,不允许使用计算值。

    enum nums {
      zero = 'ZERO',
      one = 'ONE',
      two = 'TWO',
      three = fn(),   // 错误
      four = four  // 错误
    }
    
    function fn() {
      return 'THREE'
    }
    
    var four = 'F' + 'OUR'

    字符串枚举的枚举成员需要初始化,如果没有初始化,会默认给成员加上数字常量的值,这时,它就是下面的异构枚举类型了。

  • 异构枚举类型

    enum nums {
        zero,
        one = 'ONE',
        two = 'TWO'
    }

数字枚举在编译后生成反向映射表,即除了生成键值对的集合,还会生成值键对的集合。而字符串枚举并不会,对比下这两种实际生成的代码:

数字枚举编译结果

字符串枚举编译结果


按照枚举的声明方式,可以分为下面这三种:

  • 普通枚举

    上面示例中的都是普通枚举,就不详细展开了

  • 常量枚举

    即使用 const 修饰符来强调当前枚举类型,常量枚举不同于常规的枚举,他们会在编译阶段被删除。通常为了避免在额外生成的代码的开销和非直接的对枚举成员的访问,可以使用 常量枚举。

    枚举成员不允许使用计算值,枚举成员在使用的地方会被内联进来。

    const enum nums { zero, one, two }
    let numsArr = [nums.zero, nums.one, nums.two ]
    
    // 最终编译后的代码:
    // var numsArr = [0 /* zero */, 1 /* one */, 2 /* two */];
  • 外部枚举

    使用 declare 关键词来声明一个枚举,用来描述已经存在的枚举类型的形状。

    外部枚举类似于类型断言,只要在开发中有这个声明,意味着在当前开发环境上下文中一定存在当前这个对象,可以随意使用当前对象。

    外部枚举和非外部枚举之间有一个重要的区别,在正常的枚举里,没有初始化方法的成员被当成常数成员。 对于非常数的外部枚举而言,没有初始化方法时被当做需要经过计算的。

    declare enum nums { zero = 0, one, two }

  • 外部常量枚举

    使用 declareconst 关键词联合声明的枚举类型,和常量枚举没有太大的区别。

    declare const enum nums { zero, one, two }

Any

any可用于表示任意类型,通常用于在开发阶段还不清楚类型的变量,例如用户输入或第三方代码库等,或者是将 原先的 js 代码改为 ts 时的处理。建议非必要情况少用,毕竟 TypeScript 的一个优异之处就是强类型。

const x: any = 'TypeScript'
const y: number = 12
const z: any = true

Void 、Null 和 Undefined

void类型像是与any类型相反,它表示没有任何类型。变量值只能是 undefined 和 null 。

对于undefinednull这两种类型,它们的值也就是undefinednull,默认情况下是所有类型的子类型(除了下面的never类型),也就是可以赋值给其他类型的变量;但是如果在编译配置里开启了严格的 null 检查 (--strictNullChecks: true),nullundefined就只能赋值给any和它们各自(有个例外, undefined可以赋值到 void)。

function fn(msg: string): void {
  console.log(msg)
}

const x: any = null  
const y: number = undefined  // 严格 null 检查会报错

Never

never类型表示的是那些永不存在的值的类型。它是任何类型的子类型,也可以赋值给任何类型;然而,没有类型是never的子类型或可以赋值给never类型(除了never本身之外)。 即使 any也不可以赋值给never

// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
  throw new Error(message);
}
function infiniteLoop(): never {
  while (true) {
  }
}

const x: never = null  // 错误
const y: never = undefined  // 错误

参考

TypeScript 官网

typescript 枚举

一文让你彻底掌握 TS 枚举


这个我不会啊
16 声望0 粉丝