1. ts 接口 interface
    2. 在 ts 中,我们使用接口(Interfaces)来定义对象的类型。
    3. 在面向对象语言中,接口(Interfaces)是一个很重要的概念,它是对行为的抽象,而具体如何行动需要由类(classes)去实现(implement)。
    4. ts 中的接口是一个非常灵活的概念,除了可用于对类的一部分行为进行抽象以外,也常用于对「对象的形状(Shape)」进行描述。赋值的时候,变量的形状必须和接口的形状保持一致。
    5. 接口命名规范:一般首字母大写,建议接口的名称加上 I 前缀,表示它是 ts 中的接口类型。
    6. 定义接口时,每个字段结束可以加一个分号也可以不加分号
    1. interface IPerson {
    2. name: string;
    3. age: number;
    4. }
    5. // ok “形状”一致
    6. let p1: IPerson = {
    7. name: "p1",
    8. age: 1,
    9. };
    10. // error “形状”不一致
    11. let p2: IPerson = {
    12. name: "p2",
    13. };
    14. // error “形状”不一致
    15. let p3: IPerson = {
    16. name: "p3",
    17. age: 3,
    18. gender: "male",
    19. };
    1. 在接口中描述一个可选字段 <字段> ?: <类型>
    1. interface IPerson {
    2. name: string;
    3. age?: number; // age 可有可无
    4. }
    5. // ok
    6. let p1: IPerson = {
    7. name: "p1",
    8. };
    9. // ok
    10. let p2: IPerson = {
    11. name: "p2",
    12. age: 2
    13. };
    14. // error “形状”不一致
    15. let p3: IPerson = {
    16. name: "p3",
    17. age: 3,
    18. gender: "male",
    19. };

    当你去访问一个可选属性时,ts 会提示它是一个联合类型,比如 typeof p1.age 得到的结果是 number | undefined

    1. 在接口中描述任意属性 [propName: string]: any
    2. 一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集
    3. 一个接口中只能定义一个任意属性
    4. 如果接口中有多个类型的属性,则可以在任意属性中使用联合类型
    1. interface IPerson {
    2. name: string
    3. age?: number // age 可有可无
    4. [propName: string]: any
    5. }
    6. // ok
    7. let p1: IPerson = {
    8. name: "p1",
    9. age: 1,
    10. // 以下皆为任意属性 [propName: string]: any
    11. gender: "male",
    12. a: 'a',
    13. bc: 'bc'
    14. };
    1. interface IPerson {
    2. name: string;
    3. // error 类型“number | undefined”的属性“age”不能赋给“string”索引类型“string”。
    4. age?: number;
    5. [propName: string]: string;
    6. }
    1. interface IPerson {
    2. name: string;
    3. // ok
    4. age?: number; // age 可能是 number、undefined
    5. [propName: string]: string | number | undefined;
    6. }
    1. 只读属性 readonly
    2. 有时候我们希望对象中的一些字段只能在创建的时候被赋值,那么可以用 readonly 定义只读属性
    3. 只读属性必须在定义的同时完成初始化,且后续不可更改
    1. interface IPerson {
    2. readonly id: number;
    3. name: string;
    4. age?: number;
    5. [propName: string]: any;
    6. }
    7. let tom: IPerson = {
    8. id: 89757,
    9. name: "Tom",
    10. gender: "male",
    11. };
    12. // error 无法为“id”赋值,因为它是只读属性。
    13. tom.id = 9527;
    1. interface IPerson {
    2. readonly id: number;
    3. name: string;
    4. age?: number;
    5. [propName: string]: any;
    6. }
    7. // error 类型 "{ name: string; gender: string; }" 中缺少属性 "id",但类型 "IPerson" 中需要该属性。
    8. let tom: IPerson = {
    9. name: 'Tom',
    10. gender: 'male'
    11. };
    1. 【应用场景】接口 vs. 类型别名
      1. interface 用来描述对象、类的结构
      2. type 用来描述非对象、类的结构
      3. 以上仅是建议,在很多场景下 interfacetype 可相互替代,可自行选择喜欢的写法,并在工程中一以贯之便是。
    2. 接口是一系列抽象方法的声明,是一些方法特征的集合,这些方法都应该是抽象的,需要由具体的类去实现,然后第三方就可以通过这组抽象方法调用,让具体的类执行具体的方法
    3. ts 中定义接口的语法 interface <接口名> { ... }
    1. interface IPerson {
    2. firstName: string,
    3. lastName: string,
    4. sayHi: () => string
    5. }
    6. var customer: IPerson = {
    7. firstName: "Tom",
    8. lastName: "Hanks",
    9. sayHi: (): string => { return "Hi there" }
    10. }
    11. console.log("Customer 对象 ")
    12. console.log(customer.firstName)
    13. console.log(customer.lastName)
    14. console.log(customer.sayHi())
    15. var employee: IPerson = {
    16. firstName: "Jim",
    17. lastName: "Blakes",
    18. sayHi: (): string => { return "Hello!!!" }
    19. }
    20. console.log("Employee 对象 ")
    21. console.log(employee.firstName)
    22. console.log(employee.lastName)
    23. console.log(employee.sayHi())
    24. // Customer 对象
    25. // Tom
    26. // Hanks
    27. // Hi there
    28. // Employee 对象
    29. // Jim
    30. // Blakes
    31. // Hello!!!
    1. // 接口
    2. interface RunOptions {
    3. program: string;
    4. // 联合类型
    5. commandline: string[] | string | (() => string);
    6. }
    7. // commandline 是字符串
    8. var options: RunOptions = { program: "test1", commandline: "hello" };
    9. console.log(options.commandline);
    10. // commandline 是字符串数组
    11. options = { program: "test1", commandline: ["Hello", "World"] };
    12. // 类型守卫
    13. if (Array.isArray(options.commandline)) {
    14. console.log(options.commandline[0]);
    15. console.log(options.commandline[1]);
    16. }
    17. // commandline 是一个函数表达式
    18. options = { program: "test1", commandline: () => "**Hello World**" };
    19. // 类型守卫
    20. if (typeof options.commandline === "function") {
    21. console.log(options.commandline());
    22. }
    23. // hello
    24. // Hello
    25. // World
    26. // **Hello World**
    1. interface namelist {
    2. [index: number]: string
    3. }
    4. var list2: namelist = ["Google", "Runoob", "Taobao"] // 类型一致,正确
    5. var list2: namelist = ["Runoob", 1, "Taobao"] // 错误元素 1 不是 string 类型
    6. // 报错:不能将类型“number”分配给类型“string”。ts(2322)
    1. interface ages {
    2. [key: string]: number
    3. }
    4. var agelist: ages = {};
    5. agelist["runoob"] = 15 // 类型正确
    6. agelist[2] = "google" // 类型错误
    7. // 报错:不能将类型“string”分配给类型“number”。ts(2322)
    1. 接口继承就是说接口可以通过其他接口来扩展自己
    2. ts 允许接口继承多个接口
    3. ts 接口继承使用关键字 **extends**
    4. 接口继承语法 **<子接口> extends <父接口>****<子接口> extends <父接口1>, <父接口2>, ..., <父接口n-1>, <父接口n>**
    5. 接口不会出现在编译结果中
    1. interface Person {
    2. age: number
    3. }
    4. interface Musician extends Person {
    5. instrument: string
    6. }
    7. var drummer = <Musician>{};
    8. drummer.age = 27
    9. drummer.instrument = "Drums"
    10. console.log("年龄: " + drummer.age)
    11. console.log("喜欢的乐器: " + drummer.instrument)
    12. // 年龄: 27
    13. // 喜欢的乐器: Drums
    1. interface IParent1 {
    2. v1: number
    3. }
    4. interface IParent2 {
    5. v2: number
    6. }
    7. interface Child extends IParent1, IParent2 { }
    8. var Iobj: Child = { v1: 12, v2: 23 }
    9. console.log("value 1: " + Iobj.v1 + " value 2: " + Iobj.v2)
    10. // value 1: 12 value 2: 23