Skip to content
目录

基础篇-接口

基础

接口是一系列抽象方法的声明,是一些方法特征的集合,这些方法都应该是抽象的,需要由具体的类去实现,然后第三方就可以通过这组抽象方法调用,让具体的类执行具体的方法。

简单示例:

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

类实现接口时,声明接口中定义的属性和方法不能修饰为 privateprotected

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 = '';
}