TypeScript 认识 | 青训营笔记

103 阅读10分钟

这是我参与「第四届青训营」笔记创作活动的的第6天

TypeScript 类型

常用基础类型

划分

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

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

原始类型

原始类型特点: 简单,完全按照JS中的类型来书写。

image.png

对象类型

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

数组类型

两种写法

// 1  推荐写法
const numbers: number[] = [1,2,3]
// 2
const strings: Array<string> = ['a', 'b', 'c']

如果是数组中既有number,又有string,该怎么写?

**联合类型**

const arr: (number | string)[] = [1,'a',2,'b'] :|(竖线)在TS中叫联合类型(由两个或多个其他类型组成的类型,表示可以是这些类型中的任意一种)

类型别名

类型别名(自定义类型):为任意类型奇别名

使用场景:当同一类型被多次使用时,可以通过类型别名,简化类型使用。 image.png

其他

type关键字创建类型别名

image.png

函数类型

两种写法(有返回值):

  1. 单独指定参数,返回值类型:
// 1.
// 第一第二number分别为参数num1,num2添加数字类型,第三个number则为返回值添加数字类型
function plus(num1: number, num2: number): number {
  return num 1+ num2
}
plus(1,2) // 3

// 2.
// 同上
const add = (num1: number, num2: number): number => {
  return num1 + num2
}
add(2,3) // 5

2.  同时指定参数,返回值类型

// 第一第二number分别为参数num1,num2添加数字类型,第三个number则为返回值添加数字类型 const add: (num1: number, num2: number) => number = (num1, num2) => { return num1 + num2 } add(2,3) // 5

:这种形式(第二种)只适合用于函数表达式

void(无返回值):

// void类型
function greet(name: string): void {
  console.log('hello',name)
}
greet('大佬')
**函数参数可传可不传**:

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ca3e984ce22f459287bae5b1c945c278~tplv-k3u1fbpfcp-zoom-1.image)

#### 对象类型

js中的对象是否属性和方法构成,TS中的对象类型就是在描述对象的结构(有什么类型的属性和方法)

**写法**

let person: {name: string; age: number; sayHi(): void; greet(name: number):void} = { name: '大佬好', age: 18, sayHi() {}, greet(name) {} }

image.png

可选属性

对象的属性或方法,可选可不选,就可以用可选属性(用?表示可选),如:

function myAxios(config: {url: string; method?: string}){}
myAxios({
  url: ''
  //
  method可不填
})
`_______________________________________________________`

`下面介绍TS新增的类型(js本来就没有的)`

### 接口 `interface`

#### 接口使用

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

解释:

1.  使用`interface`关键字声明接口
1.  接口名称可以是任意合法的变量名称(eg: IPerson)
1.  声明接口后,直接使用接口名称作为变量的类型
1.  因为每一行只有一个属性类型,因而属性类型后没有`;`(分号)

// 接口 interface IPerson { name: string age: number sayHi(): void }

let person1: IPerson = { name: '大佬', age: 16, sayHi(){} }

let person2: IPerson = { name: '大佬', age: 1, sayHi() {} }

接口 VS 类型别名

  • 相同点:都可以给对象指定类型

  • 不同点:

    • 接口只能为对象指定类型
    • 类型别名可以为任意类型指定别名

image.png

接口继承 extends

如果两个接口之间有相同的属性或方法,就可以将公共的属性或方法剥离出来,通过继承来实现复用。 eg:

interface Node1 {x: numbery: string}
interface Node2 {x: numbery: string, z: number}

上面代码有重复的,很繁琐,可以有关键字`extends`继承实现接口Node2继承Node1

interface Node2 extends Node1 {z: number}

用extends继承公共的属性或方法后,是不是觉得上段代码恒简洁,恒舒服!!!

元组

元组(Tuple)类型是另一种类型的数组,它确切的知道包含多少个元素,以及特定索引对应的类型。

eg:

image.png

类型推论

