您好, 欢迎来到 !    登录 | 注册 | | 设为首页 | 收藏本站

TypeScript 接口(Interface)

本节介绍 TypeScript 各种类型接口的声明及其使用,接口在 TypeScript 中是极其重要的,我们使用接口来定义契约,如类型命名、检查、类型定义等。

在下一节学习完类之后,你会知道类也可以作为接口来使用。接口的种类繁多,在学习过程中一定要亲手编写,以达到灵活使用。

TypeScript 的核心原则之一是对值所具有的结构进行类型检查。 它有时被称做“鸭式辨型法”或“结构性子类型化”。 在 TypeScript 里,接口的作用就是为这些类型命名和为你的或第三方定义契约。——官方定义

接口是对 JavaScript 本身的随意性进行约束,通过定义接口,约定了变量、类、等应该按照什么样的格式进行声明,实现多人合作的一致性。TypeScript 编译器依赖接口用于类型检查,最终编译为 JavaScript 后,接口将会被移除。

// 语法格式
interface DemoInterface {

}

在声明对象或者时,先定义接口,确保其数据结构的一致性。

在多人协作时,定义接口尤为重要。

过去我们写 JavaScript 定义:

function getClothesInfo(clothes) {
  console.log(clothes.price)
}

let myClothes = {
  color: 'black', 
  size: 'XL', 
  price:  
}
getClothesInfo(myClothes)

之前我们写 JavaScript 这样是很正常的,但同时你可能会遇到下面这些问题:

getClothesInfo() // Uncaught TypeError: Cannot read property 'price' of undefined
getClothesInfo({ color: 'black' }) // undefined

相信原因你也知道,JavaScript 是 弱类型 语言,并不会对传入的参数进行任何检测,在运行时才被发现。那么通过定义 接口,在编译阶段甚至开发阶段就避免掉这类,接口将检查类型是否和某种结构做匹配。

下面通过接口的方式重写之前的例子:

interface Clothes {
  color: string;
  size: string;
  price: number;
}

function getClothesInfo(clothes: Clothes) {
  console.log(clothes.price)
}

let myClothes: Clothes = { 
  color: 'black', 
  size: 'XL', 
  price:  
}

getClothesInfo(myClothes)

解释: 中,定义了接口 Clothes,在传入的变量 clothes 中,它的类型为 Clothes。这样,就约束了这个传入对象的 外形 与接口定义一致。只要传入的对象满足上面的类型约束,那么它就是被允许的。

Tips:

定义接口要 首字母大写

只需要关注值的 外形,并不像其他语言一样,定义接口是为了实现。

如果没有特殊声明,定义的变量比接口少了一些是不允许的,多一些也是不允许的,赋值的时候,变量的形状必须和接口的形状保持一致。

接口中的不全是必需的。可选的含义是该在被变量定义时可以不存在。

// 语法
interface Clothes {
  color?: string;
  size: string;
  price: number;
}

// 这里可以不定义 color
let myClothes: Clothes = { 
  size: 'XL', 
  price:  
}

带有可选的接口与普通的接口定义差不多,只是在可选名字定义的后面加 ? 符号。

这时,仍不允许未定义的,如果引用了不存在的时 TS 将直接捕获。

一些对象只能在对象刚刚创建的时候其值。你可以在名前用 readonly 来指定只读,比如价格是不能被的:

// 语法
interface Clothes {
  color?: string;
  size: string;
  readonly price: number;
}

// 创建的时候给 price 赋值
let myClothes: Clothes = { 
  size: 'XL', 
  price:  
}

// 不可
myClothes.price = 
// error TS2540: Cannot assign to 'price' because it is a constant or a read-only property

TypeScript 可以通过 ReadonlyArray<T> 设置数组为只读,那么它的所有写都会失效。

let arr: ReadonlyArray<number> = [,,,,];
arr[] = ; // Index signature in type 'readonly number[]' only permits reading

解释: 中的泛型语法在之后会有专门的小节介绍。

最简单判断该用 readonly 还是 const 的是看要把它做为变量使用还是做为。做为 变量 使用的话用 const,若做为 则使用 readonly。

有时候我们希望接口允许有任意的,语法是用 [] 将包裹起来:

// 语法
interface Clothes {
  color?: string;
  size: string;
  readonly price: number;
  [propName: string]: any;
}

// 任意 activity
let myClothes: Clothes = { 
  size: 'XL', 
  price: ,
  activity: 'coupon'
}

解释: 这里的接口 Clothes 可以有任意的,并且只要它们不是 color sizeprice,那么就无所谓它们的类型是什么。

this.$axios({
  method: 'put',
  url: '/cms/user',
  data: {
    nickname: this.nickname,
  },
  showBackend: true,
})

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

为了使接口表示类型,我们需要给接口定义签名。 它就像是只有 参数列表返回值类型 的定义。

interface SearchFunc {
  (source: string, subString: string): boolean;
}

