一、安装ts
- 命令:
npm i -g typescript
- 查看安装包命令
tsc -v
- 显示版本
Version 5.1.6
二、ts的常用类型
1、类型注解
示例代码:
let age:number = 18
注意:说明上序代码中的 :number 就是类型注解
作用:为代码添加类型约束,约定了是什么类型就只能给该类型付同类型的值。否则会报错!
2、常用基础类型概括
1、js已有类型
- 原始类型:number、string、boolean、null、undefined、symbol
- 对像类型: object(数组、对象、函数)
2、ts新增类型
- 联合类型、 自定义类型、接口、元组、字面量类型、枚举、void、any等
3、原始类型
- 原始类型:number、string、boolean、null、undefined、symbol
- 这些类型完全是按照js原始类型来写的

4、数组类型
1、数组类型的两种写法,推荐用第一种
let arrNumber: number[] = [1, 2, 3]
let arrString: Array<string> = ['1', '2', '3']
5、联合类型
注意:一个数组中存在多个类型的数据
//加上小括号说明,数组内的类型可以是小括号里的任何类型
let arr: (number | string | null)[] = [null, 1, '23']
//不加小括号,说明arr即可以是Number类型,也可以是string类型的数组
let arr2: number | string[] = 123
let arr3: number | string[] = ['1', '2', '3']
解释:|在ts中叫联合类型
6、类型别名
- 类型别名:为任意类型取别名
- 使用场景:当同一类型被多次复杂使用时,可以通过类型别名,简化该类型的使用。
type CustomArray = (number | string)[]
let arr1: CustomArray = ['1', 1]
let arr2: CustomArray = ['1', 1]
注意:
- 使用type关键字来创建类型别名
- 类型别名可以是任意合法变量名称
7、函数类型
注意:
- 函数类型是指:函数参数类型、函数返回值类型
1、单独自定参数、返回值的类型
function add(number1:number,number2:number):number{
return number1 + number2
}
console.log(add(1,2));
let add = (number1: number, number2: number): number => {
return number1 + number2
}
console.log(add(1, 3));
2、同时指定参数和返回值的类型
注意:这种方法只能用于函数表达式

