TypeScript 的接口(interface)

基本用法

TypeScript 的核心原则之一是对值所具有的结构进行类型检查,接口的作用就是为这些类型命名和为代码定义约束

interface Person {
  name: string
  age: number
}

// 报错,age 类型不对
let user: Person = {
  name: 'John Han',
  age: 'Hello'
}

// 报错,sex 不在定义的约束内
let user: Person = {
  name: 'John Han',
  age: 18,
  sex: 'male'
}

// 正确
let user: Person = {
  name: 'John Han',
  age: 18
}

可选属性

有时候接口里的属性不全都是必需的,此时可使用可选属性

interface Person {
  name: string
  age: number
  sex?: 'male'
}

// 正确
let user: Person = {
  name: 'John Han',
  age: 18,
  sex: 'male'
}

// 正确
let user: Person = {
  name: 'John Han',
  age: 18
}

上面代码的 sex 属性就是可选属性,属性名后多了一个 ? 号

只读属性

有时希望一些对象属性只能在对象刚刚创建的时候修改其值,这是可以用 readonly 来指定只读属性

interface Person {
  readonly name: string
  age: number
}

let user: Person = {
  name: 'John',
  age: 18
}

user.name = 'Han' // 报错,该属性为只读属性
user.age = 20 // 正确

readonly 和 const 作用很像,但用法不同,做为变量使用的话用 const,若做为属性则使用 readonly

函数类型

接口除了描述带有属性的普通对象外,也可以描述函数类型

interface SumFun {
  (a: number, b: number): number
}

上面的接口描述了一个带有两个参数(a 和 b)、参数类型都为 String、返回一个布尔值的函数

这样定义后,我们可以像使用其它接口一样使用这个函数类型的接口

let sum: SumFun = function (a: number, b: number) {
  return a + b
}
console.log(sum(1, 2))

对于函数类型的类型检查来说,函数的参数名不需要与接口里定义的名字相匹配

let sum: SumFun = function (x: number, y: number) {
  return x + y
}
console.log(sum(1, 2))

可索引的类型

接口也可以描述对象索引的类型
TypeScript 支持两种索引签名:字符串和数字

限制索引类型为 number 的例子:

interface Person {
  // 限制索引类型为 number,属性值类型为 string
  [index: number]: string
}

let user: Person = {
  name: 'John Han', // 报错,索引 name 不是 number
  1: 'hello world' // 正确,索引 1 是 number
}

限制索引类型为 string 的话会有一些不同,数字会被当成字符串:

interface Person {
  // 限制索引类型为 string,属性值类型为 string
  [index: string]: string  
}

let user: Person = {
  name: 'John Han', // 正确,索引 name 是字符串
  1: 'hello world' // 正确,索引 1 被当成 '1'
}

限制索引类型与普通类型可以混用,但值的类型必须一致

// 正确,值的类型都是 string
interface Person {
  [index: number]: string
  sex: string
}
// 错误,值的类型不一致
interface Person {
  [index: number]: string
  sex: number
}

let user: Person = {
  1: 'hello world',
  sex: 'male'
}

类类型之实例部分的约束

TypeScript 也能够用它来明确的强制一个类去符合某种契约

interface Person {
  name: string
}

// 错误,name 的类型不匹配
class PersonClass implements Person {
  name: number
}

// 正确
class PersonClass implements Person {
  name: string
}

接口中定义的属性和方法在类中必须存在,在类中的属性和方法在接口中可以没有

interface Person {
  name: string
  rename (n: string)
}

// 错误,缺少 name
class PersonClass implements Person {
  rename (n: string) {}
}

// 正确
class PersonClass implements Person {
  name: string
  age: number
  rename (n: string) {}
}

类类型之静态部分的约束

静态部分的约束和实例部分的约束有所不同,例如 constructor 构造函数:

interface Person {
  new (n: string)
}

class PersonClass {
  constructor (n: string) {}
}

let PClass: Person = PersonClass
let user = new PClass('John Han')

其他静态方法也一样

interface Person {
  new (n: string)
  getClassName (): string
}

class PersonClass {
  constructor (n: string) {}
  static getClassName () {
    return 'Person'
  }
}

let PClass: Person = PersonClass
let user = new PClass('John Han')

类类型之实例部分和静态部分结合

下面是一个实例部分和静态部分结合的类类型约束的例子:

interface Person {
  new (n: string)
  getClassName (): string
}

interface People {
  getName (): string
}

class PersonClass implements People {
  name: string
  constructor (n: string) {
    this.name = n
  }
  static getClassName () {
    return 'Person'
  }
  getName () {
    return this.name
  }
}

let PClass: Person = PersonClass
let user = new PClass('John Han')

接口继承

接口也可以相互继承,一个接口可以继承多个接口

interface Shape {
  color: string
}

interface PenStroke {
  penWidth: number
}

interface Square extends Shape, PenStroke {
  sideLength: number
}

let square: Square = {
  color: 'blue',
  sideLength: 10,
  penWidth: 5
}

接口继承类

接口也可以继承类,当接口继承了一个类类型时,它会继承类的成员但不包括其实现

class Control {
  state: string
}

interface SelectableControl extends Control {
  select (): void
}

// 报错,缺少 state 属性
class MyControl implements SelectableControl {
  select () { }
}

// 正确
class MyControl implements SelectableControl {
  state: string
  select () { }
}
除特殊说明外本人博客均属原创,转载请注明出处:http://blog.johnhan.cn/blog_1053.html
鄂ICP备17018604号-1  鄂公网安备42060702000030号