Appearance
基础篇-接口
基础
接口是一系列抽象方法的声明,是一些方法特征的集合,这些方法都应该是抽象的,需要由具体的类去实现,然后第三方就可以通过这组抽象方法调用,让具体的类执行具体的方法。
简单示例:
js
interface IPerson {
name: string;
age: number;
}
let tom: IPerson = {
name: 'Tom',
age: 25,
};
function getName(info: IPerson): string {
return `我叫${info.name},今年${info.age}岁了`;
}
console.log(getName(tom)); //我叫Tom,今年25岁了
我们定义了一个接口 Person,接着定义了一个变量 tom,它的类型是 Person。这样,我们就约束了 tom 的形状必须和接口 Person 一致。
接口一般首字母大写,有的编程语言中会建议接口的名称加上 I 前缀,定义的变量多少都会报错!
js
interface IPerson {
name: string;
age: number;
}
let tom: IPerson = {
name: 'Tom',
};
//ERROR Property 'age' is missing in type '{ name: string; }' but required in type 'IPerson'.
let tom: IPerson = {
name: 'Tom',
age: 13,
grade: '一年级',
};
//ERROR Object literal may only specify known properties, and 'grade' does not exist in type 'IPerson'.
可选属性
js
interface IPerson {
name: string;
age?: number;
}
let tom: IPerson = {
name: 'Tom',
};
任意属性
有时候我们希望一个接口允许有任意的属性,可以使用如下方式
js
interface IPerson {
name: string;
age?: number;
[propName: string]: any;
}
let tom: IPerson = {
name: 'Tom',
gender: 'male',
};
使用 [propName: string] 定义了任意属性取 string 类型的值。
需要注意的是,一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集:
js
interface IPerson {
name: string;
age?: number;
[propName: string]: string;
}
let tom: IPerson = {
name: 'Tom',
age: 12,
gender: 'male',
};
//ERROR Property 'age' is incompatible with index signature. Type 'number' is not assignable to type 'string'.
只读属性
js
interface IPerson {
readonly id: number;
name: string;
}
let tom: IPerson = {
id:123,
name: "Tom",
};
tom.id=456
//ERROR TS2540: Cannot assign to 'id' because it is a read-only property.
- readonly vs const 做为变量使用的话用 const,若做为属性则使用 readonly。
额外的属性检查
js
interface SquareConfig {
color?: string;
width?: number;
}
function createSquare(config: SquareConfig): { color: string, area: number } {
let newSquare = { color: 'white', area: 100 };
if (config.color) {
newSquare.color = config.color;
}
if (config.width) {
newSquare.area = config.width * config.width;
}
return newSquare;
}
let mySquare = createSquare({ colo: 'red', width: 100 });
console.log(mySquare); //{color: "red",area: 10000}
//Object literal may only specify known properties, but 'colo' does not exist in type 'SquareConfig'. Did you mean to write 'color'?
虽然 color 是可选属性,但是会报colo错误
可以使用类型断言跳过这些错误
js
let mySquare = createSquare({ colo: "red", width: 100 } as SquareConfig);
console.log(mySquare); //{color: "white", area: 10000}
函数类型接口
js
interface Addfunc {
(num1: number, num2: number): number;
}
const add: Addfunc = (a, b) => a + b;
console.log(add(1, 2));
也可以用类型别名 type
js
type Minfunc = (num1: number, num2: number) => number;
const min: Minfunc = (a, b) => a - b;
console.log(min(33, 2));
可索引类型接口
可索引类型接口是对数组、对象的
定义数组的 2 种方式
js
let arr: number[] = [1, 2];
let arr: Array<string> = ['1', '2'];
对数组的约束:
js
interface IArray {
[index: number]: string;
}
let arr1: IArray = ['Hello', 'TS']; //正确
let arr2: IArray = [123, 'TS']; //错误
let str: string = arr1[0];
//上面规定索引签名是number类型,返回值是字符串类型。
共有支持两种索引签名:字符串和数字。 可以同时使用两种类型的索引,但是数字索引的返回值必须是字符串索引返回值类型的子类型。
这是因为当使用 number 来索引时,JavaScript 会将它转换成 string 然后再去索引对象。
对对象的约束:
js
interface UserArr {
//定义索引key为number类型,索引值为string类型
[index: number]: string;
}
let arr3: UserArr;
arr3 = { 1: 'hello', 2: 'world' };
console.log(arr3);
将索引签名设置为只读,防止给索引赋值
js
interface Itest {
readonly [index: number]: string;
}
let arr: Itest = ["Hello", "TS"];
arr[1] = "ok";
//Index signature in type 'Itest' only permits reading.
//再次给索引为1的数组赋值,所以报错
类实现接口 implements
实现(implements)是面向对象中的一个重要概念。一般来讲,一个类只能继承自另一个类,有时候不同类之间可以有一些共有的特性,这时候就可以把特性提取成接口(interface),用 implements 关键字来实现。这个特性大大提高了面向对象的灵活性。
js
interface Animal {
name: string;
eat(): void;
}
class Cat implements Animal {
constructor(name: string) {
this.name = name;
}
name: string;
eat() {}
}
Cat 类中必须包含 name 属性和 eat 方法。
WARNING
类实现接口时,必须声明接口中所有定义的属性和方法。
js
interface Animal {
name: string
eat (): void
}
class Cat implements Animal {
constructor (name: string) {
this.name = name
}
name: string
// eat () {}
}
// Error: Class 'Cat' incorrectly implements interface 'Animal'. Property 'eat' is missing in type 'Cat' but required in type 'Animal'.
WARNING
类实现接口时,声明接口中定义的属性和方法不能修饰为 private 或 protected。
ts
interface Animal {
name: string;
eat(): void;
}
class Cat implements Animal {
constructor(name: string) {
this.name = name;
}
private name: string;
eat() {}
}
// Error: Class 'Cat' incorrectly implements interface 'Animal'. Property 'name' is private in type 'Cat' but not in type 'Animal'.
WARNING
接口不能约束类中的构造函数
ts
interface Animal {
new (name: string): void;
name: string;
eat(): void;
}
class Cat implements Animal {
constructor(name: string) {
this.name = name;
}
name: string;
eat() {}
}
// Error: Class 'Cat' incorrectly implements interface 'Animal'. Type 'Cat' provides no match for the signature 'new (name: string): void'.
接口继承
js
interface Iperson {
name: string;
}
interface Istudent extends Iperson {
age: number;
grade: string;
}
let jack: Istudent = {
age: 18,
name: 'Jack',
grade: '二年级',
};
console.log(jack);
TIP
继承多个接口用 , 分割,同理实现多个接口方式相同。
js
interface Iname {
name: string;
}
interface Iage {
age: number;
}
interface Istudent extends Iname, Iage {
grade: string;
move(): void;
}
let jack: Istudent = {
age: 18,
name: 'Jack',
grade: '二年级',
move() {
console.log('move');
},
};
console.log(jack);
混合类型接口
接口可以描述函数、对象的方法或者对象的属性。
有时希望一个对象同时具有上面提到多种类型,比如一个对象可以当做函数使用,同时又具有属性和方法。
代码实例如下:
js
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
//声明一个接口,如果只有(start: number):
//string一个成员,那么这个接口就是函数接口,同时还具有其他两个成员,可以用来描述对象的属性和方法,这样就构成了一个混合接口。
const getCounter = (): Counter => {
let counter = <Counter>function(start: number) {};
counter.interval = 123;
counter.reset = function() {};
return counter;
};
//创建一个函数,它的返回值是Counter类型的
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;
接口继承类
js
class Auto {
constructor(state: string) {
this.state = state;
}
state: string;
}
interface AutoInterface extends Auto {}
class C implements AutoInterface {
state = '';
}