(精华)2020年8月2日 TypeScript 接口的使用
发布日期:2021-06-29 15:08:41 浏览次数:2 分类:技术文章

本文共 12270 字,大约阅读时间需要 40 分钟。

接口

接口的作用:在面向对象OOP的编程中,接口是一种规范的定义,它定义了行为和动作的规范,在程序设计里面,接口起到一种限制和规范的作用。

接口定义了某一批类所需要遵守的规范,接口不关心这些类的内部状态数据,也不关心这些类里方法的实现细节,它只规定这批类里必须提供某些方法,提供这些方法的类就可以满足实际需要。 typescrip中的接口类似于java,同时还增加了更灵活的接口类型,包括属性、函数、可索引和类等。

OOP : Object Oriented Programming

定义标准。

  • 属性类接口
  • 函数类型接口
  • 可索引接口
  • 类类型接口
  • 接口扩展
    1. 类实现接口
    2. 接口可以继承接口
    3. 类可以继承类, 类也可以实现接口
    4. 接口继承类
    5. 总结
  • 接口与type的区分

一. 属性类接口

1.1 未使用接口的情况:

print的参数是一个对象, 但是必须有label这个属性

ts中自定义方法传入参数,对json进行约束

function print(labelObj:{
label:string }){
console.log( labelObj.label );}// 跳过这些检查的方式, 它就是将这个对象赋值给一个另一个变量: // myObj 不会经过额外属性检查,所以编译器不会报错。print({
name:'张三'}); //错误的写法print({
label:'你好'}); //正确的写法print({
label:"Size test", name:'sss'} as {
label:string });

注意下面写法会提示报错:

print({

label:“Size test”,
name:‘laney’
});

将对象字面量作为参数传递的时候, 对象字面量会被特殊对待而且会经过"额外属性检查"。

如果一个对象字面量存在任何"目标类型"不包含的属性时,你会得到一个错误。

如何跳过这种检查

官网上给了三种方式:

  • 第一种使用类型断言:

    print({
    label:“Size test”,
    name:‘laney’
    } as { label:string });

  • 第二种添加字符串索引签名:

function print(labelObj:{ label:string ,[propName: string]: any;}){

console.log(labelObj);
}
print({
label:“Size test”,
name:‘laney’
});

  • 第三种将字面量赋值给另外一个变量:

它就是将这个对象赋值给一个另一个变量:

myObj 不会经过额外属性检查,所以编译器不会报错。

let myObj={ size:10,label:“Size 10 Object” };

print(myObj);

这是一个简单的函数,在调用print时,会检查传入参数的类型,并且只检查那些必需属性是否存在,并且类型是否匹配。下面利用接口重写上面的例子

1.2 使用接口

//接口interface labelValue{
label:string; size:number;}//type 别名type labelObj = {
label?:string; size:number;} function print(labelObj:labelValue){
console.log( 'labelObj.label' ); console.log( labelObj ); } let myObj2={
size:10,label:"Size 10 Object",name:'Alice' }; print(myObj2);

在参数类型检查的时候,会发现参数遵循的是接口labelValue的规范,然后就回去检查是不是符合接口所描述的规范。

接口就相当于是一个约束,让大家都遵循。

//接口:行为和动作的规范,对批量方法进行约束

//就是传入对象的约束 属性接口

1.3 类型断言

类型断言 Type Assertion

定义: 可以用来手动指定一个值的类型

语法

  • 方式一: <类型> 值
function getLength(x:number|string):number{
if((
x).length) {
return (
x).length } else {
return x.toString().length }}
  • 方式二: 值 as 类型
function getLength(x:number|string):number{
if((x as string).length){
return (x as string).length } else {
return x.toString().length }}

类型断言并非是类型转换,断言一个联合类型中不存在的类型会报错!

function wrong(x:number|string):boolean{
return
x // 报错!}

1.3 额外的属性检测

interface labelValue{
label:string; size:number;} function print(labelObj:labelValue){
console.log( 'labelObj.label' ); console.log( labelObj ); }

上面1.2 使用接口 的例子,如果这么这么传值会报错

print({
label:‘hello’,
size:30,
name:‘laney’
});

如何绕过额外属性检查,官方给了3种方式

解决方式:

//  第一种使用类型断言print({
label1:'hello', size:30} as labelValue);// 第二种添加字符串索引签名/* interface labelValue{ label?:string; size:number; [propName: string]: any; //+} *///将字面量赋值给另外一个变量 var labK = {
label1:'hello', size:30 };print(labK);

1.4 可选属性

interface FullName{
firstName:string; //注意;结束 secondName:string; // secondName?:string; //可选属性,可传可不传 hello(str:string):string}function printName(name:FullName){
// 必须传入对象 firstName secondName console.log(name.firstName+'--'+name.secondName); console.log(name)}// printName('1213'); //错误/*传入的参数必须包含 firstName secondName*/var obj={
firstName:'张', secondName:'三', hello(str:string):string{
return obj.firstName+obj.secondName; }};printName(obj)//如果secondName是可选参数,传参的时候可加以不加// printName({
// firstName:'firstName'// })

