Typescript常用类型

166 阅读13分钟

参考链接

blog.csdn.net/qq_64294848…

1.1类型注解

let age:number = 18

说明:代码中的** :number** 就是类型注解

作用:为变量添加类型约束,上述声明的age 变量,只能赋值为number 类型

TS中的常用类型如下:

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

1.2数组类型

// 推荐,简洁
let numbers:number[] = [1,2,3]
// 不推荐 ,麻烦,不直观
let strings:Array<string> = ['a','b','c']

1.3联合类型

需求:当数组中既有number类型,又有string类型,这个数组该如何写

let arr:(number | string)[] = [1,'a',2,'b']

解释:|(竖线)在TS中叫联合类型,(由两个或多个其他类型组成的类型,表示可以是这些类型中的任意一种,注意:这是TS中联合类型的语法,只有一根竖线,跟JS中的或 ( || ) 不同)

1.4类型别名

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

使用场景:当同一类型(复杂)被多次使用时,可以通过类型别名,简化该类型的使用

type CustomArray = (number | string)[]
let arr1:CustomArray = [1,'a',2,'b']
let arr2:CustomArray = ['x','y',6,7]
解释:
1. 使用type关键字来创建类型别名
2. 创建类型别名后,直接使用该类型别名作为变量的类型注解即可

1.5函数类型

单独指定参数和返回值类型 (推荐使用)

// 函数声明创建
function add(num1:number,num2:number):number{
  return num1 + num2
}      
 
// 函数表达式创建
const add = (num1:number,num2:number):number =>{
  return num1 + num2
}

void 类型

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

function greet(name:string):void{
  console.log('hello',name)
}

函数可选参数

function mySlice(start?: number,end?: number):void{
  console.log('起始索引:',start,'结束索引',end)
}

可选参数:在可传可不传的参数名称后面添加 ? (问号)

注意:可选参数只能出现在参数列表的最后,也就是说可选参数后面不能再出现必选参数

1.6对象类型

对象用法

let person:{name: string;age: number; sayHi(): void} = {
  name:'jack',
  age:19,
  sayHi(){}
}
  1. 直接使用{}来描述对象结构,属性采用属性名:类型的形式;方法采用方法名():返回值类型的形式
  2. 如果方法有参数,就在方法名后面的小括号中指定参数类型(比如 greet(name:string):void )
  3. 再一行代码中指定对象的多个属性类型时,使用 ; (分号)来分割
  4. 如果一行代码只能指定一个属性类型(通过换行来分隔多个属性类型),可以去掉 ; (分号)
  5. 方法的类型也可以使用箭头函数形式(比如: {sayHi:() => void})
  • 对象可选属性
function myAxios(config:{url:string;method?: string}){
  console.log(config)
}

可选属性的语法与函数可选参数语法一致,都使用 ? (问号) 来表示

1.7接口

接口用法

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

 interface Iperson{
    name: string
    age: number
    sayHi(): void
 }
 
 let person: Iperson = {
     name: 'jack',
     age:19,
     sayHi(){}
 }
  1. 使用interface关键字来声明接口。
  2. 接口名称(比如,此处的Iperson),是可以任意合法的变量名称。
  3. 声明接口后,直接使用接口名称作为变量的类型。
  4. 因为每一行只有一个属性类型,因此,属性类型后没有 ; (分号)。
  • 接口和类型别名的对比

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

    不同点:

    接口,只能为对象指定类型

    类型别名,不仅可以为对象指定类型,实际上可以为任意类型指定别名

interface IPerson{
  name:string
  age:number
  sayHi():void
}
 
type IPerson{
  name:string
  age:number
  sayHi():void
}
 
type NumStr = number | string
  • 接口继承

    如果两个接口之间有相同的属性或方法,可以将公共的属性或方法抽离出来,通过继承来实现复用。比如,这两个接口都有x,y两个属性,重复写两次,可以,但是很繁琐