let add: (number1: number, number2: number) => number = (number1, number2) => {
return number1 + number2
}
console.log(add(1, 3));
8、 void类型
注意:如果函数没有返回值,那么函数返回值就是void(就是空值的意思)
function onVoid(num:number):void{
console.log(num);//执行结果:12
}
onVoid(12)
9、函数可选参数
注意:使用函数实现某个功能时,函数参数可传可不传。这种情况在给函数指定参数类型时,就用到可选参数,
可选参数:在可传可不传的参数后面加 ?
注意:可选参数后面不能再写必传参数
function mySlice(start?: number, end?: number) {
console.log('开始', start, '结束', end);
}
mySlice()//开始 undefined 结束 undefined
mySlice(1)//开始 1 结束 undefined
mySlice(1, 2)//开始 1 结束 2
10、对象类型
注意:js中的对象是由属性和方法构成的,而TS中对象的类型就是在描述对象的结构(有什么类型的属性和方法)
解释:
- 直接使用 {} 来描述对象结构。属性采用属性名:类型的形式,方法采用方法名:返回值类型的形式
- 如果方法有参数,就在方法名后面的小括号内指定参数类型(比如:greet(name:string):void )
- 在一行代码中指定对象的多个属性类型时,使用 ;来分割。如果换行可以不用 ;
对象类型写法:
let obj: { name: string; age: number; onSay(): void } = {
name: '李玉',
age: 21,
onSay() {
console.log('111');
}
}
let obj2: {
name: string
age: number
onSay: (e:number) => void
} = {
name: '简隋英',
age: 28,
onSay(e) {
console.log(e);
}
}
11、可选属性
注意:
- 对象的属性或方法也是可选的,此时就用到了可选属性了。
- 可选属性的语法与可选参数的语法是一致的,都使用 ? 来表示
function myAxios(config: { url: string, methods?: object }) {
}
let config = {
url:'https://www.baidu.com'
}
myAxios(config)
12、接口
一、基础用法
注意:当一个对象类型被多次使用时,一般会使用接口(interface)来描述对象的类型,达到复用的效果。
解释:
- 使用interface关键字来声明接口
- 接口名称可以是任意合法的名称
- 声明接口后可以直接使用变量的名称作为变量的类型
- 因为每一行只有一个属性类型,因此属性类型没有;{分号}
interface IPeron {
name: string
age: number
onSay: () => void
}
let obj: IPeron = {
name: '李玉',
age: 21,
onSay() {
}
}
二、接口和类型别名对比
interface(接口)和type(类型别名)的对比
- 相同点:都可以给对象指定类型
- 不同点
* 接口interface只能为对像指定类型
* 类型别名type,不仅可以对对象指定类型,实际上可以为任意类型指定别名
*type用=(等号),interface不用= (等号)
interface例子:
interface IPeron {
name: string
age: number
onSay: () => void
}
let obj: IPeron = {
name: '李玉',
age: 21,
onSay() {
}
}
type例子:
type IPeron = {
name: string
age: number
onSay: () => void
}
let obj: IPeron = {
name: '李玉',
age: 21,
onSay() {
}
}
type例子:
type NumStr = number | string
三、接口的继承
注意: 如果两个接口之间有相同的属性或方法,可以将公共属性和方法抽离出来,通过继承来实现复用。
如下列,这两个接口都有x,y属性,重复写两次太过繁琐。
interface IPearn1 {
x:string,
y:number
}
interface Ipearn2{
x:string,
y:number,
id:number
}
更好的方式:使用extends关键字的方法去实现继承
interface IPearn1 {
x:string,
y:number
}
interface Ipearn2 extends IPearn1{
id:number
}
13、元组
场景:
在地图中使用经纬度来标记坐标信息。
可以使用数组来标记坐标,那么,该数组中只有两个元素,并且这两个元素都是数值类型。
let position: number[] = [12, 23]
- 使用number[]的缺点:不严谨,因为该类型的数组中可以出现任意多个数字。
注意:更好的方法是使用元组(Tuple),元组类型是另一种类型的数组,它确切的知道包含多少个元素,以及特定索引对应的类型。
let position: [number, number] = [12, 45]
解释:
- 元组类型可以确切的标记出有多少个元素,以及每个元素的类型
14、类型推论
注意:在TS中,某些没有声明却指出类型的地方,TS的类型推论机制会帮助提供类型。所以,因为有类型推论的存在,有的地方可以沈略类型注解( ; )
1、声明变量并且初始化时
let aa = 12
let bb = 'dddd'
2、决定函数返回值时
function add(num1: number, num2: number) {
return num1 + num2
}
add(1, 2)
15、类型断言
注意:有时你会比TS更加明确一个值的类型,此时,可以使用类型断言来指定更具体的类型
比如:

注意:getElementById方法返回值的类型是HTMLElement,该类型只包含所有标签的公共属性或方法,不包含a标签特有的href等属性。
因此,这个类型太宽泛(不具体),无法超做href等a标签特有的属性的方法。
解决方式:这种情况下就需要使用类型断言指定更加具体的类型。
1、类型断言语法1

解释:
- 使用as关键字实现
- 关键字as后面的类型是一个更加具体的类型(HTMLAnchorElement是HTMLElement的子类型)
- 通过类型断言,aLink的类型变得更加的具体,这样就可以访问a标签特有的类型或方法了
1、类型断言语法2
注意:另一种写法是,使用<>语法 
技巧:在浏览器控制台,通过console.dir()打印DOM元素,在属性列表的后面,即可看到该元素的类型。
16、字面量类型
思考以下代码,两个变量的类型分别是?

