【TypeScript笔记】接口

【TypeScript笔记】接口接口是对 JavaScript 本身的随意性进行约束,通过定义一个接口,约定了变量、类、函数等应该按照什么样的格式进行声明。

接口的作用就是为类型命名和为代码或第三方代码定义契约。

一、基本使用

interface LabelledValue { // 定义接口
  label: string;
}

function printLabel(labelledObj: LabelledValue) { // 指定参数的类型为LabelledValue
  console.log(labelledObj.label);
}

let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj); // 此例中实参必须包含label属性

类型检查器会查看printLabel的调用。 printLabel有一个参数,并要求这个对象参数有一个名为label类型为string的属性。

二、可选属性

可选属性名字后面加一个?符号即表示为可选属性(opational)

interface SquareConfig {
  color?: string;
  width?: number;
}

三、只读属性

用 readonly来指定只读属性, 对象创建后变不能修改

interface Point {
    readonly x: number;
    readonly y: number;
}
  • ReadonlyArray<T>类型,用来确保数组创建后再也不能被修改,不能直接将该类型的变量赋值给一个普通数组,但是可以用类型断言重写

    let a: number[] = [1, 2, 3, 4];
    let ro: ReadonlyArray<number> = a;
    a = ro; // error!
    
    a = ro as number[]; // 类型断言
    
  • readonlyconst

    做为变量使用的话用 const,若做为属性则使用readonly

四、额外的属性检查

当对象字面量被赋值给变量或作为参数传递的时候,如果一个对象字面量存在任何“目标类型”不包含的属性时,会得到一个错误

interface SquareConfig {
    color?: string;
    width?: number;
}

function createSquare(config: SquareConfig): { height: number; area: number } {
    // ...
}

let mySquare = createSquare({ height: "300", width: 100 }); // height不存在于接口SquareConfig中

绕开类型检查

  • 类型断言

    let mySquare = createSquare({ height: "300", width: 100 } as SquareConfig);
    
  • 添加字符串索引签名

    interface SquareConfig {
        color?: string;
        width?: number;
        [propName: string]: any;
    }
    
  • 将实参对象赋值给一个另一个变量,因为另一个变量(被赋值变量)不会经过额外属性检查,所以编译器不会报错

    let squareOptions = { colour: "red", width: 100 };
    let mySquare = createSquare(squareOptions);
    

五、函数类型

给接口定义一个调用签名, 就像是一个只有参数列表和返回值类型的函数定义,参数列表里的每个参数都需要名字和类型

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

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

六、可索引的类型

可索引类型具有一个 索引签名,它描述了对象索引的类型,还有相应的索引返回值类型

interface StringArray {
  [index: number]: string; // 索引签名
}

let myArray: StringArray;
myArray = ["Bob", "Fred"];

let myStr: string = myArray[0];

此处索引签名表示当用 number去索引StringArray时会得到string类型的返回值

  • TypeScript支持两种索引签名:字符串和数字, 可以同时使用两种类型的索引,但是数字索引的返回值必须是字符串索引返回值类型的子类型

    class Animal {
        name: string;
    }
    class Dog extends Animal {
        breed: string;
    }
    
    // 错误:使用数值型的字符串索引,有时会得到完全不同的Animal!
    interface NotOkay {
        [x: number]: Animal; // 数字索引
        [x: string]: Dog;    // 字符串索引
    }
    

    此例中数字索引的返回值类型为 Animal, 字符串索引的返回值类型为 Dog, Animal不是Dog的子类型

  • 字符串索引签名能够很好的描述dictionary模式,并且它们也会确保所有属性与其返回值类型相匹配

    interface NumberDictionary {
      [index: string]: number;
      length: number;    // 可以,length是number类型
      name: string       // 错误,`name`的类型与索引类型返回值的类型不匹配
    }
    

    name: string => name为string型索引,其返回值应为number,此处为string,故不匹配

七、类类型

TypeScript用类类型来明确的强制一个类去符合某种契约

interface ClockInterface {
    currentTime: Date;
    setTime(d: Date); // 描述一个方法
}

class Clock implements ClockInterface {
    currentTime: Date;
    // 实现这个方法
    setTime(d: Date) {
        this.currentTime = d;
    }
    constructor(h: number, m: number) { }
}
  • 类静态部分与实例部分的区别

    用构造器签名去定义一个接口并试图定义一个类去实现这个接口时会得到一个错误,因为当一个类实现了一个接口时,只对其实例部分进行类型检查。 constructor存在于类的静态部分,所以不在检查的范围内

    interface ClockConstructor {
        new (hour: number, minute: number);
    }
    
    class Clock implements ClockConstructor {
        currentTime: Date;
        constructor(h: number, m: number) { }
    }
    

    应该直接操作类的静态部分。 看下面的例子,定义了两个接口, ClockConstructor为构造函数所用和ClockInterface为实例方法所用。 为了方便,再定义一个构造函数 createClock,它用传入的类型创建实例。

    interface ClockConstructor {
        new (hour: number, minute: number): ClockInterface;
    }
    interface ClockInterface {
        tick();
    }
    
    function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface {
        return new ctor(hour, minute);
    }
    
    // 类型1
    class DigitalClock implements ClockInterface {
        constructor(h: number, m: number) { }
        tick() {
            console.log("beep beep");
        }
    }
    // 类型2
    class AnalogClock implements ClockInterface {
        constructor(h: number, m: number) { }
        tick() {
            console.log("tick tock");
        }
    }
    
    let digital = createClock(DigitalClock, 12, 17);
    let analog = createClock(AnalogClock, 7, 32);
    

    createClock的第一个参数是ClockConstructor类型,在createClock(AnalogClock, 7, 32)里,会检查AnalogClock是否符合构造函数签名

八、继承接口

能够从一个接口里复制成员到另一个接口里,可以更灵活地将接口分割到可重用的模块里

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

interface Shape {
    color: string;
}

interface PenStroke {
    penWidth: number;
}

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

let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;

九、混合类型

一个对象可以同时做为函数和对象使用,并带有额外的属性

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

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

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

十、接口继承类

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

接口同样会继承到类的private和protected成员, 这意味着当创建了一个接口继承了一个拥有私有或受保护的成员的类时,这个接口类型只能被这个类或其子类所实现(implement)

class Control {
    private state: any;
}

interface SelectableControl extends Control { // 包含了Control的所有成员,包括私有成员state
    select(): void;
}

class Button extends Control implements SelectableControl { // Control子类
    select() { }
}

class TextBox extends Control {  // Control子类
    select() { }
}

// 错误:“Image”类型缺少“state”属性。
class Image implements SelectableControl {
    select() { }
}

class Location {

}

今天的文章【TypeScript笔记】接口分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/16505.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注