在进入正题前先说下在TS中声明的变量默认是全局的。也就是如果你在1.ts文件中声明了一个变量testStr,在其他ts文件中就不能再次声明testStr这个变量了。解决的办法是每个ts文件内加上export {}语句将文件变为模块文件。

# 基础数据类型

TS中冒号后面的都为类型标识

# 原始值

JS中有六种原始值:Undefined、Null、Boolean、Number、String和Symbol。它们对应TS中的写法如下:

const undefined: undefined = undefined
const : null = null
const bool:boolean = true;
const num:number = 10;
const str:string = 'hello world';
// Symbol表示独一无二
const s1 = Symbol('key');
const s2 = Symbol('key');
console.log(s1 == s2); // false

这里重点说下null和undefined,在TS中null和undefined有它们各自的类型分别是null和undefined。

在非严格模式下null、undefined是所有其他类型的子类型,我们可以将null、undefined赋值给其他类型。在严格模式下null和undefined的只能赋值给unknow、any和它们自身,undefined还可以赋值给void。

// 非严格模式下可以赋值
const a: number = null
const b: string = undefined
const c: boolean = null


// 严格模式下只能赋值给unknow、any和它们自身
// 无论是否是严格模式undefined都可以赋值给void类型
const d: void = undefined
const e: unknown = null
const f: null = null
const g: undefined = undefined
const h: any = null

TS中严格模式可以通过配置文件tsconfig.json进行配置

{
  "compilerOptions": {
    //...
     "strict": true, /* Enable all strict */
     "strictNullChecks": true, /* Enable strict null checks. */
  }
}

任何类型的子类型,如果strictNullChecks的值为true,则不能把null 和 undefined付给其他类型

let name:number | boolean;
name = null;

# 数组

数组类型可以使用下面两种方式编写,下面以创建一个存放数字数组为例:

const arr1:number[] = [1, 2, 3];
const arr2:Array<number> = [1, 2, 3]

上面声明的两个数组只能存放数字,如果我们想放字符串类型该怎么办呢?

let arr3:(number|string)[] = [1,'2',3]; //使用联合类型声明数组
let arr4:Array<number | string> = [1,'2',3]; // 使用泛型方式声明

# 元组类型

元组和JS中的数组类似,元组类型用来表示一个具有固定数量的元素的数组,且这些元素的类型是已知的。

声明一个元素类型:

// 元素类型需要和声明一一对应
let tuple:[string, number, boolean] = ['hello', 10, true];

给元组中添加元素,只能存放元组声明时指定的类型。

// 不能像下面这样通过索引给元组添加元素
// tuple[3] = 1

touple.push(1)
touple.push('a')
touple.push(true)

// 不能放元组中没有声明的类型
// touple.push({})

console.log(touple) // ["a", 1, false, 1, "a", true]

# 枚举类型

枚举是一种为数值集提供更友好名称的方式

enum Color {
  Red,
  Green,
  Blue,
}
let c: Color = Color.Green;

console.log(c) // 1

// 可以枚举也可以反举
console.log(Color[0]) // 'Red'

编译成JS后的结果

(function () {
  'use strict';
  var Color;
  (function (Color) {
      Color[Color["Red"] = 0] = "Red";
      Color[Color["Green"] = 1] = "Green";
      Color[Color["Blue"] = 2] = "Blue";
  })(Color || (Color = {}));

  var c = Color.Green; // 1

  console.log(Color[0]); // 'Red'
  console.log(c);
}());

从上面的例子可以看出枚举的索引默认从0开始,我们也可以手动设置枚举的值:

enum Color {
  Red = 10,
  Green = 5,
  Blue,
  Orange = 'orange',
  // Enum member must have initializer, Yellow需要手动赋初始值
  Yellow = 1
}

console.log(Color.Red) // 10
console.log(Color[10]) // 'Red'

console.log(Color.Blue) // 6

console.log(Color.Orange) // orange

console.log(Color['orange']) // Element implicitly has an 'any' type because index expression is not of type 'number'

// console.log(Color.Yellow) // orange