通过TS类型推论机制,可以得到答案:
- 变量str1的类型为:string
- 变量str2的类型为:'Hello TS'
解释:
- str1是一个变量(let),它的值可以是任意字符串,所以类型是string
- str2是一个常量(const),它的值不能变化,所以类型是'Hello TS'
注意:此处的 'Hello TS'就是字面量类型。也就是说某个特定字符串也可以作为TS中的类型。
除字符串外,任何的JS字面量(比如、对象、数字等)都可以作为类型使用。
let str1 = 'Hello'
const str2: 'Hello' = 'Hello'
let age: 18 = 18
使用模式:字面量类型配合联合类型(|)一起用
使用场景:用来表示一组明确的可选值列表。
比如贪吃蛇游戏中,游戏的方向就只有(上下左右)任意一个
解释:参数direction的值只能是up/down/left/right中的任意一个。
优势:相比于string类型,使用字面量类型更加精确、严谨。
17、枚举
1.1、基本枚举
注意:形参direction的类型为枚举Direction,那么,实参的值就应该是枚举Direction成员中的任意一个。
enum Direction { UP, Down, Left, Right }
function changeDirection(direction: Direction) {
console.log(direction);
}
changeDirection(Direction.UP)
changeDirection(Direction.Down)
changeDirection(Direction.Left)
changeDirection(Direction.Right)
解释:类似于JS中的对象,直接通过点(.)语法访问枚举成员。
1.2、 枚举成员的值以及数字枚举
问题: 我们将枚举成员作为函数的实参,它的值是什么?

解释:通过将鼠标移入Direction.UP可以看到枚举成员UP的值是0
注意:枚举成员是有值的,默认为:从0开始自增的数字
我们把,枚举成员的值为数字枚举
当然也可以给枚举中的成员初始化值:
//UP=10, Down=11, Left=12, Right=13
enum Direction { UP = 10, Down, Left, Right }
//UP=10, Down='abc', Left=8, Right=4
enum Direction { UP = 10, Down = 5, Left = 8, Right = 4 }
1.3、字符串枚举
注意:枚举成员的值是字符串
//UP=UP, Down='Down', Left='Left', Right='Right'
enum Direction { UP = 'UP', Down = 'Down', Left = 'Left', Right = 'Right' }
function changeDirection(direction: Direction) {
console.log(direction);
}
changeDirection(Direction.UP)
changeDirection(Direction.Down)
changeDirection(Direction.Left)
changeDirection(Direction.Right)
注意:字符串枚举没有自增长行为,因此,字符串枚举成员都必须拥有初始值
1.4、枚举的特点及其原理
枚举是TS为数不多的非JS类型级拓展(不仅仅是类型)的特征之一
因为:其他类型不仅仅被当做类型,而是枚举不仅用于类型还提供值(枚举成员都是有值的)
也就是说,其他类型会在编译成JS代码时直接移除,但是,枚举类型会被编译成JS代码。
TS:
//UP=UP, Down='Down', Left='Left', Right='Right'
enum Direction { UP = 'UP', Down = 'Down', Left = 'Left', Right = 'Right' }
function changeDirection(direction: Direction) {
console.log(direction);
}
changeDirection(Direction.UP)
changeDirection(Direction.Down)
changeDirection(Direction.Left)
changeDirection(Direction.Right)
JS:
//UP=UP, Down='Down', Left='Left', Right='Right'
var Direction;
(function (Direction) {
Direction["UP"] = "UP";
Direction["Down"] = "Down";
Direction["Left"] = "Left";
Direction["Right"] = "Right";
// Direction为对象,UP,Down。。。为属性加赋值
})(Direction || (Direction = {}));
//传入Direction=understand||Direction = {}
function changeDirection(direction) {
console.log(direction);
}
changeDirection(Direction.UP);
changeDirection(Direction.Down);
changeDirection(Direction.Left);
changeDirection(Direction.Right);
说明:枚举类型和前面说到的字面量类型+联合类型组合功能类似,都用来表示一组明确的可选值列表。
一般情况下,推荐使用字面量类型+联合类型,因为相比于枚举这种方式更加简洁、高效。
18、any类型
原则:不推荐使用any!这会让TS变成"AnyScript"(失去类型保护的优势)
因为当值的类型为any时,可以对该值进行任意超做,并且不会有任何提示。

