TypeScript的核心原则之一是对值所具有的结构进行类型检查,而接口就是用来定义值的结构,接口自身当作类型来用。

 

基本使用

先看一段代码:

function func(obj: { name: string }): void {
  console.log('hello ' + obj.name)
}

很明显,函数func要求参数是一个包含name属性的对象,name值为string类型。而 { name: string } 就可以被定义为接口,如下:

interface objType { // 定义接口 objType
  name: string
}

function func(obj: objType): void {
  console.log('hello ' + obj.name)
}

代码里使用 interface 来定义接口,需要注意的是,不要把它理解为是在定义一个对象,而要理解为 {} 括号包裹的是一个代码块,里面是一 条条声明语句,只不过声明的不是变量的值而是类型。上述代码定义的是最简单的接口结构,只有一个属性时定义成接口或许没必要,但当结构复杂时,接口的用处就会慢慢体现。

 

可选属性

当我们定义一些结构的时候,一些结构对于某些字段的要求是可选的,有这个字段就做处理,没有就忽略。接口里在属性名后面加上 ? 就可以表示这个属性是可选的:

interface objType {
  name: string
  age?: number  // age 是可选的
} 

function func(obj: objType): void {
  let age = obj.age ? obj.age : 18
  console.log(obj.name + ' is ' + age + ' years old ')
}

 

只读属性

一些对象属性只能在对象刚刚创建的时候修改其值。 你可以在属性名前用 readonly来指定只读属性:

interface Point {
  readonly x: number  // 加上readonly前缀,表示只读
  readonly y: number  // 同上 
}

let n: Point = { x: 3, y: 5 }

n.x = 10  // Cannot assign to 'x' because it is a read-only property.

 定义只读变量用 const,定义只读属性用 readonly。

 

额外属性检查 

当某接口类型里不存在某个属性时,就不能应用这个属性,否则会报错,例如:

interface Point {
  x: number
  y: number
}

let n: Point = { x: 3, y: 5, z: 10 }  // 这里加入了z属性,会报错

//  Type '{ x: number; y: number; z: number; }' is not assignable to type 'Point'.
//  Object literal may only specify known properties, and 'z' does not exist in type 'Point'.

很多时候,我们希望绕过typescript的这个检查,那么有几个方法需要知道:

(1)类型断言

将值断言为Point类型,告诉typescript我们已经自行检查过了,没有问题

let n: Point = { x: 3, y: 5, z: 10 } as Point

 

(2)字符串索引签名

定义任意属性,属性名是string类型,propName是泛指的属性名,只要不是 x 和 y,就无所谓它的值类型:

interface Point {
  x: number
  y: number
  [propName: string]: any
}

let n: Point = { x: 3, y: 5, z: 10 }

 

(3)利用类型兼容性

这个方法说白了就是先把值赋值给一个变量,然后再把这个变量赋值给这个接口类型的变量:

interface Point {
  x: number
  y: number
}

let obj = { x: 3, y: 5, z: 10 } // 需要的值赋值给变量
let n: Point = obj // 变量赋值给接口类型变量

 

函数类型接口

除了描述带有属性的普通对象外,接口也可以描述函数类型。为了使用接口表示函数类型,我们需要给接口定义一个调用签名:

interface AddFunc {
  (num1: number, num2: number): number
}

接口AddFunc要求实现这个接口结构的值须包含一个函数,其函数结构和接口内定义的函数结构一致。如:

let func: AddFunc
func = function(num1:number, num2:number): number {
  return num1 + num2
}

对于函数类型的类型检查来说,函数的参数名不需要与接口里定义的名字相匹配。 比如,我们使用下面的代码重写上面的例子:

let func: AddFunc
func = function(n1:number, n2:number): number {  // n1,n2代替num1,num2
  return n1 + n2  
}

函数的参数会逐个进行检查,要求对应位置上的参数类型是兼容的。另外你也可以不指定类型,由typescript推导类型:

interface AddFunc {
  (num1: number, num2: number): number
}

let func: AddFunc
func = function(n1, n2) {
  return n1 + n2
}

 

可索引的类型接口

接口可以描述那些“可通过索引得到”的类型,如数组 a[1] 或对象 obj["name"],可以同时给索引和值都设置类型,如:

interface MyArray {
  [index: number]: string
}

const arr: MyArray = ['Apple', 'Orange']

interface MyObject {
  [key: number]: string
}

const obj: MyObject = {
  0: 'hello',
  1: 'world'
}

 

 接口的继承

接口也可以继承,本质上就是从一个接口里复制成员到另一个接口里,更灵活的复用属性。

// 定义一个 Person 接口,它包含两个属性和一个方法
interface Person{
  age:number;
  name:string;
  say():void;
}

// 定义一个 Teacher 接口,继承 Person 的属性成员,同时拥有自己的 teach 方法
interface Teacher extends Person{
  teach():void;
}

// 使用接口
const teacher:Teacher = {
  teach(){},
  age:25,
  name:'tom',
  say(){}
}

 

混合类型

在 js 中,函数是对象,而对象可以有属性,所以有时一个对象,它既是一个函数,也包含一些属性,如这个计数器函数:

function countUp(){
  return ++countUp.count
}
countUp.count = 0

console.log(countUp()) // 1
console.log(countUp()) // 2

 

现在来使用混合类型接口来定义上面 countUp 类型:

interface Counter{
  ():void;
  count:number
}

function getCounter():Counter{
  const c = ()=>{
    c.count++
  }
  c.count = 0
  return c
}

const counter:Counter = getCounter()

counter()
console.log(counter.count)
counter()
console.log(counter.count)

 

以上就是接口的相关内容,接口中还有涉及类的一些内容,后面写到类相关时再说。