可以将 TS 中的常用基础类型细分为两类:

  • JS 中已有类型:
    • 原始类型:number、string、boolean、null、undefined、symbol
    • 对象类型:object(数组、对象、函数等对象)
  • TS 新增类型:
    • 联合类型、自定义类型(类型别名)、接口、元组、字面量类型、枚举、void、any等

      类型注解

      例:const name: string = 'xinxianying'
      作用:为变量添加类型约束
      类型注解就是在声明变量的时候,在变量名右侧通过冒号:声明变量的类型,约定了什么类型,就只能给变量赋值该类型的值。如果没有声明变量类型,TS 默认会自动推断类型。 ```typescript // 主动指定类型 let name: string = ‘xinxianying’ name = ‘li’ // 不报错 name = 10 // 报错

// TS 自动推断 name 为 string 类型 let name = ‘xinxianying’ name = ‘li’ // 不报错 name = 10 // 报错

  1. <a name="BWRsP"></a>
  2. ## 原始类型(基础类型、简单类型)
  3. 特点:简单。这些类型完全按照 JS 中类型的名称来书写(小写)。
  4. <a name="q6qYk"></a>
  5. ### String
  6. ```typescript
  7. const say: string = 'hello'

Number

const num: number = 100

Boolean

const isDone: boolean = true

null

const n: null = null

undefined

const u: undefined = undefined

Symbol

const s: symbol = Symbol()

对象类型(复杂类型、引用类型)

特点:对象类型在 TS 中更加细化,每个具体的对象都有自己的类型语法。

Array 数组类型

数组类型的两种写法:(推荐使用 number[]

// 指定数组元素只能为 number 类型
const arr: number[] = [1, 2, 3]

// 第二种方式
const arr: Array<number> = [1, 2, 3]

联合类型

由两个或多个其他类型组成的类型称为 联合类型,表示其值可以是这些类型中任意一种。
语法:用 |(竖线)隔开不同的类型 const s: string | number = 1
注意:复杂类型声明联合类型时,必须用小括号()包起来。

// 复杂类型声明联合类型时,必须用小括号()包起来
const arr: (number | string)[] = [1, 'a', 2, 'c']
const arr: Arrat<number | string> = [1, 'a', 2, 'c']

/*
*    如果不是用小括号,那下面的类型注解则变成了:
* arr 是 number 类型,或者是 stirng 类型的数组
*/ 
const arr: number | string[] = 1

类型别名(自定义类型)

为任意类型起别名。
当同一种复杂的类型被多次使用时,可以通过类型别名,简化该类型的使用。
语法:

  • 使用 type 关键字创建类型别名。
  • 可以是任意合法的变量名称。
  • 创建类型别名后,直接使用该类型别名作为变量的类型注解即可。 ```typescript type CustomArray = (number | string)[]

const arr: CustomArray = [1, ‘a’, 2, ‘b’]

<a name="uoepg"></a>
### 函数类型
指函数参数和返回值的类型。如果声明了函数返回值类型,那么该函数必须 **return **一个该类型的值。<br />函数类型声明方式有两种:

- 单独指定参数、返回值的类型
- 同时指定参数、返回值的类型
<a name="zImtC"></a>
#### 单独指定参数、返回值的类型
```typescript
// 方式一:函数声明
function getName(name: string): string {
  return name
}
say(1) // 报错
say('xinxianying') // 不报错


// 方式二:函数表达式
const add = (num1: number, num1: number): number => {
  return num1 + num2
}

同时指定参数、返回值的类型

这种形式只适用于函数表达式。
const add: (num1: number, num2: number) => number = (num1 + num2) => {}

const add: (num1: number, num2: number) => number = (num1 + num2) => {
  return num1 + num2
}

void 类型

如果函数没有返回值,那么该函数的返回值类型为: void

function log(text: string): void {
  console.log(text)
}

可选参数

使用函数实现某个功能时,有些参数并不是必需要传,这种情况下,在给函数参数指定类型时,就可以使用可选参数。
语法:在非必传的参数名称后面添加 (问号)即可。
注意:可选参数只能出现在参数列表的最后。

function sayName(obj: {first: string, last?: string}){
  // 因为 last 非必传,有可能为 undefined,所以调用 `toUpperCase` 可能会报错
  console.log(obj.last.toUpperCase()) // 报错

  console.log(obj.last?.toUpperCase()) // 不报错
}

sayName({ first: 'li' }) // 不报错
sayName({ first: 'li', last: 100 }) // 报错
sayName({ first: 'li', last: 'xiao' }) // 不报错

匿名函数

当一样函数出现在 TypeScript 可以确定它如何被调用的地方的时候,这个函数的参数会自动的指定类型。

const names = ['xin', 'wei', 'li']
names.forEach((name) => {
  // 此时 TS 推断 name 类型为 `string`,所以调用 String 的方法不会报错
  console.log(name.toUpperCase())
})

Object 对象类型

JS 中对象是由属性和方法构成的,而在 TS 中对象的类型就是在描述对象的结构(有什么类型的属性和方法)。
对象的属性或方法也可以是可选的,可选属性的语法和函数的可选参数语法一致,使用 (问号)来标识。

// 声明对象类型,可以用`,`或者';'分割
function move(pt: { x: number, y:number }){
}

const person: {
  name: string,
  age: number,
  sayHi(text: string): void,
  run: () => void,
  eat?: boolean
} = {
  name: '辛宪英',
  age: 22,
  sayHi('hello'){},
  run(){}
}

interface 接口类型

当一个对象类型被多次使用时,一般会使用 接口interface)来描述对象的类型,以达到复用的目的。
语法:

  • 使用 interface 关键字声明接口
  • 接口名称可以是任意合法的变量名称
  • 接口声明后,直接使用接口名称作为变量的类型
  • 因为每行只能有一个属性类型,因此属性类型后可以不写分号(;) ```typescript interface Person { name: string; age: number; sayHi(): void; eat?: boolean; }

const p1: Person = { name: ‘辛宪英’, age: 22, sayHi(){} }

<a name="runib"></a>
#### 接口继承
当两个接口之间出现相同的属性或方法时,可以将公共的属性或方法抽离出来,通过继承实现复用。<br />语法:

- 使用 **extends**(**继承**)关键字继承
- 继承后将拥有被继承的属性或方法
```typescript
interface Point2D {
  x: number;
  y: number;
}

