[TOC] >[success] # TS -- interface 接口 [第一条的参考文章](https://blog.csdn.net/qq_21033779/article/details/79071887) ~~~ 1.了解什么是接口:接口只是定义了一些方法,而没有去实现,多用于程序 设计时,只是设计需要有什么样的功能,但是并没有实现任何功能,这些功 能需要被另一个类(B)继承后,由 类B去实现其中的某个功能或全部功能 '接口定义了多个类的公共行为规范,这些行为是和外部进行交流的通道' 2.疯狂java 讲义中描述接口(这里笔者稍微改动了下):电脑主板上有usb接 口,但是不同时期usb接口遵循不同的协议,必须遵循了这个协议才能享受 对应不同时期usb 版本带来的体验,这个规范我们可以理解成接口,他定义 了一些规则,但具体的方法我们实现这个接口的时候各大厂商按照自己优化 去实现,好处是让规范和实现分离,让软件之间面向接口耦合,是一种松耦 合的设计,这样主板就不会关心这个是那个厂家制造的usb,也不管你的内 部实现,只要你遵循了我的协议,我们就能匹配。'java' 还利用接口的特性 实现了多继承 3.pyhon 角度来看,pyhon没有明确接口概念,使用的抽象类来变相实现, 加上本身的多继承的特性,也让他可以将接口这个概念和其他语言划分开 4.上面是通过csdn作者--' leeyan85'和疯狂java讲义书中加上笔者本身略微 学习过pyhon 和 java,在这基础上通过语言的对比,和描述希望可以对前端 本身没有接口概念的开发一点帮助 ~~~ >[info] ## 开始TS的接口 ~~~ 1.可选类型 -- 用'?'号修饰 2.索引签名 -- 可以添加不确定参数 3.限制接口中的参数是只读 -- readonly 4.接口 数组 和只读 5.接口定义函数 6.接口做闭包(混合类型) 7.绕过接口的多余参数检查-- 三种方式 ~~~ >[danger] ##### 通过几个例子了解ts 接口 ~~~ 1.例子来自'官方文档',改进了解接口 2.下面的第一个案例是按照官网思路进行改进的为了更加直观,有一个 方法 用了打印个人信息的,我们用'ts' 约束了 传递参数,有几个分别是什么类型 3.看完下面的案例其实深入思考,可以指定传入的对象每一个key规定类 型,和传递参数,缺点灵活性不高,不能将公共格式参数的提取出来复用 ~~~ * 根据官网案例的思路改进 ~~~ // 这里用了ts规定了parmasinfo 参数格式 function printPesonInfo(parmasinfo:{name:string,sex:string}) { console.log(`姓名:${parmasinfo.name }性别:${parmasinfo.sex}`) } // 错误使用因为没有使用定义的sex // let paramsinfo = {name:'wang',age:12} let paramsinfo = {name:'wang',age:12,sex:'男'} printPesonInfo(paramsinfo) // 姓名:wang性别:男 ~~~ >[danger] ##### 了解接口 -- interface ~~~ 1. 使用在变量前声明'interface' 2. 简单案例: 声明一个interface: interface person { name: string; age: number; } 然后就可以使用这个interface来限制一个对象的类型: let tom: person = { name: 'tom', age: 12 } 3.和刚才比我们解决了缺点中的问题可以公共提取,规范一个系列 ~~~ * 所有动物都有一个最基本的信息名字和性别,必须按照这个规范来创建 ~~~ interface Baseinfo { name:string, sex:string } // 人 function printPesonInfo(parmasinfo: Baseinfo) { console.log(`姓名:${parmasinfo.name }性别:${parmasinfo.sex}`) } // 动物 function printAnimalInfo(parmasinfo: Baseinfo) { console.log(`姓名:${parmasinfo.name }性别:${parmasinfo.sex}`) } let paramsinfo = {name:'wang',age:12,sex:'男'} printPesonInfo(paramsinfo) // 姓名:wang性别:男 let paramsAnimainfo = {name:'小黑',age:12,sex:'公'} printAnimalInfo(paramsAnimainfo) // 姓名:小黑性别:公 ~~~ >[danger] ##### 可选类型 -- 用'?'号修饰 ~~~ 1.现在我们发现不是接口中提供的每一个参数我们都需要,为此'TS' 提供了 一种'?' 来修饰,被问号修饰的参数就是可以选传,不是必须参数 ~~~ ~~~ interface Baseinfo { name:string, sex?:string } // 人 function printPesonInfo(parmasinfo: Baseinfo) { console.log(`姓名:${parmasinfo.name }`) } let paramsinfo = {name:'wang'} printPesonInfo(paramsinfo) // 姓名:wang ~~~ >[danger] ##### 索引签名 -- 可以添加不确定参数 ~~~ 1.刚才的问号让我们可以选填一些参数,但是如果需要增加一些不确定 参数就需要使用 '索引签名' 2.利用这个可以规定你的索引签名的类型(即使你的索引规定是string类型 但也可以使用数字,案例三有说明) 3.主要注意这里有个问题:需要注意的是,一旦定义了任意属性, 那么确定属性和可选属性的类型都必须是它的类型的子集(案例二) ~~~ * 案例一 ~~~ interface Baseinfo { name:string, sex?:string, [other:string]:any } // 人 function printPesonInfo(parmasinfo: Baseinfo) { console.log(`姓名:${parmasinfo.name }`) } // 接口中的索引签名other 就会收到age printPesonInfo( {name:'wang',age:13}) // wang ~~~ * 案例二的错误示范 ~~~ // 错误示范 interface Person { name: string; age?: number; // 必须是string类型才行,需要是propName类型的子集 [propName: string]: string; // 改正 [propName: string]: any; } } ~~~ * 案例三 ~~~ interface Person { name: string; age?: number; // 必须是string类型才行,需要是propName类型的子集 [propName: string]: any; } let tom: Person = { name: 'Tom', age: 25, 1: 'male' // 因为key 会调用 自带的toString 方法 }; ~~~ >[danger] ##### 限制接口中的参数是只读 -- readonly ~~~ 1.readonly 和 const 都是只读,但两者如何用区别在哪里,首先'readonly ' 是'TS' 在接口提出,只针对接口中的参数赋值后就禁止更改,而const 是 es6 提出的针对变量,不让变量进行更改 2.用'ts' 文档的总结来说:最简单判断该用readonly还是const的方法是看要 把它做为变量使用还是做为一个属性。 做为变量使用的话用const,若做 为属性则使用readonly。 ~~~ * 使用 ~~~ interface Point { readonly x: number; readonly y: number; } // p1 是接口Point 类型 let p1: Point = { x: 10, y: 20 }; p1.x = 5; // error! 已经设置了readonly 属性所以禁止更改 ~~~ >[danger] ##### 接口 数组 和只读 ~~~ 1.我们还可以利用接口定义数组位置的类型,和元组不同,元组不仅限制 了类型也限制了最开始赋值时候的长度(push 还是可以往元组添加东西的) ~~~ * 接口定义数组 ~~~ // 接口 数组 和只读 // 定义了一个数组接口,数组第一位必须数字,第二位是字符串 interface ArrInter { 0:number, 1:string } let arr: ArrInter = [1,"w",3] console.log(arr) ~~~ * 接口 数组 和只读 ~~~ // 定义了一个数组接口,数组第一位必须数字,第二位是字符串 interface ArrInter { readonly 0:number, 1:string } let arr: ArrInter = [1,"w",3] arr[0] = 5 // erro 后续禁止改变 ~~~ >[danger] ##### 接口定义函数 ~~~ 1.接口不止能定义变量值也可以定义函数 ~~~ * 将BaseInfo 加工 ~~~ // 定义了一个信息接口,里面有姓名,年龄和一个吃的方法返回的类型是string类型 interface BaseInfo{ name:string, age:number, eat(food: string):string } const personInfo:BaseInfo = { name:'wang', age:99, eat(parmas){ return parmas } } ~~~ * 只定义 方法的接口 ~~~ 1.对于函数类型的类型检查来说,函数的参数名不需要与接口里定义的名字相匹配 2.函数的参数会逐个进行检查,要求对应位置上的参数类型是兼容的 ~~~ ~~~ interface SearchFunc { (source: string, subString: string): boolean; } let mySearch: SearchFunc; mySearch = function(src: string, sub: string): boolean { let result = src.search(sub); return result > -1; } ~~~ * 类型别名 ~~~ // ts箭头函数不加大括号的含义是 返回 类型 type PersonInfo = (name:string) => string // 这里箭头函数是正常的js 表达的意思 let personName:PersonInfo = (name) => name // 做一个小备注冒号后面等号前面属于ts定义, (x: number, y: number, z?:number) => number // 是ts的定义这里=> 不是js箭头函数 const add2: (x: number, y: number, z?:number) => number = add ~~~ >[danger] ##### 接口做闭包(混合类型) ~~~ 1.利用function 也可以定义key的方式 ~~~ * 有时候,一个函数还可以有自己的属性和方法: ~~~ interface Counter { (start: number): string; interval: number; reset(): void; } function getCounter(): Counter { let counter = <Counter>function (start: number) { }; counter.interval = 123; counter.reset = function () { }; return counter; } let c = getCounter(); c(10); c.reset(); c.interval = 5.0; ~~~ * 编译后效果 ~~~ function getCounter() { var counter = function (start) { }; counter.interval = 123; counter.reset = function () { }; return counter; } var c = getCounter(); c(10); c.reset(); c.interval = 5.0; ~~~ * 案例二 编译前 ~~~ interface Counter { (): void; count: number; } function getCounter(): Counter { const c = ()=>{ c.count++ } c.count = 0 return c } ~~~ * 编译后 ~~~ function getCounter() { var c = function () { c.count++; }; c.count = 0; return c; } ~~~ >[danger] ##### 绕过接口的多余参数检查-- 三种方式 * 什么是接口的多余参数检查 ~~~ interface Baseinfo { name:string, sex?:string } // 人 function printPesonInfo(parmasinfo: Baseinfo) { console.log(`姓名:${parmasinfo.name }`) } // 如果直接传递参数,且传递的参数key未在接口中定义会提示错误 printPesonInfo( {name:'wang',age:13} ) // 报错的 ~~~ * 第一种解决使用类型断言 ~~~ interface Baseinfo { name:string, sex?:string } // 人 function printPesonInfo(parmasinfo: Baseinfo) { console.log(`姓名:${parmasinfo.name }`) } // 利用类型断言,告诉编译器我们传递的参数 就是Baseinfo 接口的东西 printPesonInfo( {name:'wang',age:13} as Baseinfo ) // wang ~~~ * 第二种 索引签名 ~~~ interface Baseinfo { name:string, sex?:string, [other:string]:any } // 人 function printPesonInfo(parmasinfo: Baseinfo) { console.log(`姓名:${parmasinfo.name }`) } // 接口中的索引签名other 就会收到age printPesonInfo( {name:'wang',age:13}) // wang ~~~ * 利用类型兼容性 ~~~ interface Baseinfo { name:string, sex?:string, } // 人 function printPesonInfo(parmasinfo: Baseinfo) { console.log(`姓名:${parmasinfo.name }`) } let paramsinfo = {name:'wang',age:13} // 类型兼容性就是我们定义的paramsinfo 不管有都少东西,只要包含接口中定义的即可 printPesonInfo(paramsinfo) // 姓名:wang ~~~