其他隐式具有any类型的情况:
- 申明变量不提供类型也不提供默认值
- 函数参数不加类型
19、TS中的typeof
众所周知,JS提供了typeof运算符,用来在js中获取数据的类型
console.log(typeof 'hello'); //打印 string
实际上TS也提供了typeof操作符:可以在类型上下文中引用变量或属性的类型(类型查询)。
使用场景:根据已有变量的值,来获取该值的类型,来简化类型书写。
let parameter = {
x: 10,
y: 20
}
function forMater(point: { x: number, y: number }) {}
forMater(parameter)
let parameter = {
x: 10,
y: 20
}
let p = {
x: 0,
y: 0
}
function forMater(point: typeof p) {}
forMater(parameter)
三、TypeScript高级类型
class
1、calss的基本使用
TS全面支持ES5中引入的class关键字
- 添加属性的方法有以下两种:
class Person{
//添加属性的方法有以下两种
name1:string
name2 = '简隋英'
}
const p = new Person()
p.name1//属性是string
p.name2//属性是string
解释:
- 根据TS中的类型推论,可以知道Person类的实例对象p的类型是Person
- TS中的class,不仅仅提供class的语法功能,也作为一种类型存在。
2、calss的构造函数
class Person{
//添加属性的方法有以下两种
name1:string
name2 = '简隋英'
constructor(str1:string,str2:string){
this.name1 = str1
this.name2 = str2
}
}
const p = new Person('李玉','简隋英')
p.name1//属性是string
p.name2//属性是string
解释:
- 成员初始化(比如:name1:string )后,才能通过this.name1来访问实例成员
- 需要为构造函数指定类型注解,否则会被隐式推断为any,构造函数不需要返回值
3、class实例方法

