Typescript 接口
基本用法
直接使用
这里的{ label: string }
就是一个接口,规范labelledObj
必须有一个label
为字符串的属性
编译器只会检查那些必需的属性是否存在,并且其类型是否匹配
类型检查器不会去检查属性的顺序,只要相应的属性存在并且类型也是对的就可以
function printLabell(labelledObj: { label: string }) {
console.log(labelledObj.label);
}
let myObj = { size: 10, label: "Size 10 Object" };
printLabell(myObj);
使用interface关键字
接口可以单独声明出来,注意接口分隔符写 ,
和 ;
都可以,推荐分号,和对象区分
interface LabelledValue {
label: string;
name: string;
}
function printLabel1(labelledObj: LabelledValue) {
console.log(labelledObj.label);
}
let myObj1 = { name: '10', label: "Size 10 Object" };
printLabel1(myObj1);
可选属性
属性后面加个?
表示此属性可选
interface xyz {
x?: number;
y?: number;
z?: number;
}
function logXYZ(xyzObj: xyz): { x: number; y: number; z: number } {
let xyz = { x: 100, z: 100, y: 100 } // 不关心顺序
return xyz
}
let xyzObj = { x: 1000, ss: 100, y: 1000, z: 10000 }
logXYZ(xyzObj)
只读属性
属性前面加上readonly
表示此属性只读
interface Point {
readonly x: number;
readonly y: number;
}
let p1: Point = { x: 10, y: 20 };
// p1.x = 5; // error!
let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
// ro[0] = 12; // error!
// ro.push(5); // error!
// ro.length = 100; // error!
// a = ro; // error! 注意这里,ro是只读的,即使是重新赋值给另外一个变量,也是不允许的
a = <Array<number>>ro // 但是可以使用断言来重写
额外属性检查
interface Label {
label: string
}
function printLabel(labelledObj: Label) {
console.log(labelledObj.label);
}
let myObj2 = { size: 10, label: "Size 10 Object" };
printLabel(myObj2);
上面的接口允许我们传递除了label的额外属性,上面这样写没有问题,但是当我们把myObj2直接写到函数中,ts会校验不通过,这是因为对象字面量会被特殊对待而且会经过额外的属性检查
printLabel({ size: 10, label: "Size 10 Object" });
解决这个可以使用断言
printLabel(<Label>{ size: 10, label: "Size 10 Object" })
但是注意一下,只有在断言后面直接设置的时候,才能添加额外的属性,下面这样使用会报错
let obj = <Label>{}
obj.label = 'string'
// obj.size = 10 // 这里报错
也可以像上面一样,把对象参数单独保存为一个变量,也可以绕过检查
let myObj3 = { size: 10, label: "Size 10 Object" };
printLabel(myObj3);
最佳方案,添加字符串索引签名,接收任意类型的参数,只要不是label属性,则不关心其类型到底是什么
interface NewLabel {
label: string;
// 属性名是string,值是任意值都可以,规定类型为any
[propName: string]: any
}
function newpPrintLabel(labelledObj: NewLabel) {
console.log(labelledObj.label);
}
newpPrintLabel({ size: 10, label: "Size 10 Object" })
函数接口
假设有个函数,有两个参数,source
和subString
,都是字符串类型,返回值是布尔类型
- 通过接口定义,先声明一个函数,指定接口类型,再定义函数
interface SearchFunc { (source: string, subString: string): boolean; } let mySearch: SearchFunc; mySearch = function (source: string, subString: string) { let result = source.search(subString); return result > -1; }
- 在定义函数的时候声明接口类型
interface SearchFunc { (source: string, subString: string): boolean; } let mySearch1: SearchFunc = function (source: string, subString: string) { let result = source.search(subString); return result > -1; }
ts的类型系统会自动检查,函数的参数会逐个进行检查,要求对应位置的参数类型是相同的,形参不用和接口里的相同,也可以不指定类型
let mySearch2: SearchFunc = function (src, sub) { // 这里入参不用手动添加类型,ts会自动检查
let result = src.search(sub);
return result > -1;
}
注意!函数类型的接口里面没有函数名,如果有函数名,就是类接口。并且函数的参数只能少些,不能多写,多写了就会报错
混合类型接口
函数上是可以绑定实例属性的,我们可以队这些属性做接口规范,例如一个函数上可以有interval
属性和reset
属性,值分别为number
和函数,此时我们可以这样定义接口:
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
// 这里定义一个myCounter,要符合接口的定义,
let myCounter: Counter = <Counter>function (start: number) { }
myCounter.interval = 100
myCounter.reset = function () { }
myCounter.func = function () { } // 报错
类接口
在类中,使用接口需要使用implements关键字继承,并且需要先指定实例属性的类型,和接口中的保持一致
下面定义的接口只能用于类实例上,对于静态属性是无效的。如果想要约束静态属性的类型,往下看静态属性部分。
interface PersonInterface {
gender: string;
age: number;
eat(food: string): string;
}
class Student implements PersonInterface {
// 和ES6不同的是,TS中属性必须声明,需要指定类型,不写的话ts检查失败
gender: string
age: number
// 声明好属性之后,属性必须赋值一个默认值或者在构造函数中进行初始化
constructor(gender: string, age: number) {
this.gender = gender
this.age = age
}
eat(food: string) {
console.log(food)
return food
}
}
let jerry = new Student('male', 18)
console.log(jerry.gender)
console.log(jerry.age)
jerry.eat('米饭')
多继承
implements
后面使用,可以继承多个接口
interface interface1 {}
interface interface2 {}
class Cls1 implements interface1, interface2 {}
定义构造函数的接口
当定义类接口中的constructor
函数时,有特殊写法,构造函数需要单独写一个接口,定义构造函数的入参类型,以及返回类型是类的接口类型
interface constructorInterface {
new (name: string): interface3 & interface4;
}
interface interface3 {
name: string
log():string
}
interface interface4 {
show(): string
}
const Cls3:constructorInterface = class implements interface3,interface4 {
name: string
constructor(name: string) {
this.name = name;
}
log(){
return 'loggggg'
}
show(){
return 'showwww'
}
}
可索引类型
定义索引key
为number
类型,索引值为string
类型
这个索引签名表示了当用 number
去索引StringArray
时会得到string
类型的返回值。index
可以是任意名字,不一定必须是index
interface StringArray {
[index: number]: string;
}
let myArray: StringArray;
myArray = ["Bob", "Fred"];
let myArray1: StringArray
myArray1 = { 1: 'jack', 2: 'rose' }
let myStr: string = myArray[0];
接口继承
一个接口可以继承自另外一个接口,使用extends
关键字
interface AnimalInterface {
color: string
age: number
[propsName: string]: any
}
interface DogInterface extends AnimalInterface {
jiao(): void
}
class Dog implements DogInterface {
color: string
age: number
name: string
constructor(name: string, color: string, age: number) {
this.color = color
this.age = age
this.name = name
}
jiao() {
console.log('wangwang~')
}
}
let daHuang = new Dog('大黄', 'red', 3)
daHuang.jiao()
接口多重继承
interface Shape {
color: string;
}
interface PenStroke {
penWidth: number;
}
interface Square extends Shape, PenStroke {
sideLength: number;
}
先断言再赋值,如果使用了断言,则属性只能设置为接口中定义了的属性
let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;
// square.someOtherProp = 'someOtherProp'; // Error
但如果直接在断言的对象中赋值,则可以赋额外的属性
let square1 = <Square>{
color: "blue",
sideLength: 10,
penWidth: 5.0,
someOtherProp: 'someOtherProp'
}
如果直接赋值,则一次性必须赋值所有,不能少也不能多
let mySquare: Square = {
color: "blue",
sideLength: 10,
penWidth: 5.0,
}
静态属性部分
这里声明的接口只能作用于实例部分,无法作用于静态部分
interface CarInterface {
run(): void
size: number
}
class Car implements CarInterface {
// CarInterface接口只对实例部分生效,如果实例部分没有定义相应的属性或方法,ts则会报错
// 下面的代码注释将会报错
size: number
constructor(size: number) {
this.size = size
}
run() {
console.log('跑')
}
// 上面接口定义的规则只对实例属性和实例方法有效,对静态属性和静态方法都无效
// 因此这里的size有没有都不会发生报错
static size = 1000
static run() {
console.log('跑')
}
}
上面的static size = 1000
这部分其实并没有被接口约束,因为他是静态属性,而上面我们定义的接口只能作用于实例属性,因此这里有没有都不会发生错误,我们需要改造一下,再声明一个类类型的接口,里面包含了构造函数、静态实例、静态方法,再将构造函数的返回值指向实例部分的接口就可以了
// 这里面都是实例部分的属性和方法
interface CarInterface1 {
piaoyi(): void
name: string
}
// 声明一个类接口,声明静态方法,其中包含构造函数,静态属性以及静态方法
// 其中构造函数的返回值指向实例部分的类接口
interface CarStatic {
new(name: string): CarInterface1
size: number
run(a: string): void
}
class Car1 implements CarInterface1 {
name: string
constructor(name: string) {
this.name = name
}
piaoyi() {
console.log('漂移~~~')
}
static size = 1000
static run() {
console.log('跑')
}
}
let bmw = new Car1('宝马');
console.log(bmw)
console.log(bmw.name)
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 289211569@qq.com