介绍
TypeScript的核心原则之一是对值所具有的结构进行类型检查。 它有时被称做“鸭式辨型法”或“结构性子类型化”。 在TypeScript里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。
说人话
接口是一种规范的定义,定义了行为和动作的规范,和抽象类abstract比较相像,但是能力更强
属性接口
自定义方法传入参数,对json进行约束
接口初探
下面通过一个简单示例来观察接口是如何工作的:
function Cat(obj: { name: string }) {
console.log(obj.name);
}
let catInfo = { leg: 3, name: "dahuang" };
Cat(catInfo);
//fine
let catInfo = { leg: '3'};
Cat(catInfo);
//error
和普通的函数定义有所不同,对于传入的对象,ts只会检测规定的项是否存在,而不会严格恪守长度规则
然后还是刚刚里例子,这次使用接口来描述,要求包含一个name属性且类型为string
interface catObj {
name: string;
}
function Cat(obj: catObj) {
console.log(obj.name);
}
let catInfo = { leg: 3, name: "dahuang" };
Cat(catInfo);
catObj就是一个名字,用来描述上面例子里的要求。表示有一个 name属性且类型为string的对象。这里需要说的是,catobj并不像平时理解的参数传入了cat,实际上只是规范了结构,只要传入的对象满足上面提到的必要条件,那么它就是被允许的。
可选属性
跟函数一样直接使用‘?’修饰符就可以了·
interface catObj {
name?: string;
leg?:string;
}
function Cat(obj: catObj) {
console.log(obj.name);
}
let catInfo = { leg: '3',name:'dahuang',age:12}; //fine
let catInfo = { leg: '3',name:'dahuang'}; //fine
let catInfo = { leg: '3', }; //fine
let catInfo = { age: 12 }; //error
可选属性的好处之一是可以对可能存在的属性进行预定义,好处之二是可以捕获引用了不存在的属性时的错误。 虽然文档上没有写,但是对几种情况进行尝试,的出结论当接口都为可选时,传入参数至少要满足一个,否则会报错;
此外由于可选参数和函数很像,这里举一反三尝试了默认参数,发现接口并不支持定义默认值
只读属性
一些对象属性只能在对象刚刚创建的时候修改其值。 你可以在属性名前用 readonly来指定只读属性:
interface cat{
name: string;
readonly leg: number;
}
let dahuang:cat = { name: 'dahuang', leg: 3 };
dahuang.leg = 4; // error 大黄失去的腿就长不回来了,永远只能做三脚猫
readonly vs const
官方给了我们一个很好区分readonly | const使用场景的例子
最简单判断该用readonly还是const的方法是看要把它做为变量使用还是做为一个属性。 做为变量使用的话用 const,若做为属性则使用readonly。
函数类型
接口除了描述带有属性的普通对象外,也可以描述函数类型。
interface catRule {
(sound: string, eat: string): boolean;
}
let isCat: catRule = function (sound: string, eat: string) {
return (sound === 'miao' && eat === 'rat') ? true : false
}
isCat('miao', 'rat') //true
在函数中入参的类型定义可以被省略,名称也不必一致,ts将在接口catRule中判断类型
interface catRule {
(sound: string, eat: string): boolean;
}
let isCat: catRule = function (x,y) {
return (x === 'miao' && y === 'rat') ? true : false
}
isCat('miao', 'rat') //true
isCat('miao',3) //error
可索引的类型
对于类似数组这种可以通过索引得到的类型(a[0]、obj[‘key’])也可以使用接口进行定义,它描述了对象索引的类型,还有相应的索引返回值类型。
interface catArr {
[index: number]: string;
}
let myCats: catArr = ["dahuang", "xiaobai"];
let myCat: string = myCats[0];
需要注意的是ts仍然要受js的桎梏,使用number进行索引时,会现将number.toString,然后再使用字符串进行查找,我记得有道面试题就是
let a = {'2':2,'1':1,'0':0};
a[0] = 'a';
a["0"] = 'b'
//问a最终是什么
类类型
和上边介绍的函数的基本作用一样,也用来明确的强制一个类去符合某种规范。
interface catInfo {
name: string;
leg: number;
say();
}
class Cat implements catInfo {
name: string;
leg: number;
constructor(n: string, l: number) {
}
say() { console.log(this.name) }
}
let cat = new Cat('dahuang',3);
cat.say();
继承接口
和类一样,接口也可以相互继承。 这让我们能够从一个接口里复制成员到另一个接口里,可以更灵活地将接口分割到可重用的模块里。
interface Animal{
name: string;
}
interface Cat extends Animal {
leg: number;
}
let cat = {} as Cat;
square.name = "dahuang";
square.leg = 3;
接口继承类
当接口继承了一个类类型时,它会继承类的成员但不包括其实现。
class CatInfo{
name: string;
}
interface CatRule extends CatInfo {
say(): void;
}
class Cat extends CatInfo implements CatRule {
say() { }
}
class Cat1 extends CatInfo {
say() { }
}
class CatRulePlus implements CatRule {//error 缺少name属性
say() { }
}