从上面这个例子我们可以得出四个结论:

  1. 枚举成员的初始值可以手动设置
  2. 如果枚举成员没有赋初始值,它的上一个成员的初始值是数字,则它代表的值是上一个成员初始值加1,如枚举成员Blue;如果它的上一个成员的初始值是字符串,那么它必须手动赋初始值才行,如枚举成员Yellow。
  3. 枚举成员的初始值可以是数字或字符串
  4. 数字枚举可以反举,字符串枚举不行,如枚举成员Orange

# 常量枚举

Const枚举是在枚举上使用Const修饰符定义的枚举,可以避免在访问枚举值时产生额外的代码和额外的间接开销。

const enum Enum {
  A = 1
}
console.log(Enum.A)

上面这个枚举使用const修饰后得到的编译结果为

(function () {
  'use strict';
  console.log(1 /* A */);
}());

不用const修饰时得到的编译结果为

(function () {
  'use strict';
  var Enum;
  (function (Enum) {
      Enum[Enum["A"] = 1] = "A";
  })(Enum || (Enum = {}));
  console.log(Enum.A);
}());

可以看出使用const修饰后编译后产生的代码量更小。Const枚举只能使用常量枚举表达式,而且与常规枚举不同,它们在编译过程中会被完全删除,因为const枚举不能有计算成员,常量枚举不能反举。

# 外部枚举

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

declare enum Enum {
    A = 1,
    B,
    C = 2
}

# unknown类型

当我们声明的变量的时候并不知道这个变量会接收什么样的值时可以给变量赋值为unknown类型

let a: unknown
a = 1
a = true
a = 'hello'

但是对象不行

let obj: unknown
obj = {
  name: 'f'
}

// Object is of type 'unknown'
console.log(obj.name, obj.age)

# any类型

如果我们在编写代码的时候不希望使用类型检测可以将变量声明成any类型

const arr: any = [1, '2', true, {}]

与unknown不同,any类型的变量允许访问任意属性,甚至是不存在的属性。这些属性包括函数,TypeScript不会检查它们是否存在或类型:

const num1: any = 1
num1.print()
num1.toFixed()

let num2: unknown = 4;

// Object is of type 'unknown'
num2.toFixed();

any类型带来便利的同时也失去了类型安全检测,类型安全是使用TypeScript的主要动机之一,应该尽量避免使用any类型。

any的使用场景:在某些情况下,并不是所有类型信息都可用,或者它的声明将花费不适当的工作量。这些可能发生在没有TypeScript或第三方库的代码中。在这些情况下,我们可能希望选择退出类型检查。为此,我们用any类型来标记这些值。

# void类型

void类型一般用于函数的返回值,表示这个函数没有返回值。

function warnUser(): void {
  console.log("This is my warning message");
}

将一个变量声明成void的类型是没有意义的,因为只能给它们赋值null(非严格模式)或undefined。

# never类型

never类型是任何类型的子类型,never代表不会出现的值。除了never类型不能把其他类型赋值给never类型。never类型表示永不出现的值的类型。

// Function returning never must not have a reachable end point
function error(message: string): never {
  throw new Error(message);
}

// Inferred return type is never
function fail() {
  return error("Something failed");
}

// Function returning never must not have a reachable end point
function infiniteLoop(): never {
  while (true) {}
}

# Symbol类型

Symbol表示独一无二

const s1 = Symbol('key');
const s2 = Symbol('key');
console.log(s1 == s2); // false

# BigInt类型

const num1 = Number.MAX_SAFE_INTEGER + 1;
const num2 = Number.MAX_SAFE_INTEGER + 2;
console.log(num1 == num2)// true

// BigInt只能和BigInt相加
const num3 = BigInt(Number.MAX_SAFE_INTEGER) + BigInt(1)
const num4 = BigInt(Number.MAX_SAFE_INTEGER) + BigInt(2)
console.log(num3 == num4) // false

BigInt类型只能和BigInt类型进行操作

// Operator '+' cannot be applied to types 'bigint' and '2'.
let num3 = BigInt(Number.MAX_SAFE_INTEGER) + 2;

# object对象类型

object表示非原始类型

let create = (obj:object):void=>{}
create({});
create([]);
create(function(){})