Typescript最核心的原则之一,就是对值所具有的结构进行类型检查,就是我们常说的 “鸭式辩型法”或者是“结构性子类型机构”;
Typescript中的interface的作用就是为这些类型命名,为编码者或者第三方代码提供一个定义好的契约;
我们假设一个场景,在编写js的函数时,又一个getUserId的方法,我们要通过这个方法直接回去userId这个属性;可以这么写:
const geyUserId = (user) => user.userId; //参数“user”隐式具有“any”类型
在ts中发现“参数“user”隐式具有“any”类型”这个提示。也就是说我们必须要用一种类型描述这个user参数,但是这个类型又不是我们常见的基本类型;这个时候就用到了接口(interface):
interface User {
userId: string,
name: string
}
const getUserAge= (user: User) => user.userId;
通过这个接口User描述了参数user的类型,可以找到对应的属性存在并且兼容即可;
我们在开深入扩展一下这场景,当这个user属性可能不存在age这个属性怎么办?常见场景就是在处理表单中,age这个字段是可选,我该用接口怎么描述呢?
interface User {
userId: string,
name: string,
age?: number
}
const getUserAge= (user: User) => user.age;
鼠标移上去可以看到一句提示语:(property) User.age?: number | undefined;也就是说如果age不存在的话接口中属性的类型可以是undefined;存在值的时候是number类型;
再来扩展一下我们的场景,表单中有些属性是默认值,不允许修改,我们考虑一下怎么设置呢?
其实很简单就是利用readonly我们可以把一个属性变成只读性质;用来禁止用户对他的操作;
interface User {
userId: string,
name: string,
age?: number,
readonly nation: string
}
const setUserNation = (param: User) => param.nation = '02';
我们在编写的过程中发现代码出现了错误警告 “无法分配到 “nation” ,因为它是只读属性。”
我们再来想一下,如果这个接口中需要一个函数,来作为他的默认函数,怎么办?
user中属性映射一个函数很简单;
interface Say {
(word: string): string
}
interface User {
userId: string,
name: string,
age?: number,
readonly nation: string,
say: Say
}
const sayUserInfo = (user: User) => {
user.say = (word:string) => user.name;
}
假设我们又一个Config接口,我们想配合函数实现计算面积的功能:
/**
* @description 计算面积
*/
interface Setting {
width?: number
}
const getArea = (config: Setting): { area: number } {
let square = 100;
if (config.width) {
square = config.width * config.width;
}
return { area: square };
}
let mySquare = getArea({ widths: 5 }); //类型“{ widths: number; }”的参数不能赋给类型“Setting”的参数。
//对象文字只能指定已知的属性,但“widths”中不存在类型“Setting”。是否要写入 width ?
这里我故意把width写成widths结果出现了参数传递经过属性检查的情况,这种情况我们有没有主流的解决方案?
let mySquare = getArea({ widths: 5 }) as Setting
interface Setting {
width?: number,
[propName:string]:any
}
这种我们发现可以传入任意数量的属性,而且无所谓是什么类型;
interface Setting {
width?: number
}
let options: any = { widths: 5 }
const getArea = (config: Setting): { area: number } {
let square = 100;
if (config.width) {
square = config.width * config.width;
}
return { area: square };
}
let mySquare = getArea(options)
本质上是转化为any类型,这种方法不是非常情况,不建议使用,过于暴力;
我在开发中遇到一种场景,user属性中某一个属性是个集合,不确定多少成员;这种怎么使用,我们先看一下数据;
{
data: [
{
name: 'xiaozhang',
age: 18,
isMale: false,
say: function(){},
email: {
NetEase: '[email protected]',
qq: '[email protected]',
}
},
{
name: 'xiaoming',
age: 16,
isMale: true,
say: function () { },
email: {
NetEase: '[email protected]',
qq: '[email protected]',
sina: '[email protected]',
}
}
]
}
我们发现email的属性成员不确定,属性有一些共性特征,key全部都是string类型,value也是string类型;个性是成员数量不等;
我们面对这种场景就可以使用可索引的类型,原理就是利用其索引签名,描述对象索引的类型及相应索引返回值的类型;
interface Say {
(word: string): string
}
interface Email {
[name: string]: string
}
interface User {
userId: string,
name: string,
age?: number,
readonly nation: string,
say: Say,
email: Email
}
我们在面向对象中常见的对象继承,使用接口也是可以的;
interface Say {
(word: string): string
}
interface Email {
[name: string]: string
}
interface User {
userId: string,
name: string,
age?: number,
readonly nation: string,
say: Say,
email: Email
}
interface VipUser extends User {
vipcard:string,
vipSay:Say
}
同理也可以继承多个接口,使用“,”分开;
以上我们了解了接口的一些基本用法,还有一些简单场景的结合;总之,接口(interface)是一个非常强大的代码类型定义描述,欢迎探讨。