这是我参与「第四届青训营」笔记创作活动的的第6天
TypeScript 类型
常用基础类型
划分
可以将TS的常用基础类型细分为两类:
- JS已有类型
- 原始类型(基础数据类型):number/string/boolean/null/undefined/symbol
- 对象类型:object
- TS新增类型
- 联合类型,自定义类型(类型别名),接口,元组,字面量类型,枚举,void,any等
原始类型
原始类型特点: 简单,完全按照JS中的类型来书写。
对象类型
特点:对象类型在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中叫联合类型(由两个或多个其他类型组成的类型,表示可以是这些类型中的任意一种)
类型别名
类型别名(自定义类型):为任意类型奇别名
使用场景:当同一类型被多次使用时,可以通过类型别名,简化类型使用。
其他
type关键字创建类型别名
函数类型
两种写法(有返回值):
- 单独指定参数,返回值类型:
// 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('大佬')
**函数参数可传可不传**:

#### 对象类型
js中的对象是否属性和方法构成,TS中的对象类型就是在描述对象的结构(有什么类型的属性和方法)
**写法**
let person: {name: string; age: number; sayHi(): void; greet(name: number):void} = { name: '大佬好', age: 18, sayHi() {}, greet(name) {} }
可选属性
对象的属性或方法,可选可不选,就可以用可选属性(用?表示可选),如:
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 类型别名
-
相同点:都可以给对象指定类型
-
不同点:
- 接口只能为对象指定类型
- 类型别名可以为任意类型指定别名
接口继承 extends
如果两个接口之间有相同的属性或方法,就可以将公共的属性或方法剥离出来,通过继承来实现复用。 eg:
interface Node1 {x: number,y: string}
interface Node2 {x: number,y: string, z: number}
上面代码有重复的,很繁琐,可以有关键字`extends`继承实现接口Node2继承Node1:
interface Node2 extends Node1 {z: number}
用extends继承公共的属性或方法后,是不是觉得上段代码恒简洁,恒舒服!!!
元组
元组(Tuple)类型是另一种类型的数组,它确切的知道包含多少个元素,以及特定索引对应的类型。
eg:
类型推论
在TS中,某些没有明确指出类型的地方,TS的类型推论机制就会提供帮助。换句话说:由于类型推论的存在,有些情况下,类型注解可以省略不写—偷懒
发生类型推论的2种场景:
- 声明变量并初始化时
- 决定函数返回值时
类型断言 as
有些时候你会比TS更加想明确一个值得类型,因而可以用类型断言来指定更具体的类型。
eg:
<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')
 other egs

### 字面量类型
**来个栗子**:
// 思考一下,以下两个变量类型分别为? let str1 = 'hello dalao'
const str2 = 'hello dalao' 有人秒解答:都是string类型!!!
然而并非如此
正解:
通过类型推论机制就可以得到答案(鼠标放到str1,str2就会显示类型)
- str1是一个变量(let),其值可以是任意字符串,所以类型为
string - 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代码是自动移除,但是枚举类型的类型成员会作为对象的属性,而枚举成员的值会作为属性的值
枚举类型编译 
### any 类型
当值为any是,可以进行任意操作,失去TS类型保护优势, 所以不推荐使用`any`
let fruit: any = {name: '香蕉'} person.color = 'yellow' fruit() const shuiguo: string = fruit // 以上操作都不会有任何的类型错误提示,即使存在错误!!!
注意:
-
尽可能的避免使用any类型,除非临时使用
any来避免书写很长,复渣的类型(代码写到一半) -
其他隐式具有
any类型情况:- 声明变量不提供类型也不提供默认值
- 函数参数不加类型
// 隐式具有`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}){}
解释:
- 使用
typeof操作符获取 getnum 的类型,结果和第一种相同 typeof只能用来查询变量或属性的类型,无法查询其他形式的类型(如,函数调用的类型)
常用高级类型
划分
TS高级类型较多,重点介绍一下类型
高级类型:
- class类
- 类型兼容性
- 交叉类型
- 泛型,keyof
- 索引签名,索引查询类型
- 映射类型
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)
类继承
extends(继承父类),js中只有extends
implements(实现接口),TS提供的implements
咱们来看看implements与extends相比有何不同:
用implements关键字可以实现 Person类 继承 Singer的所有属性及方法,但是现在提示错误,原来啊,接口Singer里有的属性和方法都要写出来(写在Person类中),如下图:这样就不会提示错误了!
类成员可见性
TS提供了许多可见性修饰符,从而控制class的方法和属性对于class外的代码是否可见
可见性修饰符:
public:表示公有的,可以被任意地方访问,类成员默认为 public
class Animal{ // 任何地方可访问 // 也可不写,默认为public public say() { console.log('汪汪') } }
protected:表示受保护的,仅对其声明所在的类和子类(非实例对象)中可见。简单来说就是实例对象不可访问该属性或方法
可以看到实例对象dog只能访问say和walk方法,而不能访问run方法(run受到保护)
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