TypeScript在实际开发中的实践

235 阅读7分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第9天,点击查看活动详情

许多小伙伴在学习TypeScript都会遇到一个问题,就是在学习TypeScript时,能够明白各个类型的作用,但在实际开发中,却并不懂得如何根据实际情况去运用,并且对于一些类型的联合使用也不太熟练,本文忽略了比较基础的内容,着重对实际开发中常用的内容进行讲解回顾,并对重要概念 类、函数、泛型以及类和接口的联合使用、等进行了讲解,希望能够对大家有所帮助。

一、interface接口

1.概念

接口是对对象的形状(shape)进行描述,TypeScript,类型检查关注的是值的形状,是Duck Typing类型语言,Duck Typing(鸭子类型) 动态编程语言的对象类型推断策略,更关注对象类型的本身、抽象的契约、非常灵活、能够描述类型。

2.常用接口定义形式

(1)普通interface
    interface Person{
        name:string,
        age:number,
    }
(2)只读属性、可选属性
    interface Person{
        readonly id:numbr
        age?:number
    }
(3)索引签名,当实际使用时,后端常常返回的接口数据非常复杂,存在非必要字段。只要数据符合索引类型就可以。
    interface Person{
        [propName:string]:any
    }

二、Function 函数

1.可选参数后不能放确定类型的参数

     function (x:number,y?:string,不能再放确定的参数){
            iftypeof z===number){
      }
    }

2.声明式

    let add2:(x:number,y?:string)=>number=add 

注意:此处箭头并非es6 箭头函数 而是ts中对于函数返回值类型的定义

3.interface 声明函数类型

    interface ISum{
            (x:number,y?:string):number
    }
    let add2:ISum=add 

三、类型推断 类型断言 联合类型

1.联合类型 union type

    let age:number | string

2.类型断言

有时我们的数据类型可能是联合类型,在编译时,编译器会提示我们类型不确定的情况,我们可以使用as进行类型断言,告诉编译器,我们确定该数据为某种类型,从而使用对应的类型方法。

function getLength(input:string|number):number{
	let str=input as string // 断言input为字符串类型
	if(str.length){
		return str.length
	}else{
		const number=input as number // 断言input为数字类型
		return str.toString().length
	}
}

3.type guard 类型守卫

type of || instance of
js中提供了typeof、instance of方法用来检测一个变量的类型,TypeScript中也使用typeof、instance of来对变量类型进行检测并作出相应操作。

function getLength(input:string|number):number{
	if(typeof input ===string){
		return input.length
	}else{
		return input.toString().length
	}
}

四、类

1.概念

类(Class):定义了一切事物的抽象特点
对象(Object):类的实例
面向对象(OOP)三大特性:封装、继承、多态
简单定义一个动物类,利用该类,创建一个蛇的实例,类中包含属性及方法:

class Animal{
 	constructor(name){
            this.name=name
 	}
	run(){
            return `${this.name} is running`
	}
}
const snake=new Aanimal('lily')

console.log(snake.run())

2.类的继承

类可以使用extends进行继承。

class Dog extends Animal{
	bark(){
	return `${this.name} is barking`
}
}
const xiaobao=new Dog('xiaobao')
console.log(xiaobao.run() ,xiaobao.bark())

3.多态

所谓父类型的引用指向子类型的对象,不同类型的对象针对相同的方法,产生了不同的行为,简单来说就是父类定义一个方法并不去实现,让继承它的子类去实现  每一个子类有不同的执行结果,如下面代码,Animal中定义了run方法,我们使用cat去继承,并创建实例,run方法执行的是‘maomao’,如果再来一个dog去继承那么run方法又会得到一个不同的结果。

class Cat extends Animal{
	static categories=['mammal']
	constructor(name){
	super(name)
            console.log(this.name)
	}
	run(){
            return 'Sound,'+super.run()
	}
}
const maomao=new Cat('maomao')
console.log(maomao.run())
console.logCat.categories//可以直接访问类上的属性 不需要实例化

4.Public、Private、Protected

public:修饰的属性或方法是共有的
private:修饰的属性或方法是私有的 子类无法访问 只能在内部使用
protected:修饰的属性或方法是受保护的 子类可以访问
注:子类并非实例
因此以下代码

   class Cat extends Animal{
	static categories=['mammal']
	constructor(name){
	super(name)
            console.log(this.name)
	}
	run(){
            return 'Sound,'+super.run()
	}
}

此处super.run(),如果run方法是protected修饰此处可以使用 但maomao.run()不能使用 如果是private则都不能使用

5.只读修饰属性

类中的属性除了Public、Private、Protected,也可以使用readonly进行只读的限制。

class Animal{
	readonly name:string;
 	constructor(name){
		this.name=name
 	}
	run(){
		return `${this.name} is running`
	}
}

五、类和接口