在TS中,某些没有明确指出类型的地方,TS的类型推论机制就会提供帮助。换句话说:由于类型推论的存在,有些情况下,类型注解可以省略不写—偷懒

发生类型推论的2种场景:

  1. 声明变量并初始化时
  2. 决定函数返回值时

image.png

类型断言 as

有些时候你会比TS更加想明确一个值得类型,因而可以用类型断言来指定更具体的类型。

eg:

image.png

<a href="http://www.jd.com" id="link">京东</a>

const aLink = document.getElementById('link')
aLink.href
// 此时访问不到href属性而提示报错,因为类型太过宽泛(不具体),无法操作href等a标签特有的属性和方法。

// 解决:使用类型断言
// 1.推荐使用
const aLink = document.getElementById('link') as HTMLAnchorElement
aLink.href
// 2.不常用
const aLink = <HTMLAnchorElement>document.getElementById('link')
![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/53af712c427341948e4621e70cacebe2~tplv-k3u1fbpfcp-zoom-1.image) other egs

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5b0e58e9b0a8448e9fb06895cf7d6535~tplv-k3u1fbpfcp-zoom-1.image)

### 字面量类型

**来个栗子**:

// 思考一下,以下两个变量类型分别为? let str1 = 'hello dalao'

const str2 = 'hello dalao' 有人秒解答:都是string类型!!!

然而并非如此

正解:

通过类型推论机制就可以得到答案(鼠标放到str1,str2就会显示类型)

  1. str1是一个变量(let),其值可以是任意字符串,所以类型为string
  2. str2是一个常量(const),它的值不是变化的只能是 ‘hello dalao’ ,所以类型为'hello dalao'

:这里的'hello dalao'就是一个字面量类型。也就是说某个特定的字符串也可以作为TS的类型。除字符串外,任意js字面量(数字,对象)都可以作为类型使用!

使用模式:一般情况下,字面量类型配合联合类型一起使用

使用场景:用来表示一组的可选值列表

eg:在贪吃蛇游戏里方向选择、只有上下左右任意一个

// direction 的值只能从四个值之中选
function changeDirection(direction: 'up' | 'down' | 'left' | 'right') {
  console.log(direction)
}
changeDirection('left') // left

**优势**:相比于string类型,字面量类型更加精准

### 枚举类型 enum

**枚举**:定义一组常量。它描述一个值,这个值可以是这些命名常量中的一个。`枚举类型`的功能类似于`字面量类型`+`联合类型`组合的功能。比如上述代码中的`direction`功能类似于枚举类型的功能

eg:

enum Direction {Up, Down, Left, Right} function changeDirection(direction: Direction){ console.log(direction) } changeDirection(Direction.Left) // 2 changeDirection(Direction.Up) // 0

注意

  • 形参direction类型为枚举Direction
  • 枚举成员是有值的,默认为:从0开始自增的数字(针对数字枚举)
  • 枚举成员可进行初始化值
// 
enum Direction {
  Up, // 默认0,以后自增+1
  Down, // 1
  Left, // 2
  Right // 3
}
// 
enum Direction {
  Up=10, // 10,以后自增+1
  Down, // 11
  Left, // 12
  Right // 13
}
// 枚举成员可进行初始化值
enum Direction {
  Up ='',
  Down = '12',
  Left= '1123',
  Right= '234'
}
// 字符串枚举没有自增行为,因此字符串枚举的每个成员都必须有初始值

**解释**:

1.  使用`enum`关键字定义枚举
1.  约定枚举类型名称,枚举中的值以大写字母开头
1.  枚举中的多个值之间通过`,`分隔开
1.  定义好枚举后,直接使用枚举名称作为类型注解
1.  访问枚举成员可用点(.)的形式

**特殊性**

-   枚举是TS的非JavaScript类型级扩展(不仅仅是类型)的特性之一
-   原因:其他类型仅仅被当作类型,而枚举不仅用作类型,还提供值(枚举类型都是有值的)
-   其他类型在编译为JS代码是自动移除,但是枚举类型的类型成员会作为对象的属性,而枚举成员的值会作为属性的值

