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

接口的使用

栗子时间:有一个函数,这个函数接受一个User对象,然后返回这个User对象的name属性:

  1. const getUserName = (user) => user.name

在自定义的typescript开发环境下这个是会报错的。
接口 - 图1
我们必须用一种类型描述这个user参数,但是这个类型又不属于上一节介绍到的各种基本类型。此时需要interface来描述这个类型。

  1. interface User {
  2. name: string
  3. age: number
  4. isMale: boolean
  5. }
  6. const getUgerName = (user:User) => user.name

这个接口User描述了参数user的结构,当然接口不会去检查属性的顺序,只要相应的属性存在并且类型兼容即可。

可选属性

当user属性可能没有age时,如在前端处理表单时,年龄age这个字段本身是可选的,我们应该如何用接口描述这种情况。
我们可以用可选属性描述这种情况:

  1. interface User {
  2. name: sting
  3. age?: number
  4. isMale: boolean
  5. }

当看到代码提示的时候,这个age属性可能是number也可能是undefined。
接口 - 图2

只读属性

当确定user的性别以后就不允许更改,interface中要如何保证这一点?
答案是利用readonly我们可以把一个属性变成只读性质,此后就无法对其进行修改。

  1. interface User {
  2. name: string
  3. age?: number
  4. readonly isMale: boolean
  5. }

一旦要修改只读属性,就会出现警告。
接口 - 图3

函数类型

如果user有一个函数怎么办?
比如:

  1. user.say = function(words:string) {
  2. return 'hello world';
  3. }

应该如何描述这种情况?

  • 直接在interface内部描述函数:

    1. interface User {
    2. name: string
    3. age?:number
    4. readonly isMale: boolean
    5. say:(words:string) => string
    6. }
  • 先用接口直接描述函数类型: ```typescript interface Say { (words:string): string }

// 然后在User内使用 interface User { name: string age?: number readonly isMale: boolean say: Say }

  1. <a name="tYIyk"></a>
  2. ## 属性检查
  3. 假设有一个config接口如下:
  4. ```typescript
  5. interface Config {
  6. width?:number;
  7. }
  8. function CalculateAreas(config:Config): {area:number} {
  9. let square = 100;
  10. if (config.width) {
  11. square = config.width * config.width;
  12. }
  13. return {area:square};
  14. }
  15. let mySquare = CalculateAreas({widdth: 5});

注意:传入的参数是widdth而不是width
此时,typescript会认为这段代码可能存在问题。对象字面量当被赋值给变量或者作为参数传递时,会被特殊对待而且经过“额外属性检查”。如果一个对象字面量存在任何“目标类型”不包含的属性时,你会得到一个错误。

  1. let mySquare = CalculateAreas({widdth: 5}); // error: 'widdth' not expected in type 'Config'

目前对于这个问题,官网推荐了三种主流的解决办法:

  • 使用类型断言:

    1. let mySquare = CalculateAreas({widdth: 5} as Config);
  • 添加字符串索引签名 ```typescript interface Config { width?:number;

  1. [propName: string]:any;

}

  1. 这样config可以有任意数量的属性,并且只要不是width,那么就无所谓他们的类型是什么了。
  2. - 将字面量赋值给另一个变量:
  3. ```typescript
  4. let options: any = {widdth: 5};
  5. let mySquare = CalculateAreas(options);

本质上是转化为any类型,除非有万不得已的情况,不建议采用上述方法。

可索引类型

假设如果User还包含一个属性,这个属性是User拥有的邮箱的集合,但是这个集合有多少成员不确定,应该如何描述呢?
如小张信息如下:

  1. {
  2. name: 'xiaozhang',
  3. age: 18,
  4. isMale: false,
  5. say: Function,
  6. phone: {
  7. NetEase: 'xiaozhang@163.com',
  8. qq: '1234567@qq.com',
  9. }
  10. }

而小明的信息如下:

  1. {
  2. name: 'xiaoming',
  3. age: 16,
  4. isMale: true,
  5. say: Function,
  6. phone: {
  7. NetEase: 'xiaoming@163.com',
  8. qq: '784536325@qq.com',
  9. sina: 'abc784536325@sina.com',
  10. }
  11. }

由上可知,他们的phone属性有共同之处,首先是key都是string类型的,其次是value也是string类型,虽然数量不等。
此时可以用可索引类型表示,可索引类型具有一个索引签名,描述了对象索引的类型,还有相应的索引返回值类型。

  1. interface Phone {
  2. [name:string]:string
  3. }
  4. interface User {
  5. name:string
  6. age?:number
  7. readonly isMale:boolean
  8. say:() => string
  9. phone: Phone
  10. }

继承接口

如果需要重新创建一个新的VIP User,这个VIPUser的属性与普通User一致,只是多了一些额外的属性,是否需要重写一个接口吗?
并不需要

  1. interface VIPUser extends User {
  2. broadcast:() => void
  3. }

也可以继承多个接口:

  1. interface VIPUser extends User, SupperUser {
  2. broadcase: () => void
  3. }