课程地址:【黑马程序员前端TypeScript教程,TypeScript零基础入门到实战全套教程】 https://www.bilibili.com/video/BV14Z4y1u7pi/?share_source=copy_web&vd_source=b1cb921b73fe3808550eaf2224d1c155
目录
3、TypeScript常用类型
3.1 类型注解
3.2 常用基础类型概述
3.3 原始类型
3.4 数组类型
3.5 联合类型
3.6 类型别名
3.7 函数类型
1. 单独指定参数、返回值的类型
2. 同时指定参数、返回值的类型
3 函数可选参数
3.8 void类型
3.9 对象类型
3.10 接口interface
3.10.1 接口interface
3.10.2 接口与类型别名的区别 interface vs type
3.10.3 接口继承 extends
3.11 元组tuple
3.12 类型推论
3.12.1 声明变量并初始化
3.12.2 决定函数返回值时
3.12.3 总结
3.13 类型断言
3.13.1 问题场景
3.13.2 类型断言
3.14 字面量类型
3.14.1 字面量类型是什么
3.14.3 作用
3.15 枚举 enum
3.15.1 枚举类型
3.15.2 枚举成员的值
3.15.3 数字枚举
3.15.4 字符串枚举
3.15.5 枚举的特点和原理
3.15.6 使用建议
3.16 any类型
3.17 typeof
3、TypeScript常用类型
TypeScript 是 JS 的超集,TS 提供了 JS 的所有功能,并且额外的增加了:类型系统。
所有的 JS 代码都是 TS 代码。
JS 有类型(比如,number/string 等),但是 JS 不会检查变量的类型是否发生变化。而 TS 会检查。
TypeScript 类型系统的主要优势:可以显示标记出代码中的意外行为,从而降低了发生错误的可能性。
1. 类型注解 给变量添加类型
2. 常用基础类型
3.1 类型注解
示例代码
let age: number = 18
类型注解:代码中的: number就是类型注解
作用:为变量添加类型约束,上述代码中,约定变量age类型为number(数值类型)
约定了什么类型,就只能给变量赋值该类型的值,否则就会报错。
3.2 常用基础类型概述
将TS中的常用基础类型分为两类:JS已有类型和TS新增类型
1、JS已有类型
1.1 原始类型 number string boolean null undefined symbol
1.2 对象类型 object 包括数组、对象、函数等对象
2、TS新增类型
联合类型、自定义类型、接口、元组、字面量类型、枚举、void、any等。
3.3 原始类型
一共六个,类型都是小写
number
string
boolean
null
undefined
symbol
代码实现
let 变量名: 类型名称 = 变量值
let age: number = 25let myName: string = 'yuyao'let isLoading: boolean = falselet a: null = nulllet b: undefined = undefinedlet s: symbol = Symbol()
3.4 数组类型
对象类型 object 包括数组、对象和函数等对象
特点:对象类型,在TS中更加细化,每个具体的对象都有自己的类型语法。
数组类型的两种写法
let numbers: number[] = [1, 3, 5]
let numbers2: Array<number> = [1, 2, 3]
推荐number[]写法。
3.5 联合类型
添加小括号,表示首先arr是数组,其次数组元素是number或者是string
let arr: (number | string )[] = [1, 'a', 4, 'b', 4]
另外一种情况,不添加括号,表示arr既可以是数字number,也可以是字符串数组。
let arr: number | string[] = 12
解释:| (竖线)在 TS 中叫做联合类型(由两个或多个其他类型组成的类型,表示可以是这些类型中的任意一种)。
注意:这是 TS 中联合类型的语法,只有一根竖线,不要与 JS 中的或(||)混淆了
3.6 类型别名
类型别名(自定义类型):为任意类型起别名。
使用场景:当同一类型(复杂)被多次使用时,可以通过类型别名,简化该类型的使用。
type CustomArray = (number | string)[]
let arr2: CustomArray = [1, 2, 'q']
解释:
1. 使用 type 关键字来创建类型别名。
2. 类型别名(比如,此处的 CustomArray),可以是任意合法的变量名称。
3. 创建类型别名后,直接使用该类型别名作为变量的类型注解即可。
3.7 函数类型
函数的类型实际上指的是:函数参数和返回值的类型。
为函数指定类型的两种方式:
1 单独指定参数、返回值的类型
2 同时指定参数、返回值的类型。
1. 单独指定参数、返回值的类型
函数声明的方式
function add(num1: number, num2: number): number {return num1 + num2
}
函数表达式的方式
add = (类型) => {}
const add1 = (num1: number, num2: number): number => {return num1 + num2
}
2. 同时指定参数、返回值的类型
const add2: (num1: number, num2: number) => number = (num1, num2) => {return num1 + num2
}
解释:当函数作为表达式时,可以通过类似箭头函数形式的语法来为函数添加类型。
注意:这种形式只适用于函数表达式。
3 函数可选参数
使用函数实现某个功能时,参数可以传也可以不传。
这种情况下,在给函数参数指定类型时,就用到可选参数了。
比如,数组的 slice 方法,可以 slice() 也可以 slice(1) 还可以 slice(1, 3)
function mySlice(num1?: number, num2?: number): void {console.log('起始索引', num1, '结束索引', num2)}
mySlice(1, 2)
可选参数:在可传可不传的参数名称后面添加 ?(问号)。
注意:可选参数只能出现在参数列表的最后,也就是说可选参数后面不能再出现必选参数
3.8 void类型
如果函数没有返回值,那么,函数返回值类型为:void
function greet(name: string): void {console.log('hello', name)
}
注意:void是TS新增的。
3.9 对象类型
JS 中的对象是由属性和方法构成的,
而 TS 中对象的类型就是在描述对象的结构(有什么类型的属性和方法)。
对象类型的写法:
// 对象类型
let person: { name: string; age: number; sayHi(): void} = {name: 'jack',age: 25,sayHi() {}
}
解释:
1. 直接使用 {} 来描述对象结构。属性采用属性名: 类型的形式;方法采用方法名(): 返回值类型的形式。
2. 如果方法有参数,就在方法名后面的小括号中指定参数类型(比如:greet(name: string): void)。
3. 在一行代码中指定对象的多个属性类型时,使用 ;(分号)来分隔。
- 如果一行代码只指定一个属性类型(通过换行来分隔多个属性类型),可以去掉 ;(分号)。
let person: { name: stringage: numbersayHi(): void} = {name: 'jack',age: 25,sayHi() {}
}
- 方法的类型也可以使用箭头函数形式(比如:{ sayHi: () => void })
let person: { name: string; age: number; sayHi: () => void } = {name: 'jack',age: 25,sayHi() {}
}
对象可选属性
在属性后面加个问号?
// 对象可选属性
function myAxios(config: {url: string; method?: string }) {}
myAxios({url: 'string'}
)
可选属性的语法与函数可选参数的语法一致,都使用?(问号)来表示。
3.10 接口interface
3.10.1 接口interface
当一个对象类型被多次使用时,一般会使用接口(interface)来描述对象的类型,达到复用的目的。
例如下面的对象
let person: { name: string; age: number; sayHi(): void } = {name: 'zyy',age: 18,sayHi() {console.log('hi');}
}
复用:类型别名与接口有相同点,也有不同点。
接口示例
interface IPerson {name: stringage: numbersayHi(): void
}
解释:
1. 使用interface 关键字来声明接口。
2. 接口名称(比如,此处的IPerson),可以是任意合法的变量名称。
3. 声明接口后,直接使用接口名称作为变量的类型。
4. 因为每一行只有一个属性类型,因此,属性类型后没有;(分号)。
声明好接口后,将接口名字作为对象的类型来定义对象。
let person1: IPerson = {name: 'zyy',age: 18,sayHi() {}
}
let person2: IPerson = {name: 'zyy',age: 19,sayHi() {}
}
3.10.2 接口与类型别名的区别 interface vs type
- interface 接口 与 type 类型别名的对比
- 相同点:都可以给对象指定类型
interface IPerson {name: stringage: numbersayHi(): void } type IPerson1 = {name: stringage: numbersayHi(): void }
- 不同点
- 接口,只能为对象指定类型
- 类型别名,不仅可以为对象指定类型,实际上可以为任意类型指定别名
type NumStr = number | string;
- 相同点:都可以给对象指定类型
3.10.3 接口继承 extends
如果两个接口之间有相同的属性或方法,可以将公共的属性或方法抽离出来,通过继承来实现复用。
例如,Point2D 和 Point3D,都有x、y两个属性,这样要重复写两次,可以这样做,但是很繁琐。
interface Point2D {x: numbery: number
}
interface Point3D {x: numbery: numberz: number
}
更好的方式,接口继承
interface Point2D {x: numbery: number
}
interface Point3D extends Point2D {z: number
}
解释:
1. 使用 extends(继承)关键字实现了接口 Point3D 继承 Point2D。
2. 继承后,Point3D 就有了 Point2D 的所有属性和方法(此时,Point3D 同时有 x、y、z 三个属性)。
接口继承的使用场景:有2个以上的接口有相同的属性,那么就可以将公共的属性或方法进行抽取到一个接口中,然后使用继承实现公共属性或方法的复用。
3.11 元组tuple
场景:在地图中,使用经纬度坐标来标记位置信息。 可以使用数组来记录坐标,那么,该数组中只有两个元素,并且这两个元素都是数值类型。
let position: number[] = [29.1, 23.2];
这样做可以,但是这样做不严谨。
使用 number[] 的缺点:不严谨,因为该类型的数组中可以出现任意多个数字。
更好的方式:元组(Tuple)。
元组类型是另一种类型的数组(或者说是数组的特例),它确切地知道包含多少个元素,以及特定索引对应的类型。
let position1: [number, number] = [29.1, 23.2];
这里使用元组来记录经纬度坐标是最合适的。
上述代码含义解释:
1. 元组类型可以确切地标记出有多少个元素,以及每个元素的类型。
2. 该示例中,元素有两个元素,每个元素的类型都是 number。
3. 该元组规定了只能有2个元素,再给第三个元素就会报错。
3.12 类型推论
ts中的类型是可以省略的。
为什么可以省略,因为ts有类型推论机制,也可以叫类型推断。
在 TS 中,某些没有明确指出类型的地方,TS 的类型推论机制会帮助提供类型。
换句话说:由于类型推论的存在,这些地方,类型注解可以省略不写!
自动触发类型推论机制的 2 种常见场景
- 声明变量并初始化时
- 决定函数返回值时。
3.12.1 声明变量并初始化
number类型是TS类型推论机制自动推断出来的。
疑问:省略这个类型后,类型保护和检查机制还存在吗
类型检查和保护机制仍然存在。
注意:对于只声明没有立即初始化的变量,还是可以改变其类型的,下面的情况不会报错。
但是对于只声明没有立即初始化的变量,还是建议在声明的时候手动加类型注解,这样类型机制可以起到检查和保护的作用。
3.12.2 决定函数返回值时
可以省略掉函数返回值类型。
注意:函数参数的类型是必须要写的!!!
3.12.3 总结
注意:这两种情况下,类型注解可以省略不写!
推荐:能省略类型注解的地方就省略(偷懒,充分利用TS类型推论的能力,提升开发效率)。
既然TS可以推断类型,那么我们就没必要手动再写一次,能省则省。
技巧:如果不知道类型,可以通过鼠标放在变量名称上,利用 VSCode 的提示来查看类型。
示例
可见,log方法的返回值类型是void。
3.13 类型断言
有时候开发人员会比 TS 更加明确一个值的类型,此时为了类型的准确性,可以使用类型断言来指定更具体的类型。
3.13.1 问题场景
使用场景:
此时,希望通过aLink去访问href属性的时候,此时是访问不到的。
HTMLElement是一个比较宽泛的类型,该类型只包含所有标签公共的属性或方法,对于像a标签特有的href等属性是访问不到的。
注意:getElementById 方法返回值的类型是 HTMLElement,该类型只包含所有标签公共的属性或方法,不包含 a 标签特有的 href 等属性。
因此,这个类型太宽泛(不具体),无法操作 href 等 a 标签特有的属性或方法。
解决方式:这种情况下就需要使用类型断言指定更加具体的类型。
3.13.2 类型断言
aLink只有所有标签共有的属性,比如访问id属性。
对于href这种特殊的属性,就不能访问。对于这种情况,就要使用类型断言来指定更加具体的类型。此时,开发人员比TS更加清楚aLink对应元素的类型是一个a标签,而不是一个宽泛的html元素。所以使用类型断言。
类型断言的使用
解释:
1. 使用 as 关键字实现类型断言。
2. 关键字 as 后面的类型是一个更加具体的类型(HTMLAnchorElement (a标签的类型)是 HTMLElement 的子类型)。
3. 通过类型断言,aLink 的类型变得更加具体,这样就可以访问 a 标签特有的属性或方法了
此时可以访问a标签的href属性
、
第二种使用类型断言的形式
使用 <> 语法,这种语法形式不常用知道即可
疑问:怎么知道a标签的类型是HTMLAnchorElement?怎么知道div的类型是什么,span的类型又是什么?
技巧:在浏览器控制台,通过 console.dir() 打印 DOM 元素,在属性列表的最后面,即可看到该元素的类型。
具体使用:
$0表示当前选中的元素。
到控制台中,打印$0就可以访问刚才的元素了。
使用console.dir()
拖到最后面,可以知道div的类型是HTMLDivElement。
3.14 字面量类型
是什么,为什么,怎么做
3.14.1 字面量类型是什么
下面两个变量的类型是什么?
let str1 = 'Hello TS';
const str2 = 'Hello TS';
猜测:str1是字符串类型,str2的类型是?
事实:str1是字符串类型string,str2的类型是字符串‘Hello TS'
不确定变量类型的时候,把鼠标放到变量上,利用类型推断查看该变量的类型。
解释:
1. str1 是一个变量(let),它的值可以是任意字符串,所以类型为:string。
2. str2 是一个常量(const),它的值不能变化只能是 'Hello TS',所以,它的类型为:'Hello TS'。
注意:此处的 'Hello TS',就是一个字面量类型。也就是说某个特定的字符串也可以作为 TS 中的类型。
除字符串外,任意的 JS 字面量(比如,对象、数字等)都可以作为类型使用。
验证一下:使用类型注解为str2指定类型。
还有一个知识。
除了字符串以外,数值也是可以的。
用一个let声明的变量,类型设置为18,此时变量age的值就只能是18,因为他的类型就是18。直接使用数值类型的字面量类型作为age的类型。如果age值为19,那么就会出错。
let age: 18 = 18;
不管是字符串,还是数值,都可以作为类型使用。他们统称为字面量类型。
我理解,此时的age不是number类型的,其类型是18,本质是个常量。
3.14.3 作用
使用模式:字面量类型配合联合类型一起使用。
使用场景:用来表示一组明确的可选值列表。
比如,在贪吃蛇游戏中,游戏的方向的可选值只能是上、下、左、右中的任意一个。
function changeDirection(direction: 'up' | 'down' | 'left' | 'right') {console.log(direction);
}
解释:参数 direction 的值只能是 up/down/left/right 中的任意一个。
优势:相比于 string 类型,使用字面量类型更加精确、严谨。
3.15 枚举 enum
3.15.1 枚举类型
枚举的功能类似于字面量类型+联合类型组合的功能,也可以表示一组明确的可选值。
枚举:定义一组命名常量。它描述一个值,该值可以是这些命名常量中的一个。定义好枚举后,可以使用枚举名称作为一种类型。
关键字:enum + 枚举名称 + {枚举的可选值,逗号隔开}。
定义
enum Direciton { Up, Down, Left, Right };
使用
function changeDirection1(direction: Direciton) {console.log(direction);
}
解释:
1. 使用 enum 关键字定义枚举。
2. 约定枚举名称、枚举中的值以大写字母开头。
3. 枚举中的多个值之间通过 ,(逗号)分隔。
4. 定义好枚举后,直接使用枚举名称作为类型注解。
注意:形参 direction 的类型为枚举 Direction,那么,实参的值就应该是枚举 Direction 成员的任意一个。
访问枚举成员:
changeDirection1(Direciton.Down);
解释:类似于 JS 中的对象,直接通过点(.)语法访问枚举的成员。
总结:在ts中表示一组可选值,有2种方案
- 字面量类型+联合类型
- 枚举
3.15.2 枚举成员的值
问题:我们把枚举成员作为了函数的实参,既然可以把枚举成员作为函数的实参,那么它肯定是有值的,那么它的值是什么呢?
利用ts的类型推论机制。
解释:通过将鼠标移入 Direction.Up,可以看到枚举成员 Up 的值为 0。
注意:枚举成员是有值的,默认为:从 0 开始自增的数值。
3.15.3 数字枚举
我们把,枚举成员的值为数字的枚举,称为:数字枚举。
当然,也可以给枚举中的成员初始化值。
enum Direciton { Up = 10, Down, Left, Right };
enum Direciton1 { Up = 2, Down = 4, Left = 6, Right = 8 };
3.15.4 字符串枚举
字符串枚举:枚举成员的值是字符串。
enum Direciton1 { Up = 'UP', Down = 'DOWN', Left = 'LEFT', Right = 'RIGHT' };
注意:字符串枚举没有自增长行为,因此,字符串枚举的每个成员必须有初始值。
只有数字枚举才有自增长行为。
3.15.5 枚举的特点和原理
枚举是 TS 为数不多的非 JavaScript 类型级扩展(不仅仅是类型)的特性之一。
因为:其他类型仅仅被当做类型,而枚举不仅用作类型,还提供值(枚举成员都是有值的)。
也就是说,其他的类型会在编译为 JS 代码时自动移除。但是,枚举类型会被编译为 JS 代码!
示例
对比枚举和数组这两个类型在编译为js时的表现。
将ts文件编译成js
结果
1 string的数组,在编译成js的时候,类型自动就去掉了
2 Direction枚举,在编译后,变成了一段js代码。
来看枚举被编译为js时,生成的代码。
第2 行生成了一个变量,名为Direction。第3-9行,是js的自定义函数。
枚举转化成了js的对象,枚举的成员作为了对象的属性,枚举成员的值,作为对象属性的值。
验证:
直接打印一下Direction
结果
上面单词拼错了,应该是Direction。
3.15.6 使用建议
总结:
说明:枚举与前面讲到的字面量类型+联合类型组合的功能类似,都用来表示一组明确的可选值列表。
一般情况下,推荐使用字面量类型+联合类型组合的方式,因为相比枚举,这种方式更加直观、简洁、高效。(其他的类型会在编译为 JS 代码时自动移除。但是,枚举类型会被编译为 JS 代码!),因为枚举会被编译为js,开销要比前者大。
3.16 any类型
原则:不推荐使用 any!这会让 TypeScript 变为 “AnyScript”(失去 TS 类型检查和保护的优势)。
因为当值的类型为 any 时,可以对该值进行任意操作,并且不会有代码提示。
比如:
1 访问obj中不存在的属性,以及给它赋值;
2 obj当做函数来调用,这种操作一定会带来问题,但是ts不会给出任何类型检查;
3 将obj赋值给一个number类型的变量。
解释:以上操作都不会有任何类型错误提示,即使可能存在错误!
尽可能避免使用 any 类型,除非临时使用 any 来“避免”书写很长、很复杂的类型!
其他隐式具有 any 类型的情况:
1 声明变量不提供类型也不提供默认值;
2 函数参数不加类型。
注意:因为不推荐使用 any,所以,这两种情况下都应该提供类型!
3.17 typeof
众所周知,JS 中提供了 typeof 操作符,用来在 JS 中获取数据的类型。
console.log(typeof '我是字符串');
实际上,TS 也提供了 typeof 操作符:可以在类型上下文中引用变量或属性的类型(类型查询)。
在ts中,既可以用js提供的typeof,来获取一个值的js类型。还可以在类型上下文中使用typeof,此时使用的typeof就是ts中的typeof了,它提供了类型查询的功能。
这个类型查询和js的类型查询不太一样。
使用场景:根据已有变量的值,获取该值的类型,来简化类型书写。
let p = { x: 1, y: 2 };
function formatPoint1(point: { x: number, y: number }) {}
简化书写。
下面的typeof所处的环境,就是类型上下文(参数名称:后要写类型,所以typeof所处的环境就是类型上下文)。此时typeof来查询p这个值在ts中的类型,也就是{x: number, y:number}
function formatPoint(point: typeof p) {}
实际上这两种效果是一样的,只是typeof的写法更加简洁。
解释:
1. 使用 typeof 操作符来获取变量 p 的类型,结果与第一种(对象字面量形式的类型)相同。
2. typeof 出现在类型注解的位置(参数名称的冒号后面)所处的环境就在类型上下文(区别于 JS 代码),它此时拿到的类型就是ts中的类型。
3. 注意:typeof 只能用来查询变量或属性的类型,无法查询其他形式的类型(比如,函数调用的类型)。
查询变量的类型
let num: typeof p.x;
不能查询函数的返回类型
function add1(num1: number, num2: number) {return num1 + num2;
}
此时typeof不能获取函数返回值的类型