枚举类型编译 ![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9aa8884e10474ce0af790031a6119585~tplv-k3u1fbpfcp-zoom-1.image)

### any 类型

当值为any是,可以进行任意操作,失去TS类型保护优势, 所以不推荐使用`any`

let fruit: any = {name: '香蕉'} person.color = 'yellow' fruit() const shuiguo: string = fruit // 以上操作都不会有任何的类型错误提示,即使存在错误!!!

注意

  • 尽可能的避免使用any类型,除非临时使用any来避免书写很长,复渣的类型(代码写到一半)

  • 其他隐式具有any类型情况:

    1. 声明变量不提供类型也不提供默认值
    2. 函数参数不加类型
// 隐式具有`any`类型

// 1.
let a
// 可以进行任意操作
a = 1
a()

// 2.
function add(num1,num2){}
// 可以进行任意操作
add(1,2)
add(1,'2')
add(2,false)

### typeof 操作符

typeof:可以在类型上下文中引用变量或属性的类型(类型查询)

使用场景:根据已有的变量的值,获取该值的类型,简化类型书写

let getnum = {x: 1, y: 2} function formatNum(num: typeof getnum){} formatNum(getnum) // 运用 typeof 改进代码书写 function formatNum(num: {x: number; y: number}){}

解释

  1. 使用typeof操作符获取 getnum 的类型,结果和第一种相同
  2. typeof只能用来查询变量或属性的类型,无法查询其他形式的类型(如,函数调用的类型)

常用高级类型

划分

TS高级类型较多,重点介绍一下类型

高级类型

  1. class类
  2. 类型兼容性
  3. 交叉类型
  4. 泛型,keyof
  5. 索引签名,索引查询类型
  6. 映射类型

class类

基本用法

  • class定义方法的类型注解
class Person{
  age: number
  gender: string
  constructor(age: number,gender: string) {
    this.age = age
    this.gender = gender
  }
}
const per = new Person(18,'男')
console.log(per.age,per.name) // 18 男

-   class定义方法的类型注解

class Point{ x=10 y=10 mult(n: number): void { this.x *= n this.y *= n } } const point = new Point() point.mult(10) console.log(point.x, point.y)

类继承

  1. extends(继承父类),js中只有extends

image.png

  1. implements (实现接口),TS提供的implements

咱们来看看implements与extends相比有何不同:

image.png

implements关键字可以实现 Person类 继承 Singer的所有属性及方法,但是现在提示错误,原来啊,接口Singer里有的属性和方法都要写出来(写在Person类中),如下图:这样就不会提示错误了!

image.png

类成员可见性

TS提供了许多可见性修饰符,从而控制class的方法和属性对于class外的代码是否可见

可见性修饰符:

  1. public:表示公有的,可以被任意地方访问,类成员默认为 public

class Animal{ // 任何地方可访问 // 也可不写,默认为public public say() { console.log('汪汪') } }

  1. protected:表示受保护的,仅对其声明所在的类和子类(非实例对象)中可见。简单来说就是实例对象不可访问该属性或方法

image.png 可以看到实例对象dog只能访问say和walk方法,而不能访问run方法(run受到保护)

  1. private:表示私有的,只在当前类中可见,对其实例对象及子类都不可见。简单来讲就是子类继承父类后访问不了父类中private的属性或方法

class Animal { // private 私有方法只能在内部(Animal类中)访问 private sleep(){ console.log('睡觉'); } walk() { this.sleep() console.log('走了'); } protected run() { this.sleep() console.log('跑啊'); } } 4. readonly:表示只读,用来防止在构造函数之外对属性进行赋值(该修饰符不能修饰方法) class Person{ // 只读属性 // 只要是readonly来修饰的属性,必须手动提供明确的类型 readonly age: number = 18

constructor(age: number) { // age 只能在构造函数中赋值 this.age = age } } 注意:接口或者{}表示的对象类型,也可使用readonly

总结:通过本次学习让我对TypeScript有了更深的认识