TypeScript学习2——接口

目录

说明

接口

可选属性

只读属性

只读数组

绕开额外属性检查的方法

索引签名

接口的继承


说明

本文学习自掘金大神的文章https://juejin.cn/post/7003171767560716302,原作者是伟大的兔神。

接口

对类型的格式进行规定和约束。

原文中提到了鸭式辨型法,即,具有传入的对象拥有要求的属性就认为它符合要求。

举个例子:

function getName(obj: { name: string }) {
    return obj.name;
}
// console.log(getName({ name: 'Jack', age: 11 }));//报错,因为多出了一个age属性
// console.log(getName({ name: 'Jack' }));//正确
let myObj = { name: 'Jack', age: 123 };
console.log(getName(myObj));//正确

发现,调用函数时直接传入参数,要求字段必须完全符合,不能多也不能少。

而如果先定义对象变量,再调用函数传入变量,则可以多出一些属性了。这就说明,赋值使得类型检查变得宽松了。因为在赋值的时候,myObj会自动推断出所赋值的类型,相当于:

let myObj: { name: string, age: number } = { name: 'Jack', age: 123 };

然后调用函数时,将myObj赋给obj,这时就会采用鸭式辨型法,两者都拥有name属性,于是认为是相同类型了。

再使用接口写一下:

interface IObj {
    name: string
}

function getName(obj: IObj) {
    return obj.name;
}
// console.log(getName({ name: 'Jack', age: 11 }));//报错,因为多出了一个age属性
// console.log(getName({ name: 'Jack' }));//正确
let obj = { name: 'Jack', age: 123 };
console.log(getName(obj));//正确

一样的效果。

注意,在 type、interface 中可以使用逗号、分号,class 中不能用逗号。

可选属性

在接口中可以定义一些可选的属性。

interface IPerson {
    id: number;
    name: string;
    age?: number;
    gender: string;
}

只读属性

有些属性是只读的,不能修改,只能在对象刚刚创建的时候修改其值。

interface IPerson {
    readonly id: number;
    name: string;
    age?: number;
    gender: string;
}

let p: IPerson = { id: 1, name: 'Tom', gender: 'M' }
// p.id = 2;//报错

与const不同,对于引用类型,const只能保证用户不能修改引用地址,但内部的属性是可以修改的。但readonly修饰的属性,内部所有数据均不能修改。

readonly修饰变量的类型的时候,只允许修饰数组和元组。

let o: readonly object = { name: 'hello' }//错误
let arr: readonly number[] = [1, 2, 3]//可以

只读数组

提到readonly,就可以说一下ReadonlyArray。它可以声明只读数组,数组的元素均不能修改。

let arr: ReadonlyArray<number> = [1, 2, 3]
// arr[0] = 23//报错
// arr.push(56)//移除了方法,因此arr.push是不存在的
// arr.length = 15//不可修改

let arr2: number[] = []
// arr2 = arr//不能赋给普通数组
arr2 = arr as number[]//只能使用类型断言

声明类型时,ReadonlyArray<number>和readonly number[]是等价的。

绕开额外属性检查的方法

1、前面提到的赋值

2、类型断言

类型断言就是告诉TS,我就认为它是这种类型,所以TS就不会进行额外的检查了。

interface IPerson {
    readonly id: number;
    name: string;
    age?: number;
    gender: string;
}

// let p: IPerson = { id: 1, name: 'Tom', gender: 'M', city: 'Shanghai' }//报错
let p: IPerson = { id: 1, name: 'Tom', gender: 'M', city: 'Shanghai' } as IPerson;//可以

3、索引签名

索引签名

索引签名是任意属性,包含string和number两种类型。

一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集,因为确定属性与可选属性也算任意属性中的一种。

interface IF {
    name: string;
    // id: number;//报错,因为number不是string的子类型
    [prop: string]: string;
}
let m: IF = { name: 'Jack', h: 'hello' }

同时使用两种类型的任意属性时,数字索引的返回值必须是字符串索引返回值类型的子类型。

interface IF {
    name: string;
    // id: number;//报错,因为number不是string的子类型
    [prop1: string]: string;
    // [prop2: number]: number//报错,因为返回值number不是string的子类型
    [prop2: number]: string
}
let m: IF = { name: 'Jack', h: 'hello', 1: 'hello' }

接口的继承

interface IPerson {
    name: string
}

interface IStudent extends IPerson {
    id: number
}

let st: IStudent = { name: 'Jack', id: 1 }

接口允许多继承。

interface IPerson {
    name: string
}

interface IAnimal {
    class: string
}

interface IStudent extends IPerson, IAnimal {
    id: number
}

let st: IStudent = { name: 'Jack', id: 1, class: 'person' }

在TS中,若多继承的两个或多个父接口有相同属性,但定义的类型不同,则会报错。

这样不报错:

interface IPerson {
    name: string;
    city: string
}

interface IAnimal {
    class: string;
    city: string
}

interface IStudent extends IPerson, IAnimal {
    id: number
}

let st: IStudent = { name: 'Jack', id: 1, class: 'person', city: 'Shanghai' }

这样报错:

interface IPerson {
    name: string;
    city: string
}

interface IAnimal {
    class: string;
    city: number
}

interface IStudent extends IPerson, IAnimal {//报错
    id: number
}

let st: IStudent = { name: 'Jack', id: 1, class: 'person', city: 'Shanghai' }

因此,需要确保多个父接口之间没有共同属性或共同属性的类型相同。

猜你喜欢

转载自blog.csdn.net/weixin_45792464/article/details/125850245