继承的困境:类共有特性无法通过共有父类继承特性 使用接口进行特性抽象
类可以使用implements来实现接口

1.使用接口前

class Car{
	switchRadio(trigger:boolean){
	
	}
}

class Cellphone{
	switchRadio(trigger:boolean){

	}
}

2.使用接口后

interface Radio {
	switchRadio(trigger:boolean):void;
}
class Car implements Radio{
	switchRadio(trigger:boolean){
	
	}
}
class Cellphone implements Radio{
	switchRadio(trigger:boolean){

	}
}

3.使用多个接口

inerface Battery{
	checkBattery():void;
}
class Cellphone implements Radio,Battery{
	switchRadio(trigger:boolean){

	}
	checkBattery(){
	}
}

4.接口之间的继承


interface RadioWithBattery extends Radio{
	checkBattery():void;
}

上方代码可以改写为如下:

class Cellphone implements RadioWithBattery  {
	switchRadio(trigger:boolean){

	}
	checkBattery(){
	}
}

六、枚举

枚举常用的API的定义或者一些常量数据的定义中。

enum Direction{
	Up='UP',
	Down='DOWN',
	Left='LEFT',
	Right='RIGHT',
}
console.log(Direction.Up)

常量枚举:

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

常量枚举可以提升性能,不会将枚举编译成js代码(编译后会有js逻辑代码 如 Ddirection[Direction['Up']=0]='Up'),直接取值,只有常量值能使用常量枚举,计算值无法使用.

七、泛型

有时我们在定义接口等类型时,数据的类型是动态变化的,此时可以使用泛型,相当于占位符,传入时确认参数类型

1.单个泛型

function echo<T> (arg:T):T{
	return arg
    }
    const res=echo('string')

2.多个泛型

function swap<T,U>(tuple:[T,U]):[U,T]{
	return [tuple[1],tuple[0]]
}
const res2=swap(['string',123])

3.泛型约束 extends

当我们使用泛型时,由于数据的类并不能确定,我们想直接使用类似于数组的长度等方法时,会报错,此时需要泛型约束。

function echoWithArr<T>(arg:T):T{
	console.log(arr.length//会报错 无法确定类型 因此需要泛型约束
	return arg
}

(1)方法1

T[]表示这是一个数组泛型

function echoWithArr<T>(arg:T[]):T{
	console.log(arr.lengthreturn arg
}

(2) 方法2 extends

T extends IWithLength表示具有length属性

interface IWithLength{
	length:number
}
function echoWithArr<T extends IWithLength>(arg:T):T{
	console.log(arr.lengthreturn arg
}
// 只要包含length属性就可以
const str=echoWithArr('str')
const str1=echoWithArr({length:10})
const arr2=echoWithArr([1,2,3])

八、泛型在类中的使用

普通类:

class Queue{
	private data=[];
	push(item){
		return this.data.push(item)
	}
	pop(item){
		return this.data.shift(item)
	}

}

添加泛型约束后:

class Queue<T>{
	private data=[];
	push(item:T){
		return this.data.push(item)
	}
	pop(item:T){
		return this.data.shift(item)
	}

}
const queue =new Queue<number>()

九、泛型在接口中的使用

interface KeyPair<T,U>{
	key:T;
	value:U;
}
let kp:KeyPari<number,string>={key:1,value:'string'}

十、类型别名 type

类似函数类型非常麻烦 type

let sum:(x:nnumber)=>number

简化后:

type PlusType=(x:number,y:number)=>{
	number
}
let sum:PlusType
const res=sum(1,2)

十一、联合类型

联合类型是指当前数据只能赋值指定的类型,不能够赋值其他类型。

    type StrorNum=string | number
    let res:StrorNum='str'

十二、字面量 确定常量值

当我们需要确定某个常量值时,可以使用字面量提前约束,防止改动出错

const str:'name'='name'

type Direction='UP' | 'DOWN' | 'LEFT'

十三、交叉类型

type类型可以使用&进行类型的拓展,也叫作交叉类型

 interface IName{
	name:string
}
type IPerson = IName & {age:number}

let person:IPerson={name:'123',age:123}

十四、声明文件

文件名诸如jQuery.d.ts的一般为声明文件,一般目前能够支持TypeScript的库一般会编写声明文件,方便使用ts提示。
如jquery文件声明:

declare var jquery:(selector:string)=>any;

编译时检查 jquery('#foo')会补全
第三方库声明文件 需要安装 由DefinitelyTyped统一管理

十五、内置的对象类型

lib文件内置了诸多类型 同时 typescript官网 有UtilityTypes 许多方便的类型,如:
(1)Partial

全部变为可选

 interface IName{
	name:string,
	age:number
}
type IPartial=Partial<IName> //全部变为可选

(2)Omit

忽略某个属性

 interface IName{
	name:string,
	age:number
}
type IPartial=Omit<IName,name> //忽略name

type IOmit=Omit<INam,'name'>