通过ajax实例演示 属性类接口

/*       $.ajax({             type: "GET",             url: "test.json",             data: {username:$("#username").val(), content:$("#content").val()},             dataType: "json"                      });         */interface Config{
type:string; url:string; data?:string; dataType:string;}//原生js封装的ajax function ajax(config:Config){
var xhr=new XMLHttpRequest(); xhr.open(config.type,config.url,true); xhr.send(config.data); xhr.onreadystatechange=function(){
if(xhr.readyState==4 && xhr.status==200){
console.log('chengong'); if(config.dataType=='json'){
console.log(JSON.parse(xhr.responseText)); }else{
console.log(xhr.responseText) } } }}ajax({
type:'get', data:'name=zhangsan', url:'http://localhost:3000/test', //api dataType:'json'})

三、函数类型接口

ts中函数型接口,非常类似于java、c#中使用lambda表达式传入匿名函数。

因为对象中仅包含一个函数,这个对象的全部意义也仅在于那个可被外部调用的函数,故而称之为函数型接口。

加密的函数类型接口

函数类型的接口:对方法传入的参数以及返回值进行约束 批量约束

interface encrypt{
(key:string,value:string):string;}var md5:encrypt=function(key:string,value:string):string{
//模拟操作 return key+value;}console.log(md5('name','zhangsan'));var sha1:encrypt=function(key:string,value:string):string{
//模拟操作 return key+'----'+value;}console.log(sha1('name','lisi'));
//对两个数进行运算得到另一个数  函数型接口interface  CalcTwo{
(a:number,b:number):number;}/** * 计算数组被某种算法运算的结果 * @param {number[]} arr 数组 * @param {CalcTwo} calc 用户指定的算法函数 * @param {number} initVal 传入初始值 * @returns {number} 得到最终运算结果**/function calcArr(arr:number[],calc:CalcTwo,initVal:number):number{
var result=initVal; arr.forEach((value)=>{
result = calc(result,value); }); return result;}

使用:

var arr:number[]=[1,3,5,7,9]; var  add=function(a:number,b:number):number{
return a+b; }; var multiply=function(a:number,b:number):number{
return a*b; };console.log("相加",calcArr(arr, add, 0));//25console.log("相乘",calcArr(arr, multiply, 1));//945//或者直接传入一个匿名函数亦可var s1=calcArr(arr,function(a,b){
return a*b;},1);console.log("s1",s1);//945 var s2=calcArr(arr,function (a,b) {
return (a+1)*(b+1); },1);console.log("s2",s2);//10170

四、可索引接口

(不常用)

可索引接口:数组、对象的约束 (不常用)

ts定义数组的方式

/*
var arr:number[]=[2342,235325]
var arr1:Array=[‘111’,‘222’]
*/

4.1 可索引接口 对数组的约束

interface UserArr{
[index:number]:string } var arr:UserArr=['aaa','bbb']; console.log(arr[0]); var arr:UserArr=[123,'bbb']; /*错误*/ console.log(arr[0]);

4.2 可索引接口 对对象的约束

interface UserObj{
[index:string]:string } var arr:UserObj={
name:'张三'};

五、类类型接口 ,利用implements实现接口

类类型接口用来规范一个类的内容。示例代码如下

用的多,和抽象类有点相似,

类实现接口本质上 即类遵循接口的约束,接口里面写了多少个函数、参数,实现的类里面也要写相同的函数、参数。

interface FullName {
username: string; } //Person这个类需要遵循接口FullName的约束class Person implements FullName {
username: string; constructor(n: string) {
this.username = n; } }

可以在接口中描述一个方法,并在类里具体实现它的功能,如同下面的setName方法一样:

interface FullName {
username: string; setName(name: string): void; getName():void;} class Person implements FullName {
username: string; constructor(name: string) {
this.username = name; } setName(str: string) {
this.username = str; } getName(){
console.log(this.username) } } var p1 = new Person('Alice');p1.setName('tony');p1.getName();

下面这个不演示,自行学习

interface Animal{
name:string; eat(str:string):void; }class Dog implements Animal{
name:string; constructor(name:string){
this.name=name; } eat(){
console.log(this.name+'吃粮食') }}var d=new Dog('小黑');d.eat();class Cat implements Animal{
name:string; constructor(name:string){
this.name=name; } eat(food:string){
console.log(this.name+'吃'+food); }}var c=new Cat('小花');c.eat('老鼠');

六、接口扩展

6.1 类实现接口 - 类类型接口 前面已讲

interface ClockInterface{
currentTime:Date; getTime(d:Date):any;}class Clock implements ClockInterface{
currentTime:Date; constructor(){
this.currentTime = new Date() } getTime(){
console.log("123"); console.log(this.currentTime) } }let clock1=new Clock();clock1.getTime();

类实现接口本质上也是一样的,即类遵循接口的约束,接口里面写了多少个函数、参数,实现的类里面也要写相同的函数、参数。

