持续创作,加速成长!这是我参与「掘金日新计划 · 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,不能再放确定的参数){
if(typeof 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.log(Cat.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.length)
return arg
}
(2) 方法2 extends
T extends IWithLength表示具有length属性
interface IWithLength{
length:number
}
function echoWithArr<T extends IWithLength>(arg:T):T{
console.log(arr.length)
return 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'>