let mySearch: SearchFunc;
mySearch = function(source: string, subString: string): boolean {
  return source.search(subString) > -;
}

对于类型的类型检查来说,的参数名不需要与接口里定义的名字相匹配。你可以改变的参数名,只要保证参数的位置不变。的参数会被逐个进行检查:

interface SearchFunc {
  (source: string, subString: string): boolean;
}

let mySearch: SearchFunc;
// source => src, subString => sub
mySearch = function(src: string, sub: string): boolean {
  return src.search(sub) > -;
}

如果你不想指定类型,TypeScript 的类型系统会推断出参数类型,因为直接赋值给了 SearchFunc 类型变量。

interface SearchFunc {
  (source: string, subString: string): boolean;
}

let mySearch: SearchFunc;
mySearch = function(src, sub) {
  let result = src.search(sub);
  return result > -;
}

如果接口中的类型带有名,下面两种书写方式是等价的:

interface Calculate {
  add(x: number, y: number): number
  multiply: (x: number, y: number) => number
}

可索引类型接口读起来有些拗口,直接看例子:

// 正常的js
let arr = [, , , , ]
let obj = {
  brand: 'imooc',
  type: 'education'
}

arr[]
obj['brand']

再来看定义可索引类型接口:

interface ScenicInterface {
  [index: number]: string
}

let arr: ScenicInterface = ['西湖', '华山', '故宫']
let favorite: string = arr[]

示例中索引签名是 number类型,返回值是字符串类型。

另外还有一种索引签名是 字符串类型。我们可以同时使用两种类型的索引,但是数字索引的返回值必须是返回值类型的子类型。通过下面的例子理解这句话:

// 正确
interface Foo {
  [index: string]: number;
  x: number;
  y: number;
}

// 
interface Bar {
  [index: string]: number;
  x: number;
  y: string; // Error: y 必须为 number 类型
}

解释:

第 12 行,语法是因为当使用 number 来索引时,JavaScript 会将它转换成 string 然后再去索引对象。也就是说用 100(number)去索引等同于使用"100"(string)去索引,因此两者需要保持一致。

我们希望类的实现必须遵循接口定义,那么可以使用 implements 关键字来确保兼容性。

这种类型的接口在传统面向对象语言中最为常见,比如 java 中接口就是这种类类型的接口。这种接口与抽象类比较相似,但是接口只能含有抽象和成员,实现类中必须实现接口中所有的抽象和成员。

interface AnimalInterface {
  name: string;
}

class Dog implements AnimalInterface {
  name: string;
  
  constructor(name: string){
    this.name = name
  }
}

你也可以在接口中描述,在类里实现它:

interface AnimalInterface {
  name: string

  eat(m: number): string
}

class Dog implements AnimalInterface {
  name: string;

  constructor(name: string){
    this.name = name
  }

  eat(m: number) {
    return `${this.name}吃肉${m}分钟`
  }
}
 

接口描述了类的,而不是公共和私有两部分。 它不会帮你检查类是否具有某些私有成员。

和类一样,接口也可以通过关键字 extents 相互继承。 这让我们能够从接口里复制成员到另接口里,可以更灵活地将接口分割到可重用的模块里。

interface Shape {
  color: string;
}

interface Square extends Shape {
  sideLength: number;
}

let square = {} as Square;
// 继承了 Shape 的
square.color = "blue";
square.sideLength = ;

接口可以继承多个接口,创建出多个接口的合成接口。

interface Shape {
  color: string;
}

interface Pen {
  penWidth: number;
}

interface Square extends Shape, Pen {
  sideLength: number;
}

let square = {} as Square;
square.color = "blue";
square.sideLength = ;
square.penWidth = ;

在前面已经介绍,接口可以描述、对象的或者对象的。

有时希望对象同时具有上面提到多种类型,比如对象可以当做使用,同时又具有和。

interface Counter {
  (start: number): string;
  interval: number;
  reset(): void;
}

function getCounter(): Counter {
  let counter = function (start: number) { } as Counter;
  counter.interval = ;
  counter.reset = function () { };
  return counter;
}

let c = getCounter();
c();
c.reset();
c.interval = ;

解释:

第 1 行,声明接口,如果只有 (start: number): string 成员,那么这个接口就是接口,同时还具有其他两个成员,可以用来描述对象的和,这样就构成了混合接口。

第 7 行,创建 getCounter() ,它的返回值是 Counter 类型的。

let counter = function (start: number) { } as Counter;

第 8 行,通过类型断言,将对象转换为 Counter 类型,转换后的对象不但实现了接口的描述,使之成为,还具有 interval 和 reset() 。断言成功的条件是,两个数据类型只要有一方可以赋值给另一方,这里类型数据不能赋值给接口类型的变量,因为它不具有 interval 和 reset() 。

类型断言在之后的小节也会单节介绍。

本节介绍了接口的基本及其使用场景,接口在 TypeScript 中至关重要,TypeScript 编译器依赖接口用于类型检查。


联系我
置顶