接口继承就是说接口可以通过其他接口来扩展自己。

Typescript 允许接口继承多个接口。

继承使用关键字 extends。

6.2 接口可以继承接口

interface Animal {
eat(): void;}// Person 接口继承了 接口Animal// 单接口继承,只继承一个接口interface Person extends Animal {
work(): void;}//类实现接口 WebFront类实现接口Personclass WebFront implements Person {
public name: string; constructor(name: string) {
this.name = name; } eat() {
console.log(this.name + "喜欢吃馒头"); } work() {
console.log(this.name + "写代码"); }}var w = new WebFront("小李");w.eat();

接口的扩展就是给多添加了一些约束。一个接口可以扩展多个接口,当一个接口扩展另一个接口,也继承了该接口的约束。

6.3. 类可以继承类, 同时类也可以实现接口

interface Animal {
eat(): void;}interface Person extends Animal {
work(): void;}class Programmer {
public name: string; constructor(name: string) {
this.name = name; } coding(code: string) {
console.log(this.name + code); }}//类继承类并实现接口: WebFront继承类Programmer 并实现接口Personclass WebFront extends Programmer implements Person {
constructor(name: string) {
super(name); } eat() {
console.log(this.name + "喜欢吃馒头"); } work() {
console.log(this.name + "写代码"); }}var w = new WebFront("小李");w.eat();w.coding("写ts代码");

6.4. 接口继承类

class Point{
x:number; y:number; constructor(x:number,y:number){
this.x = x; this.y = y; } log(){
console.log('123456'); } } interface Point3d extends Point{
z:number; } var point3d:Point3d={
x:1, y:2, z:3, log(){
console.log('7777'); } }point3d.log();//7777

官方解释:当接口继承了一个类类型时,它会继承类的成员但不包括其实现。

也就是说,接口继承类值继承了它的约束条件,具体的值并不继承。

总结

接口扩展(继承)接口

interfaceA extends interfaceB

类实现接口

classA implements interfaceA

接口继承类

interfaceB extends classB

类扩展(继承)类

classA extends classB

有人肯分不清上面时候用extends,什么时候用implements。

记住一句话,只要涉及到继承就是extends.

interface 与 type 声明类型的区别

namespace InterfaceAndType {
// 1. Objects / Functions // 两者都可以用来描述对象或函数的类型,但是语法不同。 // Interface // interface Point {
// x: number; // y: number; // } // interface SetPoint {
// (x: number, y: number): void; // } // Type alias type Point = {
x: number; y: number; }; type SetPoint = (x: number, y: number) => void; // 2. Other Types 类型别名还可以用于其他类型,如基本类型(原始值)、联合类型、元组。Interface不行 // primitive 基本类型 type Name = string; // object type PartialPointX = {
x: number; }; type PartialPointY = {
y: number; }; // union type PartialPoint = PartialPointX | PartialPointY; // tuple type Data = [number, string]; // dom let div = document.createElement('div'); type B = typeof div; // 3. Extend // 两者都可以扩展,但是语法又有所不同。此外,请注意接口和类型别名不是互斥的。接口可以扩展类型别名,反之亦然。 // Interface extends interface interface PointX {
x: number; } interface PointY extends PointX {
y: number; } // Type alias extends type alias type PointA = {
x: number; }; type PointB = PointA & {
y: number; }; // Interface extends type alias type SizeA = {
x: number; }; interface SizeB extends SizeA {
y: number; } // Type alias extends interface interface SizeX {
x: number; } type SizeY = SizeX & {
y: number; }; // 4. class Implements // 类可以以相同的方式实现接口或类型别名。但是请注意,类和接口被认为是静态的。因此,它们不能实现/扩展命名联合类型的类型别名。 interface PointV {
x: number; y: number; } class SomePoint implements PointV {
x:number; y:number; constructor(x:number,y:number){
this.x = x; this.y = y; } } type PointV2 = {
x: number; y: number; }; class SomePoint2 implements PointV2 {
x:number; y:number; constructor(x:number,y:number){
this.x = x; this.y = y; } } type PointXX = {
x: number; } | {
y: number; }; // FIXME: can not implement a union type // class SomePoint3 implements PointXX {
// x:number; // y:number; // constructor(x:number,y:number){
// this.x = x; // this.y = y; // } // }}

总结

interface 和 type 很像,很多场景,两者都能使用。但也有细微的差别:

类型:对象、函数两者都适用,但是 type 可以用于基础类型、联合类型、元祖。

同名合并:interface 支持,type 不支持。
计算属性:type 支持, interface 不支持。
总的来说,公共的用 interface 实现,不能用 interface 实现的再用 type 实现。主要是一个项目最好保持一致。

转载地址:https://codeboy.blog.csdn.net/article/details/107748404 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:(精华)2020年8月2日 TypeScript 泛型的使用
下一篇:(精华)2020年8月2日 TypeScript 类的使用(封装,继承,多态)

发表评论

最新留言

感谢大佬
[***.8.128.20]2024年04月23日 13时47分19秒