class Person {
x = '简隋英'
y = '李玉'
scale(name1: string, name2: string): void {
this.x = name1
this.y = name2
}
}
const p = new Person()
p.scale('江停', '严峫')
console.log(p.x, p.y);
解释:方法的类型注解(参数和返回值)于函数相同。
4、class的继承
注意:类的继承有两种方式extends(继承父类)和implements(实现接口)
extends(继承父类)
class Name{
onBackName(){
console.log('江停');
}
}
class Person extends Name{
}
const p = new Person()
p.onBackName() //结果打印:江停
解释:
- 通过extends关键字实现继承
implements(实现接口)
interface Singable{
name:string
sing():void
}
class Person implements Singable{
name: string
sing(): void {
console.log('夫妻双双把家还!');
}
}
解释:
- 通过关键字implements让class实现继承
- implements实现继承,要继承接口所有的属性和方法,少一个都会报错!
5、class类的可见性
类成员的可见性,可以使用TS来控制class的方法或属性对于class外的代码是否可见。
可见性修饰符包括:1、public(公有的)2、protected(受保护的)3、privated(私有的)
public(公有的)
class Person {
name = '江停'
public sing(name: string): void {
console.log(this.name + name);
}
}
const p = new Person()
p.sing('严峫')//结果打印:江停严峫
注意:
- 在类属性或方法前面加public关键字,来修饰该属性或方法是共有的。
- 因为public是默认可见性,所以,可以直接省略。
protected(受保护的)
class Name {
protected member() {
console.log('吴雩');
}
cole() {
this.member//这里可以调用
}
}
const nameClass = new Name()
nameClass.cole()//这个方法可以调用
nameClass.member()//这个方法不可以调用
class Person extends Name {
name = '江停'
sing(name: string): void {
this.member()
}
}
const p = new Person()
p.sing('严峫')//结果打印:江停严峫
p.cole()//这个方法可以调用
p.member()//这个方法不可以调用
注意:
- 在类属性或方法前面添加protected关键字,来修饰该属性或方法是受保护的。
- 在子类的方法内部可以通过this来访问父类中受保护的成员,但是,对实例不可见
privated(私有的)
class Name {
private member() {
console.log('吴雩');
}
cole() {
this.member//这里可以调用
}
}
const nameClass = new Name()
nameClass.cole()//这个方法可以调用
nameClass.member()//这个方法不可以调用
class Person extends Name {
name = '江停'
sing(name: string): void {
this.member()//这个方法不可以调用
}
}
const p = new Person()
p.sing('严峫')//结果打印:江停严峫
p.cole()//这个方法可以调用
p.member()//这个方法不可以调用
注意:
- 在类属性或方法前面添加private关键字,来修饰该属性或方法是私有的。
- 私有属性或方法只有在当前类中可见,对子类和实例对象也都是不可见的!
6、readonly只读修饰符
出来可见修饰符以外,还有一个常见修饰符就是:readonly(只读修饰符)
readonly:表示只读,用来防止构造函数以外的属性对他进行赋值
class中
注意:只能在constructor中修改属性,其他地方不能。且readonly只能用于属性不能用于方法。
class Person{
readonly age:number = 18
constructor(age1:number){
this.age = age1 //只能在这里改
}
changAge(){
this.age = 23 //修改属性会报错
}
}
const p = new Person(21)
class中
注意:如果 readonly的属性没有增加类型注解,那么值的属性就是他的初始值,相当于一个常量。就算是在constructor里也不能改。
class Person{
readonly age = 18
constructor(age1:number){
this.age = age1 //这里也不能改
}
changAge(){
this.age = 23 //修改属性会报错
}
}
const p = new Person(21)
interface中
interface IPerson{
readonly name:string
}
let obj:IPerson ={
name:'严峫'
}
obj.name = '江停'//name属性标记只读,所以不能被复制
{}中
let obj: { readonly name: string } = {
name: '严峫'
}
obj.name = '江停'//name属性标记只读,所以不能被复制
解释:
- 使用readonly关键字修饰该属性是只读的,注意只能修饰属性不能修饰方法
- 注意:在class属性中如果不加类型注解,则属性的类型是初始化时的常量。
- 接口或{}表示的对象类型,也可以使用readonly
类型兼容性
1、基本理解
两种类型系统:结构化类型系统和标明类类型系统
结构化类型系统
TS采用的是结构化类型系统,也叫做duck typing(鸭子类型),类型检查关注的是值所具备的性状
也就是说,结构化类型系统中,如果两个对象具有相同的形状,则认为他们属于同一类型。
class Name {
x:number
y:number
}
class Name2{
x:100
y:200
}
const p:Name = new Name2()
解释:
- Name和Name1是两个名称不相同的类
- 变量p的类型被显示标注为Name类型,但是她的值却是Name1的实例,并没有类型错误
- 因为TS是结构化类型系统,只检查Name和Name1的结构是否相同
- 但是如果在标明类类型系统中,他们是不同的类,类型无法兼容
2、对象类型兼容性
注意:结构化类型系统中,如果两个对象具有相同的形状,则认为他们属于同一类型,这种说法不完全准确。
更准确的说法是:对于对象类型来说,y的成员至少于x相同,则认为x兼容y(成员多的可以赋值给少的)
class Point {
x: number
y: number
}
class Point3D {
x: number
y: number
z: number
}
const p: Point = new Point3D
解释:
- Point3D的成员至少要与Point相同,则Point兼容Point3D
- 所以,成员多的Point3D可以赋值给成员少的Point
3、接口之间的兼容性
除了class之外,TS中的其他类型也存在相互兼容的情况,包括:1、接口兼容 。2、函数兼容性等。
接口之间的兼容性,类似于class,并且,class和interface之间也可以兼容

