共计 7404 个字符,预计需要花费 19 分钟才能阅读完成。
接口
接口是一系列形象办法的申明,是一些办法特色的汇合,这些办法都应该是形象的,须要由具体的类去实现,而后第三方就能够通过这组形象办法调用,让具体的类执行具体的办法。
接口解决可用于对类的一部分行为进行形象外,也罕用于对对象的形态进行形容。
接口定义 如下:
interface interface_name {}
留神:
- 定义接口要首字母大写;
- 只须要关注值的形状;
-
如果没有非凡申明,定义的变量比接口少了一些属性是不容许的,多一些属性也是不容许的,赋值的时候,变量的形态必须和接口的形态保持一致
利用场景
在申明一个对象、函数或者类时,先定义接口,确保其数据结构的一致性;
实例
interface IPerson { name: string; age: number; } let person:IPerson = { name: 'xman', age: 18 }
以上咱们定义了一个接口 IPerson,接着定义了一个变量 person,它的类型是 IPerson。这样,咱们就束缚了
person
的形态必须和接口IPerson
统一。
变量的形态必须和接口的形态保持一致
interface IPerson {
name: string;
age: number;
}
let person:IPerson = {name: 'xman'}
// Type '{name: string;}' is not assignable to type 'IPerson'.
// Property 'age' is missing in type '{name: string;}'.
let person2:IPerson = {
name: 'xman',
age: 18,
height: '60kg'
}
// Type '{name: string; age: number; height: string;}' is not assignable to type 'IPerson'.
// Object literal may only specify known properties, and 'height' does not exist in type 'IPerson'.
接口属性
可选属性
接口里的属性不全都是必须的。在可选属性名字定义的前面加一个 ?
合乎
interface IPerson {
name: string;
age: number;
height?: string;
}
益处:
- 对可能存在的属性进行预约义;
-
捕捉援用了不存在属性时的谬误
此时仍不容许增加未定义的属性,如果援用了不存在的属性时会呈现错误信息。只读属性
一些对象属性只能在对象刚刚创立的时候批改其值。你能够在属性名前用
readonly
来指定只读属性interface IPerson { readonly name: string; age: number; }
批改只读属性值时会
Cannot assign to 'xxx' because it is a constant or a read-only property.
错误信息
测试下:let person:IPerson = { name: 'xman'; age: 18; } person.name = 'zxx'; // Cannot assign to 'name' because it is a constant or a read-only property.
TypeScript 能够通过
ReadonlyArray<T>
设置数组为只读,只是把所有可变办法去掉了,因而能够确保数组创立后再也不能被批改let arr: ReadonlyArray<T> = [1, 2, 3]; arr[0] = 100; // Index signature in type 'ReadonlyArray<any>' only permits reading. arr.push(4); // Property 'push' does not exist on type 'ReadonlyArray<any>'. arr.length = 0; // Cannot assign to 'length' because it is a constant or a read-only property.
readonly VS const
最简略判断该用 readonly
还是 const
的办法是看要把它做为变量应用还是做为一个属性。作为变量应用的话用 const
,若作为属性则应用readonly
。
任意属性
有时咱们心愿接口容许有任意的属性,语法是用 []
将属性包裹起来
任意属性有两种定义的形式:一种属性签名是 string 类型的,一种是 number 类型
任意属性为 string 类型
interface IPerson {
name: string;
age: number;
[propName: string]: any;
}
let person:IPerson = {
name: 'xman',
age: 18,
height: '60kg'
}
[propName: string]: any
是指 IPerson 类型的对象能够有任意属性签名, string 指的是对象的键名是字符串类型的,any 则是指定属性值的类型 , 至于 propName
则相似于函数的形参,是能够取其余名字的。
任意属性为 number 类型
interface StringArray {[index: number]: string;
}
let arr: StringArray = ['xman'];
[index: number]: string
是指 StringArray 的数组能够有任意的数字下标,而且数组成员的类型必须是 string。同时 index
也只是相似于函数形参的货色,用其余标识符也是齐全能够的。
能够同时定义两种任意属性吗?
能够,然而number
类型的签名指定的值类型必须是 string
类型的签名指定的值类型的子集,举个例子:
interface A {[index: number]: string;
[propName: number]: number;
}
// Numeric index type 'string' is not assignable to string index type 'number'.
阐明:string
并不是 number
的子集。
以下例子成立:
因为 Function 是 object 的子集:
interface A {[index: number]: Function;
[propName: number]: object;
}
同时定义任意属性和其余类型的属性
注:
一旦定义了任意属性,那么其余属性(确定属性、可选属性、只读属性等)的类型都必须是它的类型的子集
interface IPerson {
name: string;
age?: number; // Property 'age' of type 'number' is not assignable to string index type 'string'.
[propName: string]: string;
}
let person:IPerson = {
name: 'xman',
age: 18,
height: '60kg'
}
// Type '{name: string; age: number; height: string;}' is not assignable to type 'IPerson'.
// Property 'age' is incompatible with index signature.
// Type 'number' is not assignable to type 'string'.
以上例子中咱们定义接口 IPerson 中有一个可选属性 age,属性值的类型为 number, 任意属性值 string,number 不是 string 的子属性,所以报错了。
如何解决?咱们能够应用联结类型:
interface IPerson {
name: string;
age?: number;
[propName: string]: string | number;
}
let person:IPerson = {
name: 'xman',
age: 18,
height: '60kg'
}
对于 number 类型的任意属性, 状况也是一样
type MyArray = {
0: string,
[index: number]: number;
}
// Property '0' of type 'string' is not assignable to numeric index type 'number'.
然而,number
类型的任意属性签名不会影响其余 string
类型的属性签名:
type A {[index: number]: number;
length: string;
}
如上,尽管指定了 number
类型的任意属性的类型是 number
,但 length
属性是 string
类型的签名,所以不受前者的影响。
然而反过来就不一样了,如果接口定义了 string
类型的任意属性签名,它不仅会影响其余 string
类型的签名,也会影响其余 number
类型的签名。
如何绕过多余属性的报错
interface IPerson {
name: string;
age: number;
}
let person: IPerson = {
name: 'xman',
age: 18,
height: '60kg'
}
// Type '{name: string; age: number; height: string;}' is not assignable to type 'IPerson'.
// Object literal may only specify known properties, and 'height' does not exist in type 'IPerson'.
类型断言
类型断言是一种通知编译器变量类型的机制。当 TypeScript 确定赋值有效时,咱们能够抉择应用类型断言来笼罩类型。如果咱们应用类型断言,赋值总是无效的,所以咱们须要确保咱们是正确的。否则,咱们的程序可能无奈失常运行。
两种执行类型断言的办法:
- 应用角括号 <>
- as 关键字
interface IPerson {
name: string;
age: number;
}
let person: IPerson = {
name: 'xman',
age: 18,
height: '60kg'
} as IPerson
// 或者
let person1: IPerson = < IPerson > {
name: 'xman',
age: 18,
height: '60kg'
}
索引签名
interface IPerson {
name: string;
age: number;
[prop: string]: any;
}
let person: IPerson = {
name: 'xman',
age: 18,
height: '60kg'
}
鸭式辨型法
鸭式辨型来自于 James Whitecomb Riley 的名言:” 像鸭子一样走路并且嘎嘎叫的就叫鸭子。” 即具备鸭子特色的认为它就是鸭子, 也就是通过制订规定来断定对象是否实现这个接口。
interface LabeledValue {label: string;}
function printLabel(labelledObj: LabeledValue) {console.log(labelledObj.label);
}
let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);
类型查看器会查看 printLabel
的调用。printLabel
有一个参数,并要求这个对象参数有一个名为 label
类型为 string
的属性。须要留神的是,咱们传入的对象参数实际上会蕴含很多属性,然而编译器只会查看那些必须的属性是否存在,并且其类型是否匹配。
interface LabeledValue {label: string;}
function printLabel(labeledObj: LabeledValue) {console.log(labeledObj.label);
}
printLabel({size: 10, label: "Size 10 Object"});
// Argument of type '{size: number; label: string;}' is not assignable to parameter of type 'LabeledValue'.
// Object literal may only specify known properties, and 'size' does not exist in type 'LabeledValue'.
以上代码,在参数里写对象就相当于是间接给 labeledObj 赋值,这个对象有严格的类型定义,所以不能多参数或少参数。而当你在里面将该对象用另一个变量 myObj
接管,myObj
不会通过额定属性查看,但会依据类型推论为 let myObj: {size: number; label: string} = {size: 10, label: "Size 10 Object"};
,而后将这个myObj
再赋值给 labeledObj
,此时依据类型的兼容性,两种类型对象,参照 鸭式辨型法 ,因为都具备label
属性,所以被认定为两个雷同,故而能够用此法来绕开多余的类型查看。
注:
具体可参考 https://segmentfault.com/a/11…
函数类型
除了形容带有属性的一般对象外,接口也能够形容函数类型。
为了使接口示意函数类型,咱们须要给接口定义一个调用签名。它就像是一个只有 参数列表
和 返回值类型
的函数定义。
interface SearchFunc {(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string): boolean {return source.search(subString) > -1;
}
对于函数类型的类型查看来说,函数的参数名不须要与接口里定义的名字相匹配。你能够扭转函数的参数名,只有保障函数参数的地位不变。函数的参数会被一一进行查看:
interface SearchFunc {(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
// source => src, subString => sub
mySearch = function(src: string, sub: string): boolean {return src.search(sub) > -1;
}
如果你不想指定类型,TypeScript 的类型零碎会推断出参数类型,因为函数间接赋值给了 SearchFunc 类型变量。
interface SearchFunc {(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(src, sub) {let result = src.search(sub);
return result > -1;
}
类类型
咱们心愿类的实现必须遵循接口定义,那么能够应用 implements
关键字来确保兼容性。
这种类型的接口在传统面向对象语言中最为常见,比方 java 中接口就是这品种类型的接口。这种接口与抽象类比拟类似,然而接口只能含有形象办法和成员属性,实现类中必须实现接口中所有的形象办法和成员属性。
interface IPerson {
name: string;
age: number;
say(str: string): string;
}
class Person implements IPerson {
name: string;
age: number;
constructor (name: string, age: number) {
this.name = name;
this.age = age;
}
say (str: string) {return 'str'}
}
接口形容了类的公共局部,而不是公共和公有两局部。它不会帮你查看类是否具备某些公有成员.
继承
接口能够通过其余接口来扩大本人。接口可继承多个接口。继承应用关键字 extends。
单继承实例:
interface IAnimal {name: string;}
interface IPerson extends IAnimal{age: number;}
let person = <IPerson> {};
person.name = 'xman';
person.age = 18;
编译以下代码,失去以下 JavaScript 代码:
var person = {};
person.name = 'xman';
person.age = 18;
多继承实例:
interface Shape {color: string;}
interface PenStroke {penWidth: number;}
interface Square extends Shape, PenStroke {sideLength: number;}
let square = {} as Square;
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;
混合类型
对象能够同时做为函数和对象应用,并带有额定的属性
interface Counter {(start: number): string;
interval: number;
reset(): void;}
function getCounter(): Counter {let counter = function (start: number) { } as Counter;
counter.interval = 123;
counter.reset = function () {};
return counter;
}
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;
以上代码,先申明了一个接口,包含 number 类型的索引签名、reset 函数、interval 属性 let counter = function (start: number) {} as Counter;
通过类型断言,将函数对象转换为 Counter
类型,转换后的对象岂但实现了函数接口的形容,使之成为一个函数,还具备 interval 属性和 reset() 办法。
参考资料
https://www.tslang.cn/docs/ha…
https://juejin.cn/post/685544…
https://segmentfault.com/a/11…