// 重复属性
interface Point3D {
  x: number;
  y: number;
  z: number;
}

// 使用继承的方式
interface Point3D extends Point2D {
  z: number;
}

const p3: Point3D = { x: 1, y: 2, z: 3 }

interface(接口)与 type(类型别名)的对比

相同点:

  • 都可以给对象指定类型

不同点:

  • 接口只能为对象指定类型
  • 接口可以继承
  • 类型别名不仅可以为对象指定类型,还可以为任意类型指定别名
    interface P {}
    type P = {}
    type S = string | number
    

    Tuple 元组类型

    元组Tuple)类型是另一种类型的数组,它确切地知道包含多少个元素,以及特定索引对应的类型。
    // 长度为 2,且每项数据类型都为 number
    const position: [number, number] = [32.433759, 44.874275]
    

    字面量类型

    当需要控制参数的值为指定范围内的结果时,可以使用字面量类型。
    TS 中,任意字符串和 JS 字面量(如:对象、数字等…)都可以作为 TS 中的类型: ```typescript let str1 = ‘hello’ // 类型推论为 string const str2 = ‘hello’ // 类型推论为 hello

let num: 18 = 18 // 类型指定为 18,且值也只能为 18 了

示例:在贪吃蛇游戏中,游戏的方向的可选值只有“上、下、左、右”中任意一个,相比于 **string** 类型,使用字面量更加精确、严谨。
```typescript
function changeDirection(direction: 'up' | 'down' | 'left' | 'right') {
}

enum 枚举类型

定义一组命名常量。它描述一个值,该值可以是这些命名常量中的一个。
枚举功能类似于上面的字面量类型+联合类型组合功能,用以表示一组明确的可选值。
语法:

  • 使用 enum 关键字定义枚举
  • 约定枚举名称、枚举中的值以大写字母开头
  • 枚举中的多个值至简用,(逗号)分隔
  • 可直接使用枚举名作为类型注解
  • 和对象一样,通过 .(点)语法访问枚举成员 ```typescript enum Direction { Up, Down, Left, Right }

function changeDirection(direction: Direction) { }

changeDirection(Direction.Up)

<a name="HeVvN"></a>
#### 枚举成员的值
枚举成员的默认值为从 0 开始自增的数值(类似数组的下标),当然也可以给枚举中的成员初始化值。
```typescript
enum Direction { 
  Up,
  Down
}
console.log(Direction.Up) // 0
console.log(Direction.Down) // 1