4、函数之间的类型兼容性
函数之间的兼容性比较复杂,需要考虑:1、参数个数 2、参数类型 3、返回值类型
参数个数
参数个数,参数多的兼容参数少的(或者说,参数少的可以赋值给多的)
type F1 = (a:number) =>void
type F2 = (a:number, b:number)=>void
let a:number = 12
let f1:F1 = (a) => {
return null
}
let f2:F2 = f1
const arr = ['a', 'b', 'c']
arr.forEach(() => { })
arr.forEach((item) => { })
解释:
- 参数少的可以赋值给参数多的,所以,f1可以赋值给f2
- 数组foeEach方法的第一个参数是回调函数,该实例中类型为(value:string,index:number,array:string[])=> void
- 在JS中沈略用不到的函数参数是正常的实际上是很常见的,这样的使用方式,促成了TS中函数类型之间的兼容性。
- 并且因为回调函数是有类型的,所以,TS会自动推导出参数item,index,array的类型
类型属性
参数类型:相同位置的参数类型要相同,(原始类型)或兼容(对象类型)
- 原始类型
type F1 = (a: number) => void
type F2 = (a: number) => void
let f1:F1
let f2:F2
let num = 12
f2 = (num) => {}
f1 = f2
f2 = f1
- 对象类型
interface Point2D {
x: number
y: number
}
interface Point3D {
x: number
y: number
z: number
}
type F2 = (p: Point2D) => void
type F3 = (p: Point3D) => void
let p1 = { x: 12, y: 10 }
let f2: F2 = (p1) => { }
let f3: F3 = f2
f2 = f3//会报错,多的不能赋值给少的
解释:
- 注意,此处于前面讲的接口兼容性冲突
- 技巧,将对象拆开,把每个属性看做一个个参数,则,参数少的f2可以赋值给参数多的f1
返回值
- 例子1:
type F5 = () => string
type F6 = () => string
let f5:F5 = () => {return '111'}
let f6:F6 = f5
- 例子2:
type F7 = () => { name: string }
type F8 = () => { name: string, age: number }
let f7: F7 = () => {
return {
name:'李玉'
}
}
let f8: F8 = () => {
return {
name: '简隋英',
age: 28
}
}
f7 = f8
console.log(f7);//打印结果:[Function: f8]
解释:
- 如果返回值类型是原始类型,此时两个类型要相同,比如例子1的类型F5和F6
- 如果返回值类型是对象类型,此时成员多的可以赋值给成员少的,比如例子2 的类型F7和F8
交叉类型
1、基本介绍
交叉类型(&):功能类型接口继承(extends),用于组合多个类型为一个类型(常用于对象类型)
- extends:
interface Person{
name:string
}
interface Content extends Person{
age:number
}
let PersonContent:Content = {
name:'简隋英',
age:27
}
- 交叉类型(&):
interface Person{
name:string
}
interface Content{
age:number
}
type PersonContent = Person & Content
let obj:PersonContent = {
name:'简隋英',
age:27
}
解释:使用交叉类型后,新的类型PersonContent就同时具备了Person 和 Content的所有属性类型。
- 相当于:
type PersonContent = { name: string, age: number }
2、交叉型和接口型的对比说明
交叉型(&)和接口型(extends)的对比:
- 相同点:都可以实现对象类型的组合
- 不同点:两种方法实现实现类型组合时,对于同名属性之间,处理类型冲突的方式不同
- 接口型(extends):
interface A {
fn: (value: number) => string
}
interface B extends A{//B不兼容
fn: (value: string) => string
}
- 交叉型(&) :
interface A {
fn: (value: number) => string
}
interface B {//B不兼容
fn: (value: string) => string
}
type C = A & B
说明:以上代码,接口继承会报错(类型不兼容);交叉类型没有错误,可以简单理解为:
fn: (value: string | number) => string
泛型和keyof
1、泛型的基本使用
泛型是在保证类型安全的前提下,让函数等与多种类型一起工作,从而实现复用,常用于:函数、接口、class中。
需求 :创建一个id函数,传入什么数据就返回该数据本身。(也就是说参数和返回值类型相同)
function id(value:number):number {
return value
}
比如:id(10)调用以上函数就会直接返回10本身。但是,该函数只能用于数值类型,无法用于其他类型。
为了能让函数接收任意类型,可以将参数类型修改为any,但是,这样就会失去TS的类型保护,类型不安全。
function id(value:any):any {
return value
}
解释:泛型在保证类型安全(不丢失类型信息)的同时,可以让函数等与多种不同的类型一起工作,灵活复用。
创建泛型函数
function id<Type>(value: Type): Type {
return value
}
解释:
- 语法:在函数名称后面添加一个<>(尖括号),尖括号中添加类型变量,比如此处的Type
- 类型变量Type,是一种特殊类型的变量,它处理类型而不是值。
- 该类型变量相当于一个类型容器,能够捕获用户提供的类型(具体是什么类型由用户调用该函数时决定)
- 因为Type是类型,因此可以将其作为函数参数和返回值的类型,表示参数和返回值具有相同的类型。
- 类型变量Type,可以是任意合法的类型
调用泛型类型
function id<Type>(value: Type): Type {
return value
}
const num1 = id<number>(10)
const str = id<string>('江停')
const beal = id<boolean>(true)
解释:
- 语法:在调用函数的后面加<>(尖括号),尖括号中指定具体的类型,比如,此处的number
- 当传入类型number后这个类型就会被函数声明时指定的类型变量Type捕获。
- 此时,Type的类型就是number,所以函数id参数和返回值的类型都是number。
同样,如果传入类型string,函数id参数和返回值的类型就是string
这样,通过泛型就做到了让id函数于多种不同的类型一起工作,实现了复用的同时保证了类型安全
2、简化泛型函数调用
function id<Type>(value: Type): Type {
return value
}
const num1 = id(10)
const str = id('江停')
const beal = id(true)
解释:
- 在调用泛型函数时,可以省略<类型>来简化泛型函数的调用
- 此时,TS内部会采用一种叫做类型参数推断的机制 ,来根据传入的实参自动推断出类型变量Type的类型。
- 不如,传入实参10,Ts会自动推断出变量num的类型为number,并且作为Type的类型
推荐:使用这种简化的方式调用泛型函数,使代码变得更短,更加易读
说明:当编译器无法推断类型或者类型不准确时,就需要显式的传入类型参数
3、泛型约束
泛型约束:默认情况下,泛型函数的类型变量Type可以代表多个类型,这导致无法访问任何属性。
比如:id('a')调用函数时获取参数长度:
function id<Type>(value: Type): Type {
console.log(value.length);//这个代码报错
return value
}
const num1 = id([10,12])
解释:Type可以代表任意类型,无法保证一定存在length属性,比如number类型就没有length。
此时,就需要为泛型添加约束来收缩类型(缩小类型的取值范围)
添加泛型的收缩属性,主要有以下两种方式:指定更加具体的类型 和 添加约束
指定更加具体的类型
function id<Type>(value: Type[]): Type[] {
console.log(value.length);//这个代码报错
return value
}
const num1 = id([10,12])
比如:将类型修改为Type[](Type类型的数组)。因为只要是数组就一定存在length属性,因此就可以访问了
添加约束extends
interface ILength{
length:number
}
function id<Type extends ILength>(value: Type): Type {
console.log(value.length);//这个代码报错
return value
}
const num1 = id([10,12])
const str = id('江停')
const beal = id({length:12,name:'江停'})
解释:
- 创建描述约束的接口ILength,该接口要提供ILength属性
- 通过extends关键字使用该接口,为泛型(类型变量)添加约束
- 该约束表示:传入的类型必须具有length属性
注意:传入的实参(比如,数组)只要有length属性即可,这也符合前面讲到的接口的类型兼容性
多个泛型变量的情况
泛型的类型变量可以有多个,并且类型变量之间还可以约束(比如:第二个类型变量受第一个类型变量约束)。
比如,创建一个函数来获取对象中属性的值:
function getProp<Type,Key extends keyof Type>(obj:Type,key:Key){
return obj[key]
}
let person = {name:'江停',age:31}
console.log(getProp(person,'name'));
解释:
- 添加了第二个类型变量Key,两个类型变量之间使用(,)逗号分隔。
- keyof关键字接收一个对象类型,生成其键名称(可能是字符串或数字)的联合类型
- 本实例中keyof Type实际上获取的是person对象所有联合类型,也就是:‘name’|‘age’
- 类型变量Key受Type约束,可以理解为:key只能是Type所有键中的任意一个,或者说只能访问对象中纯在的属性
本文详细介绍了TypeScript的安装、常用类型和高级类型。常用类型包括类型注解、原始类型、数组类型等;高级类型涉及class、类型兼容性、交叉类型、泛型等。还对接口、枚举等特性进行了讲解,并对比了不同类型定义方式的异同,帮助开发者更好地掌握TS。
1333

被折叠的 条评论
为什么被折叠?