// 原版写法 (很繁琐)
interface Point2D { x: number; y: number}
interface point3D { x: number; y: number; z: number}
 
// 更好的写法(接口继承)
interface Point2D { x: number; y: number}
interface Point2D extends Point2D { z: number}
  1. 使用extends(继承)关键字实现了接口point3D继承point2D.
  2. 继承后,Point3D就有了Point2D的所有属性和方法(此时,Point3D同时有x、y、z 三个属性)

1.8元组

使用场景:在地图中,使用经纬度来记录坐标信息

可以使用数组来记录坐标,那么该数组中只有两个元素,并且这两个元素都是数值类型

// 不严谨,因为该类型的数组中可以出现任意多个数值
let position: number[] = [39.5427,116.2317]
 
// 更好的方式:元组(Tuple)
let position:[number,number] = [39.5427,116.2317]
  1. 元组类型是另一种类型的数组,它确切的知道包含多少个元素,以及特定索引对应的类型
  2. 该示例中,元素有两个元素,每个元素的类型都是number

1.9字面量类型

let str1 = 'Hello TS'
const str2 = 'hello TS'

通过TS类型推论机制,可以得到答案

  1. 变量str1的类型为:string
  2. 变量str2的类型为:'Hello TS'

解释:

  1. str1是一个变量 ( let ) ,它的值可以是任意字符串,所以类型是:string
  2. str2 是一个常量( const ),它的值不能变化只能是'Hello TS',所以它的类型是:'Hello TS'

注意:此处的'Hello TS',就是一个字面量类型,也就是说某个特定的字符串也可以作为TS中的类型,除字符串以外,任意的JS字面量(比如,对象、数字等)都可以作为类型使用

使用场景:用于表示一组明确的可选值列表

使用模式:字面量类型配合联合类型一起使用

比如,再贪吃蛇游戏中,游戏的方向的可选值只能是上、下、左、右中的任意一个。

2.0枚举类型

枚举用法

// 声明枚举变量
enum Direction{Up,Down,Left,Right}
function changeDirection(direction:Direction){
    console.log(direction);
}
// 
changeDirection(Direction.Up)

枚举成员的值 数字枚举

image.png

枚举成员默认有值,默认从0开始自增的数值

如上图,从Up开始值,依次为 0 1 2 3

image.png

字符串枚举

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

注意:字符串枚举没有自增长行为,因此,字符串枚举的每个成员都必须有初始值 其他的类型仅仅被当作类型,而枚举不仅当作类型,还提供了值(所有的枚举类型都有值); 也就是说,其他的类型会在编译为JS代码时自动移除,但是,枚举类型会被编译为JS代码!

2.1 TS中的运算符-typeof

console.log(typeof 'Hello world')
// 打印 string

实际上,TS也提供了typeof操作符,可以在类型上下文中引用变量或属性的类型(类型查询)。 使用场景:根据已有的变量的值,获取该值的类型,来简化类型书写

image.png

解释:

  1. 使用typeof操作符来获取变量p的类型,结果与第一种(对象字面量形式的类型)相同
  2. typeof出现在类型注解的位置(参数名称的冒号后面)所处的环境就在类型上下文(区别于js代码)
  3. 注意:typeof只能用来查询变量或属性的类型,无法擦汗寻其他形式的类型(比如,函数调用的类型)

2.2 Typescript高级类型

2.1类的基本使用

image.png

解释:

  1. 根据TS中的类型推论,可以知道Person类的实例对象P的类型是Person
  2. TS中的class,不仅提供了class的语法功能,也作为一种类型存在
class Person{
  age:number
  gender = '男'
  // gender: string = '男'
}

解释:

  1. 声明成员age,类型为number(没有初始值)
  2. 声明成员gender,并设置初始值,此时,可以省略类型注解(TS类型推论为string类型)

2.2类的构造函数