数字枚举

枚举成员的值,为数字的枚举,我们称为:数字枚举
当修改数字枚举某一个成员的值后,后面的成员根据当前修改的值自增。

// 后面的成员根据当前修改的值自增。
enum Direction { 
  Up = 10,
  Down, // 11
  Left, // 12
  Right // 13
}

// 指定所有成员的值
enum Direction {
  Up = 2,
  Down = 4,
  Left = 6,
  Right = 8
}

字符串枚举

枚举成员的值,为字符串的枚举,我们成为:字符串枚举
字符串枚举没有自增长行为,因此,字符串枚举的每个成员都必须有初始值。

enum Direction {
  Up = 'UP',
  Down = 'DOWN',
  Left = 'LEFT',
  Right = 'RIGHT'
}

枚举的特点及原理

枚举是 TS 为数不多的非 JavaScript 类型级扩展(不仅仅是类型)的特性之一。
枚举不同于其他类型仅仅被当做类型,枚举还提供值(枚举成员都是有值的)。
因此,其他类型会在编译为 JS 代码时自动移除,但枚举类型会被编译成 JS 代码。

// 编译前
enum Direction {
  Up = 'UP',
  Down = 'DOWN',
}

// 编译后
var Direction
(function (Direction){
  Direction['Up'] = 'UP'
  Direction['Down'] = 'DOWN'
})(Direction || (Direction = {}))

枚举与上面的字面量类型+联合类型组合的功能类似,都可以用来表示一组明确的可选列表。

any 任意类型

原则:不推荐使用 any,这会让 TypeScript 变成“AnyScript”(失去 TS 类型保护的优势)。
隐式具有 any 类型的情况:

  • 声明变量不提供类型也不提供默认值时
  • 函数参数不加类型时

当值的类型为 any 时,可以对该值进行任意操作,而不会有错误提示。

const obj: any = {
  x: 0          
}

// 以下静态校验都不会报错

// 访问不存在的属性或方法
obj.text
obj.foo()
// 当做函数调用
obj()
// 赋值其他类型的值
obj = 'hello'
// 赋值给其他类型的变量
const n: number = obj

类型推论

在 TS 中,某些没有明确指定类型的地方,TS 的类型推论机制会帮助提供类型。
以下几种情况可以省略类型注解:

  • 声明变量并初始化时

const a = 1

  • 决定函数返回值时

function show(text: string) { return text }
注意:如果声明变量但没有立即初始化值时,必须手动添加类型注解。const a: number

function sayName(obj: {first: string, last?: string}){
  // 因为 last 非必传,有可能为 undefined,所以调用 `toUpperCase` 可能会报错
  console.log(obj.last.toUpperCase()) // 报错

  console.log(obj.last?.toUpperCase()) // 不报错
}

sayName({ first: 'li' })

类型断言

有时 TS 推论的类型太宽泛(不具体),这时可以使用 类型断言 来指定更具体的类型。
示例:

// 获取页面上的 a 标签,此时 TS 推论的类型为 HTMLElement
const aLink = document.getElementById('link')

// 但是 HTMLElement 只包含所有标签的公共属性,没有 a 标签特有的属性(如: herf)
console.log(aLink.herf) // undefined

// 使用类型断言,获取更具体的类型
const aLink = document.getElementById('link') as HTMLAnchorElement
// 或者
const aLink = <HTMLAnchorElement>document.getElementById('link')

console.log(aLink.herf) // ...

typeof

在 JS 中使用 typeof 操作符可以获取数据的类型:
console.log(typeof 'hello') // string
在 TS 中,可以使用 typeof 在类型上下文中引用变量或属性的类型(类型查询)。

// JS
console.log(typeof 'hello') // string

// TS - 不使用 typeof
const p = { x: 1, y: 1 }
function onPoint(point: { x: number, y: number }){}
onPoint(p)

// TS - 使用 typeof
function onPoint(point: typeof p){}