一、接口定义

在 TypeScript 中我们使用接口 (Interfaces) 来定义约束对象的类型。在面向对象语言中,接口 (Interfaces) 是一个重要的概念,它是对一类行为的抽象,而行为的具体动作需要通过类 (Class) 来实现 (Implement) 。
如:

  1. interface IPerson {
  2. name: string;
  3. age: number;
  4. }
  5. let tom: IPerson = {
  6. name: "Tom",
  7. age: 18
  8. }

上述代码首先实现了一个 IPerson 的接口,它规定了其属性的类型,然后再声明一个 tom 实例的时候表明了它的类型是接口 IPerson 的类型,可见:

1 约束赋值

接口约束赋值变量的时候,变量的形状(属性/方法的类型)必须与接口的形状(属性/方法的类型)保持一致。多一些属性,或少一些属性,或属性类型不正确都是不允许的,如

  1. // ERROR: 类型 "{ name: string; }" 中缺少属性 "age",但类型 "IPerson" 中需要该属性。
  2. let lihua: IPerson = {
  3. name: 'lihua'
  4. }
  5. // ERROR: 不能将类型“string”分配给类型“number”。
  6. let xiaoming: IPerson = {
  7. name: 'xiaoming',
  8. age: 'ten'
  9. }
  10. /**
  11. * ERROR:
  12. * 不能将类型“{ name: string; age: number; gender: string; }”分配给类型“IPerson”。
  13. * 对象文字可以只指定已知属性,并且“gender”不在类型“IPerson”中。
  14. */
  15. let xiaofang: IPerson = {
  16. name: 'xiaofang',
  17. age: 16,
  18. gender: 'female'
  19. }

2 约束形参

接口约束函数形参时,接口采用鸭式变形法,实参仅需包含满足接口即可(多一些属性是可以的)

  1. function printMessage1(opt: { msg: string }) {
  2. console.log(opt.msg)
  3. }
  4. const opt1 = {
  5. type: 'success',
  6. msg: "成功!"
  7. }
  8. printMessage1(opt1);
  9. interface IOpt {
  10. msg: string
  11. }
  12. function printMessage2(opt: IOpt) {
  13. console.log(opt.msg);
  14. }
  15. printMessage2(opt1)
  16. const opt2 = {
  17. msg: 1111,
  18. duration: 2000,
  19. }
  20. /**
  21. * ERROR: 类型“{ msg: number; duration: number; }”的参数不能赋给类型“IOpt”的参数。
  22. * 属性“msg”的类型不兼容。
  23. * 不能将类型“number”分配给类型“string”。
  24. */
  25. printMessage1(opt2);
  26. /**
  27. * ERROR: 类型“{ msg: number; duration: number; }”的参数不能赋给类型“IOpt”的参数。
  28. * 属性“msg”的类型不兼容。
  29. * 不能将类型“number”分配给类型“string”。
  30. */
  31. printMessage2(opt2);

二、可选属性

有时候某些属性不是必要的,我们需要不完全匹配接口,这就要用到可选属性,当然这仍然不能添加未定义属性以及给属性赋值为其他类型

  1. interface IPerson {
  2. name: string;
  3. age?: number;
  4. }
  5. // OK
  6. let lihua: IPerson = {
  7. name: 'lihua'
  8. }

三、任意属性

如果我们希望某接口能够添加未定义属性,就要用到任意属性

  1. interface IPerson {
  2. name: string;
  3. age?: number;
  4. // 此处表明,此接口可以添加自定义属性,属性名为 :string 类型,属性值为 :any 类型
  5. [prop: string]: any
  6. }

❗注意:一旦接口定义了任意属性,那么它前面定义的确定属性可选属性的属性名类型与属性值类型都必须是任意属性的子类型

  1. // ERROR: 类型“number”的属性“age”不能赋给字符串索引类型“string”。
  2. interface IPerson {
  3. name: string;
  4. age?: number;
  5. [prop: string]: string
  6. }

如果接口中由多个不同类型的确定属性,却又要使用任意属性时,要么将任意属性类型定义为 :any 任意值,要么将任意属性类型定义为联合类型 (Union Types)。如

  1. interface IPerson {
  2. name: string;
  3. age?: number;
  4. [prop: string]: string | number | undefined
  5. }

四、只读属性

有时候我们希望某些属性仅能在被创建时赋初始值之后无法被更改,就需要用到只读属性

  1. interface IPerson {
  2. readonly id: number;
  3. name: string;
  4. age?: number;
  5. [prop: string]: string | number;
  6. }
  7. let tom: IPerson = {
  8. id: 89757,
  9. name: 'tom',
  10. age: 18,
  11. gender: 'female'
  12. }
  13. // ERROR: 无法分配到 "id" ,因为它是只读属性。
  14. tom.id = 9527;

❗ 注:只读属性生效是第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候。如

  1. interface IPerson {
  2. readonly id: number;
  3. name: string;
  4. age?: number;
  5. [prop: string]: string | number | undefined;
  6. }
  7. // 以下代码产生两个错误
  8. // ERROR: 1 类型 "{ name: string; age: number; gender: string; }" 中缺少属性 "id",但类型 "IPerson" 中需要该属性。
  9. let tom: IPerson = {
  10. name: 'tom',
  11. age: 18,
  12. gender: 'female'
  13. }
  14. // ERROR: 2 无法分配到 "id" ,因为它是只读属性。
  15. tom.id = 9527;

五、接口合并

TypeScript 中接口声明重复时,会产生接口合并

  1. interface IUser {
  2. id: number,
  3. account: string,
  4. password: string,
  5. }
  6. interface IUser {
  7. name: string,
  8. age: number,
  9. }
  10. const xiaoqiang:IUser = {
  11. id: 9527,
  12. account: 'xiaoqiang',
  13. password: '******',
  14. name: "小强",
  15. age: 1,
  16. }