class Person{
  age: number
  gender: string
  
  constructor(age: number,gender: string){
    this.age = age
    this.gender = gender
  }
}
// 注意:此时,this后的变量指的是上面类里面的变量,
// 等号右侧的变量是形参传入的变量

解释:

  1. 成员初始化(比如,age:number)后,才可以通过this.age来访问实例成员
  2. 需要为构造函数指定类型注解,否则会被隐式推断为any;构造函数不需要返回值类型

2.3类的实例方法

class Point{
  x = 1
  y = 2
  
  scale(n: number):void{
    this.x *= n
    this.y *= n
  }
}
 
const p = new Point()
p.scale(10)
console.log(p.x,p.y)
// 输出 10 20

2.4类的继承的两种方式

class Animal{
 move(){ console.log('Moving along!')}
}
class Dog extends Animal{
 bark(){ console.log('汪!')}
}
const dog = new Dog()

解释:

  1. 通过extends关键字实现继承
  2. 子类Dog继承父类Animal,则Dog的实例对象dog就同时具有了父类Animal和子类Dog的所有属性和方法

2.4.1 implements(实现接口)

interface Singable{
  sing():void
}
class Person implements Singable{
  sing(){
    console.log('Hello World')
  }
}
解释:

1. 通过implements关键字让class实现接口
2. Person类实现接口Singable意味着,Person类中必须提供Singable接口指定的所有方法和属性

2.5类成员的可见性

// public
class Animal{
  public move(){
    console.log('Moving along!')
  }
}
 
// protected
class Animal{
  protected move(){console.log('Moving along!')}
}
class Dog extends Animal{
  bark(){
    console.log('汪汪汪!')
    this.move() // 此处可用,因为是继承的子类
  }
}
 
// private
class Animal{
  private move(){console.log('Moving along!')}
  walk(){
    this.move()
  }
}

解释:

  1. public(共有的),公有成员可以在任何地方被访问,默认可见性。
  2. protected(表示受保护的),仅对其声明所在类的子类中(非实例对象)可见,注意:所有通过实例的对象,均不可见
  3. private(私有的),只在当前类中可见,对实例对象以及子类都是不可见的

2.6类型兼容性

2.6.1 TS中的类型兼容性

class Point{x: number;y: number}
class Point2D{x: number; y: number}
 
const p: Point = new Point2D()

解释:

  1. Point和Point2D是两个名称不同的类
  2. 变量p的类型被显著的标为Point类型,但是它的值却是Point2D的实例,并没有类型错误
  3. 因为TS是结构化类型系统,只检查Point和Point2D的结构是否相同(相同,都具有x和y两个属性,属性类型也相同)

2.6.2对象的兼容性

class Point{x: number;y: number}
class Point3D{x: number;y: number;z: number}
const p:Point = new Point3D()

解释:

  1. 对于对象类型来说,y的成员至少与x相同,则x兼容y(成员多的可以赋值给少的)
  2. Point3D的成员至少与Point相同,则Point兼容Point3D
  3. 所以,成员多的Point3D可以赋值给成员少的Point

2.6.3 接口之间的类型兼容

interface Point{x: number;y: number}
interface Point2D{x: number;y: number}
let p1:Point
let p2:Point2D = p1
 
interface Point3D {x: number;y: number;z: number}
let p3:Point3D
p2 = p3
 
class Point3D {x: number;y: number;z: number}
let p3: Point2D = new Point3D()

解释:

  1. 接口之间的兼容性,类似于class
  2. class 和 interface之间也可以兼容

2.6.4 函数之间的兼容性

参数个数

type F1 = (a: number) => void
type F2 = (a: number,b: number) => void
let f1:F1
let f2:F2 = f1
// 参数多的兼容参数少的
// 参数少的可以赋值给参数多的
// 和上面的兼容性不同

参数类型

image.png

参数类型相同,相同位置的参数类型要相同(原始类型)或兼容(对象类型)

