typeScript-接口

212 阅读4分钟

接口的定义

接口是对行为的抽象,而具体如何实现需要由类去实现。接口除了用于对类的一部分行为进行抽象外,也常用于对对象的形状进行描述。

interface Person {
    name: string;
    age: number;
}

let tom: Person = {
    name: 'Tom',
    age: 25
};

上面定义了一个了一个接口Person,并定义了一个变量tom为对应的接口类型。 接口一般首字母大写。有的编程语言中会建议接口的名称加上 I 前缀。

接口的作用

确保数据结构的一致性。接口作为一种约束,通过定义一个接口,约定了变量、类、函数应该按照什么样的格式进行声明,实现多人合作的一致性。ts编译器依赖接口做类型检查,最终编译为js后,接口将会被移除。

接口的基础篇

1. 赋值的时候,变量的形状必须和接口的形状保持一致。

定义接口的时候,不仅仅可以有属性,也可以有方法。当我们定义一个对象是该接口类型时,该对象必须包含对应的属性和方法(可选属性除外)。少了或者多了一些属性,编译都不会通过。

如上面的例子中,如果这样定义tom,将会报错
let tom: Person={
name:'wang'
}

2.可选属性(即?标识)

上面提到一个变量或对象是对应的接口类型,就必须实现接口中所有的属性和方法,但实际在开发过程中,我们为了提高接口的灵活性,在设计接口的时候,会将某些属性或方法设计为可选的,可选属性的作用就是在实际实现接口的时候,想实现可以实现,不想实现也没关系,如果该属性存在,还能约束其类型。

interface Person {
    name:string,
    age?:number,
    say ?():void
}
定义了一个Person接口,增加了age属性和say方法,两个都是可选的

注意:可选属性如果没有赋值,那么获取到的值是undefined; 对于可选方法,必须先进行判断,再调用,否则会报错;

3.只读属性(readonly)

如果没有特别声明,在接口中定义的属性可读可写,但如果我们希望对象中的一些字段只能在创建的时候被赋值,可以在其前面加上readonly,后面如果进行强制修改,会报错。

interface Person {
    name:string,
   readonly sex:string,
    age?:number,
    say ?():void
}
const stu: Person = {
  name:'wangzhaoxia', 
  sex:'man'  //只能在初始化的时候赋值
}
stu.sex='feman' // 会提示错误,只读属性不能被赋值

接口的进阶篇

1.函数类型

在上面我们约定了变量(对象)的属性类型,接下来我们来看下如何用interface来规范其函数类型,函数类型的接口里面需要列出参数列表以及函数返回值的类型

interface GetStr {
    (x:number,y:number):string;
}
const stu: GetStr = (gradeNum:number,classNum:number) :string =>{
    return  `${gradeNum}年级${classNum}班级`
}
// ts类型系统默认推论可以不必写参数类型定义,即可以写成下面这样:
const stu: GetStr = (gradeNum,classNum)=>{
    return  `${gradeNum}年级${classNum}班级`
}

2.数组类型(可索引类型)

它描述了对象索引的类型,还有相应的索引返回值类型。即通过索引得到的类型,如通过下标获取数组arr[2]的值。索引器的类型只能是number或string

// 数字索引类型
interface stringArr {
    [index:number]:string  //[index:number]是索引器(索引签名),string是该数组元素类型
}
let strArr:stringArr=['wang','chen','jiang']
let str:string=strArr[0]
console.log(str)  

// 字符串索引类型
interface ColorMap {
    [index: string]: string;
}

let colorMap: ColorMap;
colorMap = {
    a: 'Red',
    b: 'Blue',
    c: 'Green'
};
console.log(colorMap.a,colorMap['a'])

在同时使用两种类型的索引时,数字索引的返回值必须是字符串索引返回值类型的子类型

interface ColorMap {
    [x:number]:number, //这里会报错,返回值类型必须是string类型
    [index: string]: string;
}

3.类类型

所谓类类型,即把公共的部分抽象成为接口,通过类来实现接口,而不是把接口直接拿来用

interface Clock {
    time:Date,
    setTime(d:Date):void
}
class Time implements Clock{
    time:Date
    setTime(d:Date):void{
        this.time=d
    }
}

4.接口的继承

接口可以通过extends来实现继承(继承一个或多个都可以),方便将一个接口里的成员复制到另一个接口里。

interface People{
    name: string;
}

interface Student extends People{
    age: number;
}

let stu: Student = {name: '小明', age: 12 };

// 一个接口可以继承多个接口, 使用 ,分隔
interface Shape {
    color: string;
}

interface Pen {
    width: number;
}

interface Square extends Shape, Pen {
    length: number;
}
let c=<Square>{} // 注意这里的写法:创建一个对象,并指定泛型
c.color='red'
c.width=100
c.length=100
console.log(c,typeof c)

5.混合类型

即在一个接口中定义多种类型,比如属性、函数。

interface People{
    (): void;
    sex: string;
    age: number;
    sayHi(): void;
}

// 这边需要用到类型断言 as 或者 <>
function getPeople(): People{
    let people: People = (() => {}) as People;
    // let people=<People> function(){}
    people.sex = 'feman';
    people.age = 23;
    people.sayHi = () => { console.log('Hi!'+people.sex) }
    return people
}

let p = getPeople();
p.age = 24;
p.sex='man'
console.log(p.sex)
console.log('---')
console.log(p.sayHi())