TypeScript入门到精通——TypeScript类型系统基础——接口

接口

  类似于对象类型字面量,接口类型也能够表示任意的对象类型。不同的是,接口类型能够给对象类型命名以及定义类型参数。接口类型无法表示原始类型,如 boolean 类型等。

  接口声明只存在于编译阶段,在编译后生成的 JavaScript 代码中不包含任何接口代码。

一、接口声明

  通过接口声明能够定义一个接口类型。接口声明的基础语法如下所示:

interface InterfaceName
{
    TypeMember;
    TypeMember;
    ...        
}

  在该语法中,interface 是关键字,InterfaceName 表示接口名,它必须是合法的标识符,TypeMember 表示接口的类型成员,所有类型成员都置于一对大括号 "{}" 之内。

  按照惯例,接口名的首字母需要大写。因为接口定义了一种类型,而类型名的首字母通常需要大写。

接口类型的类型成员可以分为以下五类:

  1. 属性成员: 接口可以定义属性的名称、类型和可访问性。属性成员描述了对象的数据结构。
    interface Person {
        name: string;
        age: number;
    }
  2. 方法成员:接口可以定义方法的名称、参数列表和返回类型。方法成员描述了对象的行为。
    interface Person {
        name: string;
        age: number;
        greet(): void;
    }
  3. 可索引成员:接口可以定义对象的索引签名,描述对象如何通过索引访问或赋值。
    interface StringArray {
        [index: number]: string;
    }
  4. 调用签名:接口可以定义对象的调用签名,描述对象作为函数调用的方式。
    interface Callable {
        (arg: number): string;
    }
  5. 构造签名:接口可以定义对象的构造签名,描述对象作为构造函数使用的方式。
    interface Constructable {
        new(arg: number): string;
    }

二、属性签名

  属性签名声明了对象类型中属性成员的名称和类型。属性签名的语法如下所示:

PropertyName: Type;

  在该语法中,PropertyName 表示对象属性名,可以为标识符、字符串、数字和可计算属性名:Type 表示该属性的类型。示例如下:

interface Point{
    x: number;
    y: number;
} 

三、调用签名

  调用签名定义了该对象类型表示的函数在调用时的类型参数、参数列表以及返回值类型。调用签名的语法如下所示:

(ParameterList): Type

  在该语法中,ParameterList 表示函数形式参数列表类型;Type 表示函数返回值类型,两者都是可选的。

// 定义一个名为 Stringifier 的接口,这个接口定义了一个调用签名,而 toString 函数符合这个调用签名
interface Stringifier {
    (value: number): string;
}

// 定义一个名为 toString 的函数,这个函数接收一个数字类型的参数,并将其转换为字符串后返回
function toString(value: number): string {
    return value.toString();
}

// 声明一个名为 myStringifier 的变量,这个变量的类型是 Stringifier 接口
let myStringifier: Stringifier;

// 将 toString 函数赋值给 myStringifier 变量,因为 toString 函数符合 Stringifier 接口的调用签名
myStringifier = toString; // 正确,因为 toString 函数符合 Stringifier 接口的调用签名

四、构造签名

  构造签名定义了该对象类型表示的构造函数在使用 new 运算符调用时的参数列表以及返回值类型。构造签名的语法如下所示:

new (ParameterList): Type

  在该语法中,new 是运算符关键字;ParamerterList 表示构造函数形式参数列表类型;Type 表示构造函数返回值类型,两者都是可选的。

interface EerrorConstructor {
    new(message?: string): Error;
} 

五、方法签名

  方法签名是声明函数类型的属性成员的简写。方法签名的语法如下所示:

PropertyName(ParameterList): Type

  在该语法中,PropertyName 表示对象属性名,可以为标识符、字符串、数字和可计算属性名;ParameterList 表示可选的方法形式参数列表类型;Type 表示可选的方法返回值类型。从语法的角度来看,方法签名是在调用签名之前添加一个属性名作为方法名。

  下面是一个使用 TypeScript 方法签名的示例:

type Person = {
    name: string;
    age: number;
    greet: (msg: string) => void; // 方法签名
};

let person: Person = {
    name: 'Alice',
    age: 25,
    greet: function (msg: string) { // 方法实现
        console.log(msg + ', ' + this.name);
    }
};

六、索引签名

  JavaScript支持使用索引去访问对象的属性,即通过括号 "[]" 语法去访问对象属性。一个典型的例子是数组对象,我们既可以使用数字索引去访问数组元素,也可以使用字符串索引去访问数组对象上的属性和方法。

const colors = ['red', 'green', 'blue'];

// 访问数组中的第一个元素
const red = colors[0];

//访问数组对象的 length 属性
const blue = colors['length'];

  接口中的索引签名能够描述使用索引访问的对象属性的类型。索引签名只有以下两种:

6.1、字符串索引签名

  字符串索引签名是 TypeScript 中的一个特性,它允许我们使用字符串类型的键来索引对象。这个特性的语法是在对象类型的定义中,使用 [key: string]: Type 的形式来表示。其中,key 是字符串类型的索引键,而 Type 是相应的值类型。

  通过定义字符串索引签名,我们可以确保对象能够接受任意字符串作为键,并且相应的值满足一定的类型约束。这在处理动态键的对象时非常有用,比如在字典或JSON数据结构中。

  下面是一个使用字符串索引签名的示例:

interface Dictionary {
    [key: string]: string;
}

const myDictionary: Dictionary = {
    "apple": "A red fruit",
    "banana": "A yellow fruit",
    "orange": "An orange fruit"
};

console.log(myDictionary["apple"]); // 输出 "A red fruit"
console.log(myDictionary["banana"]); // 输出 "A yellow fruit"

  在上面的示例中,我们定义了一个名为 Dictionary 的接口,它具有字符串索引签名。这意味着该对象可以使用任意字符串作为键,并且相应的值都是字符串类型。然后,我们创建了一个符合 Dictionary 接口的对象 myDictionary,并使用字符串索引来访问对象的值。

  使用字符串索引签名可以为我们提供一种灵活的方式来处理具有动态键的对象,同时保持类型的安全性。

6.2、数值索引签名

  数值索引签名是 TypeScript 中的另一个特性,它允许我们使用数值类型的键来索引对象。与字符串索引签名类似,数值索引签名的语法是在对象类型的定义中,使用 [key: number]: Type 的形式来表示。其中,key 是数值类型的索引键,而 Type 是相应的值类型。

  数值索引签名常用于处理具有数值键的对象,例如数组或类似数据结构。通过定义数值索引签名,我们可以确保对象能够接受任意数值作为键,并且相应的值满足一定的类型约束。

  下面是一个使用数值索引签名的示例:

interface NumberArray {
    [key: number]: string;
}

const myArray: NumberArray = {
    0: "Apple",
    1: "Banana",
    2: "Orange"
};

console.log(myArray[0]); // 输出 "Apple"
console.log(myArray[1]); // 输出 "Banana"  
  在上面的示例中,我们定义了一个名为  NumberArray 的接口,它具有数值索引签名。这意味着该对象可以使用任意数值作为键,并且相应的值都是字符串类型。然后,我们创建了一个符合 NumberArray 接口的对象 myArray,并使用数值索引来访问对象的值。
  使用数值索引签名可以让我们更灵活地处理具有数值键的对象,并在 TypeScript 中保持类型的安全性。

七、可选属性与方法

  在默认情况下,接口中属性签名和方法签名定义的对象属性都是必选的。在给接口类型赋值时,如果未指定必须属性则会产生编译错误。示例如下:

interface Animal {
    name: string;
    sound: string;
    makeSound(): void;
}

class Dog implements Animal {
    name: string;
    sound: string;

    constructor(name: string, sound: string) {
        this.name = name;
        // 注意,我们忘记给 "sound" 属性赋值
    }

    makeSound() {
        console.log(this.name + ' says ' + this.sound);
        // 由于 "sound" 未定义,这里将会产生运行时错误
    }
}

let dog = new Dog('Rex', 'Woof');
dog.makeSound(); // Rex says undefined

八、只读属性与方法

  在接口声明中,使用 readonly 修饰符能够定义只读属性。readonly 修饰符只允许在属性签名和索引签名中使用。具体语法如下所示:

//1、在接口中使用 readonly 修饰符:

interface MyInterface {
    readonly property: string;
}


//2、在类中使用 readonly 修饰符:

class MyClass {
    readonly property: string;

    constructor(value: string) {
        this.property = value;
    }
}

九、接口的继承 

  接口可以继承其他的对象类型,这就相当于将继承的对象类型中的类型成员复制到当前接口中。接口可以继承的对象类型如下:

    • 接口

    • 对象类型的类型别名

    • 对象类型的交叉类型

  接口的继承需要使用 extends 关键字。下例中,Circle 接口继承了 Shap 接口。我们可以将 Circle 接口称作子接口,同时将 Shape 接口称作父接口。

interface Shape {
    color: string;
    area(): number;
}

interface Circle extends Shape {
    radius: number;
    circumference(): number;
}
  在上述代码中,Circle 接口继承了 Shape 接口。这意味着 Circle 接口拥有了 Shape 接口的所有成员,包括 color 属性和 area() 方法。同时,Circle 接口还可以添加自己的成员,比如 radius 属性和 circumference() 方法。
  通过继承,我们可以实现代码的重用和扩展,让子接口在拥有父接口成员的基础上,添加更多的功能或特性。
  如果子接口和父接口之间存在同名的类型成员,那么子接口中的类型成员具有更高的优先级。同时,子接口与父接口的同名类型成员必须是类型兼容的。也是就是说,子接口中同名类型成员的类型需要能够赋值给父接口中同名类型成员的类型,否则将产生编译错误。
  如果有多个父接口,且父接口之间存在同名的类型成员,而子接口本身没有该同名类型成员,那么父接口中同名类型成员的类型必须是完全相同的,否则将产生编译错误。
posted @ 2023-10-26 17:43  左扬  阅读(75)  评论(0编辑  收藏  举报
levels of contents