image.png

参数类型,相同位置的参数类型要相同或者兼容

解释:

  1. 注意,此处与前面讲到的接口兼容性冲突
  2. 技巧:将对象拆开,把每个属性看作一个个参数,则参数少的( f2 )可以赋值给参数多的(f3)

返回值类型

image.png

返回值类型,只关注返回值类型本身即可

解释:

  1. 如果返回值类型是原始类型给,此时两个类型都要相同,比如,左侧类型F5和F6
  2. 如果返回值类型是对象类型,此时成员多的可以赋值给成员少的,比如,右侧类型F7和F8

2.6.5 交叉类型

image.png

交叉类型(&):功能类似于接口继承(extends),用于组合多个类型为一个类型(常用于对象类型)

相当于

image.png

2.7 泛型和keyof

泛型的基本使用

function id<Type>(value:Type):Type{return value}

  1. 语法:在函数名称的后面添加<>(尖括号),尖括号中添加变量类型,比如此处的Type
  2. 类型变量Type,是一种特殊类型的变量,它处理类型而不是值。
  3. 该类型变量相当于一个容器,能够捕获用户提供的类型(具体是什么类型由用户调用该函数时指定)。
  4. 因为Type是类型,因此可以将其作为函数参数和返回值的类型,表示参数和返回值具有相同的类型。
  5. 类型变量Type,可以是任意合法的变量名称。

调用泛型函数

image.png

  1. 语法:在函数名称的后面添加<>尖括号,尖括号中制定具体的类型,
  2. 当传入类型number后,这个类型就会被函数声明时指定的类型变量Type捕获到。
  3. 此时,Type的类型就是number,所以,函数id参数和返回值的类型也都是number。 同样,如果传入类型string,函数id参数和返回值的类型就是string 这样,通过泛型就做到了让id函数与多种不同的类型在一起工作,实现了复用的同时保证了类型安全。

泛型约束

image.png

泛型约束:默认情况下,泛型函数的类型变量Type可以白标多个类型,这导致无法访问任何属性,比如,id('a')调用函数时获取参数的长度,Type可以代表任意类型,无法保证一定存在length属性,比如number类型就没有length,因此需要为泛型添加约束来收缩类型(缩窄类型取值范围)。

1.指定更加具体的类型

image.png

比如,将类型修改为Type[] (数组类型),因为只要是数组就一定存在length属性,因此就可以访问了

2.添加约束

image.png

  1. 创建描述约束的接口Ilength,该接口要求提供length属性。

  2. 通过extends关键字使用该接口,为泛型(类型变量)添加约束。

  3. 该约束表示:传入的类型必须有length属性。

多个泛型变量的情况

泛型的类型变量可以有多个,并且类型变量之间还可以约束(比如,第二个类型变量受第一个类型变量的约束)

image.png

  1. 添加了第二个类型变量Key,两个类型变量之间使用(,)逗号分割。
  2. keyof关键字接收一个对象类型,生成其键名称(可能是字符串或数字)的联合类型。
  3. 本实例中keyof Type实际上获取的是person对象所有键的联合类型,也就是:'name' | 'age'。
  4. 类型变量Key受Type约束,可以理解为Key只能是Type所有键中的任意一个,或者说只能访问对象中存在的属性

泛型接口

接口也可以配合泛型来使用,以增加其灵活性,增强其复用性。

image.png

image.png

  1. 在接口名称的后面添加<类型变量>,那么,这个接口就变成了泛型接口
  2. 接口的类型变量,对接口中所有的其他成员可见,也就是接口中所有成员都可以使用类型变量。
  3. 使用泛型接口是,需要显示指定具体的类型(比如,此处的IdFunc)。
  4. 此时,id方法的参数和返回值类型都是number;ids方法的返回值类型是number[]

数组是泛型接口

image.png

image.png

泛型类

image.png